llvm.org GIT mirror llvm / 46abfcf
For ARM backend, fixed "byval" attribute support. Now even the small structures could be passed within byval (small enough to be stored in GPRs). In regression tests next function prototypes are checked: PR15293: %artz = type { i32 } define void @foo(%artz* byval %s) define void @foo2(%artz* byval %s, i32 %p, %artz* byval %s2) foo: "s" stored in R0 foo2: "s" stored in R0, "s2" stored in R2. Next AAPCS rules are checked: 5.5 Parameters Passing, C.4 and C.5, "ParamSize" is parameter size in 32bit words: -- NSAA != 0, NCRN < R4 and NCRN+ParamSize > R4. Parameter should be sent to the stack; NCRN := R4. -- NSAA != 0, and NCRN < R4, NCRN+ParamSize < R4. Parameter stored in GPRs; NCRN += ParamSize. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181148 91177308-0d34-0410-b5e6-96231b3b80d8 Stepan Dyatkovskiy 7 years ago
7 changed file(s) with 360 addition(s) and 42 deletion(s). Raw diff Collapse all Expand all
162162
163163 unsigned StackOffset;
164164 SmallVector UsedRegs;
165 unsigned FirstByValReg;
166 bool FirstByValRegValid;
165
166 // ByValInfo and SmallVector ByValRegs:
167 //
168 // Vector of ByValInfo instances (ByValRegs) is introduced for byval registers
169 // tracking.
170 // Or, in another words it tracks byval parameters that are stored in
171 // general purpose registers.
172 //
173 // For 4 byte stack alignment,
174 // instance index means byval parameter number in formal
175 // arguments set. Assume, we have some "struct_type" with size = 4 bytes,
176 // then, for function "foo":
177 //
178 // i32 foo(i32 %p, %struct_type* %r, i32 %s, %struct_type* %t)
179 //
180 // ByValRegs[0] describes how "%r" is stored (Begin == r1, End == r2)
181 // ByValRegs[1] describes how "%t" is stored (Begin == r3, End == r4).
182 //
183 // In case of 8 bytes stack alignment,
184 // ByValRegs may also contain information about wasted registers.
185 // In function shown above, r3 would be wasted according to AAPCS rules.
186 // And in that case ByValRegs[1].Waste would be "true".
187 // ByValRegs vector size still would be 2,
188 // while "%t" goes to the stack: it wouldn't be described in ByValRegs.
189 //
190 // Supposed use-case for this collection:
191 // 1. Initially ByValRegs is empty, InRegsParamsProceed is 0.
192 // 2. HandleByVal fillups ByValRegs.
193 // 3. Argument analysis (LowerFormatArguments, for example). After
194 // some byval argument was analyzed, InRegsParamsProceed is increased.
195 struct ByValInfo {
196 ByValInfo(unsigned B, unsigned E, bool IsWaste = false) :
197 Begin(B), End(E), Waste(IsWaste) {}
198 // First register allocated for current parameter.
199 unsigned Begin;
200
201 // First after last register allocated for current parameter.
202 unsigned End;
203
204 // Means that current range of registers doesn't belong to any
205 // parameters. It was wasted due to stack alignment rules.
206 // For more information see:
207 // AAPCS, 5.5 Parameter Passing, Stage C, C.3.
208 bool Waste;
209 };
210 SmallVector ByValRegs;
211
212 // InRegsParamsProceed - shows how many instances of ByValRegs was proceed
213 // during argument analysis.
214 unsigned InRegsParamsProceed;
167215
168216 protected:
169217 ParmContext CallOrPrologue;
305353 MVT LocVT, CCValAssign::LocInfo LocInfo,
306354 int MinSize, int MinAlign, ISD::ArgFlagsTy ArgFlags);
307355
308 // First GPR that carries part of a byval aggregate that's split
309 // between registers and memory.
310 unsigned getFirstByValReg() const { return FirstByValRegValid ? FirstByValReg : 0; }
311 void setFirstByValReg(unsigned r) { FirstByValReg = r; FirstByValRegValid = true; }
312 void clearFirstByValReg() { FirstByValReg = 0; FirstByValRegValid = false; }
313 bool isFirstByValRegValid() const { return FirstByValRegValid; }
356 // Returns count of byval arguments that are to be stored (even partly)
357 // in registers.
358 unsigned getInRegsParamsCount() const { return ByValRegs.size(); }
359
360 // Returns count of byval in-regs arguments proceed.
361 unsigned getInRegsParamsProceed() const { return InRegsParamsProceed; }
362
363 // Get information about N-th byval parameter that is stored in registers.
364 // Here "ByValParamIndex" is N.
365 void getInRegsParamInfo(unsigned InRegsParamRecordIndex,
366 unsigned& BeginReg, unsigned& EndReg) const {
367 assert(InRegsParamRecordIndex < ByValRegs.size() &&
368 "Wrong ByVal parameter index");
369
370 const ByValInfo& info = ByValRegs[InRegsParamRecordIndex];
371 BeginReg = info.Begin;
372 EndReg = info.End;
373 }
374
375 // Add information about parameter that is kept in registers.
376 void addInRegsParamInfo(unsigned RegBegin, unsigned RegEnd) {
377 ByValRegs.push_back(ByValInfo(RegBegin, RegEnd));
378 }
379
380 // Goes either to next byval parameter (excluding "waste" record), or
381 // to the end of collection.
382 // Returns false, if end is reached.
383 bool nextInRegsParam() {
384 unsigned e = ByValRegs.size();
385 if (InRegsParamsProceed < e)
386 ++InRegsParamsProceed;
387 return InRegsParamsProceed < e;
388 }
389
390 // Clear byval registers tracking info.
391 void clearByValRegsInfo() {
392 InRegsParamsProceed = 0;
393 ByValRegs.clear();
394 }
314395
315396 ParmContext getCallOrPrologue() const { return CallOrPrologue; }
316397
3131 // No stack is used.
3232 StackOffset = 0;
3333
34 clearFirstByValReg();
34 clearByValRegsInfo();
3535 UsedRegs.resize((TRI.getNumRegs()+31)/32);
3636 }
3737
14801480
14811481 // True if this byval aggregate will be split between registers
14821482 // and memory.
1483 if (CCInfo.isFirstByValRegValid()) {
1483 unsigned ByValArgsCount = CCInfo.getInRegsParamsCount();
1484 unsigned CurByValIdx = CCInfo.getInRegsParamsProceed();
1485
1486 if (CurByValIdx < ByValArgsCount) {
1487
1488 unsigned RegBegin, RegEnd;
1489 CCInfo.getInRegsParamInfo(CurByValIdx, RegBegin, RegEnd);
1490
14841491 EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy();
14851492 unsigned int i, j;
1486 for (i = 0, j = CCInfo.getFirstByValReg(); j < ARM::R4; i++, j++) {
1493 for (i = 0, j = RegBegin; j < RegEnd; i++, j++) {
14871494 SDValue Const = DAG.getConstant(4*i, MVT::i32);
14881495 SDValue AddArg = DAG.getNode(ISD::ADD, dl, PtrVT, Arg, Const);
14891496 SDValue Load = DAG.getLoad(PtrVT, dl, Chain, AddArg,
14921499 MemOpChains.push_back(Load.getValue(1));
14931500 RegsToPass.push_back(std::make_pair(j, Load));
14941501 }
1495 offset = ARM::R4 - CCInfo.getFirstByValReg();
1496 CCInfo.clearFirstByValReg();
1502
1503 // If parameter size outsides register area, "offset" value
1504 // helps us to calculate stack slot for remained part properly.
1505 offset = RegEnd - RegBegin;
1506
1507 CCInfo.nextInRegsParam();
14971508 }
14981509
1499 if (Flags.getByValSize() - 4*offset > 0) {
1510 if (Flags.getByValSize() > 4*offset) {
15001511 unsigned LocMemOffset = VA.getLocMemOffset();
15011512 SDValue StkPtrOff = DAG.getIntPtrConstant(LocMemOffset);
15021513 SDValue Dst = DAG.getNode(ISD::ADD, dl, getPointerTy(), StackPtr,
17391750 assert((State->getCallOrPrologue() == Prologue ||
17401751 State->getCallOrPrologue() == Call) &&
17411752 "unhandled ParmContext");
1742 if ((!State->isFirstByValRegValid()) &&
1743 (!Subtarget->isAAPCS_ABI() || State->getNextStackOffset() == 0) &&
1744 (ARM::R0 <= reg) && (reg <= ARM::R3)) {
1753
1754 // For in-prologue parameters handling, we also introduce stack offset
1755 // for byval registers: see CallingConvLower.cpp, CCState::HandleByVal.
1756 // This behaviour outsides AAPCS rules (5.5 Parameters Passing) of how
1757 // NSAA should be evaluted (NSAA means "next stacked argument address").
1758 // So: NextStackOffset = NSAAOffset + SizeOfByValParamsStoredInRegs.
1759 // Then: NSAAOffset = NextStackOffset - SizeOfByValParamsStoredInRegs.
1760 unsigned NSAAOffset = State->getNextStackOffset();
1761 if (State->getCallOrPrologue() != Call) {
1762 for (unsigned i = 0, e = State->getInRegsParamsCount(); i != e; ++i) {
1763 unsigned RB, RE;
1764 State->getInRegsParamInfo(i, RB, RE);
1765 assert(NSAAOffset >= (RE-RB)*4 &&
1766 "Stack offset for byval regs doesn't introduced anymore?");
1767 NSAAOffset -= (RE-RB)*4;
1768 }
1769 }
1770 if ((ARM::R0 <= reg) && (reg <= ARM::R3)) {
17451771 if (Subtarget->isAAPCS_ABI() && Align > 4) {
17461772 unsigned AlignInRegs = Align / 4;
17471773 unsigned Waste = (ARM::R4 - reg) % AlignInRegs;
17491775 reg = State->AllocateReg(GPRArgRegs, 4);
17501776 }
17511777 if (reg != 0) {
1752 State->setFirstByValReg(reg);
1778 unsigned excess = 4 * (ARM::R4 - reg);
1779
1780 // Special case when NSAA != SP and parameter size greater than size of
1781 // all remained GPR regs. In that case we can't split parameter, we must
1782 // send it to stack. We also must set NCRN to R4, so waste all
1783 // remained registers.
1784 if (Subtarget->isAAPCS_ABI() && NSAAOffset != 0 && size > excess) {
1785 while (State->AllocateReg(GPRArgRegs, 4))
1786 ;
1787 return;
1788 }
1789
1790 // First register for byval parameter is the first register that wasn't
1791 // allocated before this method call, so it would be "reg".
1792 // If parameter is small enough to be saved in range [reg, r4), then
1793 // the end (first after last) register would be reg + param-size-in-regs,
1794 // else parameter would be splitted between registers and stack,
1795 // end register would be r4 in this case.
1796 unsigned ByValRegBegin = reg;
1797 unsigned ByValRegEnd = (size < excess) ? reg + size/4 : ARM::R4;
1798 State->addInRegsParamInfo(ByValRegBegin, ByValRegEnd);
1799 // Note, first register is allocated in the beginning of function already,
1800 // allocate remained amount of registers we need.
1801 for (unsigned i = reg+1; i != ByValRegEnd; ++i)
1802 State->AllocateReg(GPRArgRegs, 4);
17531803 // At a call site, a byval parameter that is split between
17541804 // registers and memory needs its size truncated here. In a
17551805 // function prologue, such byval parameters are reassembled in
17561806 // memory, and are not truncated.
17571807 if (State->getCallOrPrologue() == Call) {
1758 unsigned excess = 4 * (ARM::R4 - reg);
1759 assert(size >= excess && "expected larger existing stack allocation");
1760 size -= excess;
1808 // Make remained size equal to 0 in case, when
1809 // the whole structure may be stored into registers.
1810 if (size < excess)
1811 size = 0;
1812 else
1813 size -= excess;
17611814 }
17621815 }
17631816 }
1764 // Confiscate any remaining parameter registers to preclude their
1765 // assignment to subsequent parameters.
1766 while (State->AllocateReg(GPRArgRegs, 4))
1767 ;
17681817 }
17691818
17701819 /// MatchingStackOffset - Return true if the given stack call argument is
25792628
25802629 void
25812630 ARMTargetLowering::computeRegArea(CCState &CCInfo, MachineFunction &MF,
2631 unsigned InRegsParamRecordIdx,
25822632 unsigned &ArgRegsSize,
25832633 unsigned &ArgRegsSaveSize)
25842634 const {
25852635 unsigned NumGPRs;
2586 if (CCInfo.isFirstByValRegValid())
2587 NumGPRs = ARM::R4 - CCInfo.getFirstByValReg();
2588 else {
2636 if (InRegsParamRecordIdx < CCInfo.getInRegsParamsCount()) {
2637 unsigned RBegin, REnd;
2638 CCInfo.getInRegsParamInfo(InRegsParamRecordIdx, RBegin, REnd);
2639 NumGPRs = REnd - RBegin;
2640 } else {
25892641 unsigned int firstUnalloced;
25902642 firstUnalloced = CCInfo.getFirstUnallocated(GPRArgRegs,
25912643 sizeof(GPRArgRegs) /
26102662 ARMTargetLowering::StoreByValRegs(CCState &CCInfo, SelectionDAG &DAG,
26112663 DebugLoc dl, SDValue &Chain,
26122664 const Value *OrigArg,
2665 unsigned InRegsParamRecordIdx,
26132666 unsigned OffsetFromOrigArg,
26142667 unsigned ArgOffset,
26152668 bool ForceMutable) const {
26282681 MachineFunction &MF = DAG.getMachineFunction();
26292682 MachineFrameInfo *MFI = MF.getFrameInfo();
26302683 ARMFunctionInfo *AFI = MF.getInfo();
2631 unsigned firstRegToSaveIndex;
2632 if (CCInfo.isFirstByValRegValid())
2633 firstRegToSaveIndex = CCInfo.getFirstByValReg() - ARM::R0;
2634 else {
2684 unsigned firstRegToSaveIndex, lastRegToSaveIndex;
2685 unsigned RBegin, REnd;
2686 if (InRegsParamRecordIdx < CCInfo.getInRegsParamsCount()) {
2687 CCInfo.getInRegsParamInfo(InRegsParamRecordIdx, RBegin, REnd);
2688 firstRegToSaveIndex = RBegin - ARM::R0;
2689 lastRegToSaveIndex = REnd - ARM::R0;
2690 } else {
26352691 firstRegToSaveIndex = CCInfo.getFirstUnallocated
26362692 (GPRArgRegs, sizeof(GPRArgRegs) / sizeof(GPRArgRegs[0]));
2693 lastRegToSaveIndex = 4;
26372694 }
26382695
26392696 unsigned ArgRegsSize, ArgRegsSaveSize;
2640 computeRegArea(CCInfo, MF, ArgRegsSize, ArgRegsSaveSize);
2697 computeRegArea(CCInfo, MF, InRegsParamRecordIdx, ArgRegsSize, ArgRegsSaveSize);
26412698
26422699 // Store any by-val regs to their spots on the stack so that they may be
26432700 // loaded by deferencing the result of formal parameter pointer or va_next.
26442701 // Note: once stack area for byval/varargs registers
26452702 // was initialized, it can't be initialized again.
2646 if (!AFI->getArgRegsSaveSize() && ArgRegsSaveSize) {
2647
2648 AFI->setArgRegsSaveSize(ArgRegsSaveSize);
2703 if (ArgRegsSaveSize) {
26492704
26502705 int FrameIndex = MFI->CreateFixedObject(
26512706 ArgRegsSaveSize,
26542709 SDValue FIN = DAG.getFrameIndex(FrameIndex, getPointerTy());
26552710
26562711 SmallVector MemOps;
2657 for (unsigned i = 0; firstRegToSaveIndex < 4; ++firstRegToSaveIndex, ++i) {
2712 for (unsigned i = 0; firstRegToSaveIndex < lastRegToSaveIndex;
2713 ++firstRegToSaveIndex, ++i) {
26582714 const TargetRegisterClass *RC;
26592715 if (AFI->isThumb1OnlyFunction())
26602716 RC = &ARM::tGPRRegClass;
26712727 FIN = DAG.getNode(ISD::ADD, dl, getPointerTy(), FIN,
26722728 DAG.getConstant(4, getPointerTy()));
26732729 }
2730
2731 AFI->setArgRegsSaveSize(ArgRegsSaveSize + AFI->getArgRegsSaveSize());
2732
26742733 if (!MemOps.empty())
26752734 Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other,
26762735 &MemOps[0], MemOps.size());
26952754 // If there is no regs to be stored, just point address after last
26962755 // argument passed via stack.
26972756 int FrameIndex =
2698 StoreByValRegs(CCInfo, DAG, dl, Chain, 0, 0, ArgOffset, ForceMutable);
2757 StoreByValRegs(CCInfo, DAG, dl, Chain, 0, CCInfo.getInRegsParamsCount(),
2758 0, ArgOffset, ForceMutable);
26992759
27002760 AFI->setVarArgsFrameIndex(FrameIndex);
27012761 }
27262786 SDValue ArgValue;
27272787 Function::const_arg_iterator CurOrigArg = MF.getFunction()->arg_begin();
27282788 unsigned CurArgIdx = 0;
2789
2790 // Initially ArgRegsSaveSize is zero.
2791 // Then we increase this value each time we meet byval parameter.
2792 // We also increase this value in case of varargs function.
2793 AFI->setArgRegsSaveSize(0);
2794
27292795 for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) {
27302796 CCValAssign &VA = ArgLocs[i];
27312797 std::advance(CurOrigArg, Ins[VA.getValNo()].OrigArgIndex - CurArgIdx);
28232889 // Since they could be overwritten by lowering of arguments in case of
28242890 // a tail call.
28252891 if (Flags.isByVal()) {
2892 unsigned CurByValIndex = CCInfo.getInRegsParamsProceed();
28262893 int FrameIndex = StoreByValRegs(
2827 CCInfo, DAG, dl, Chain, CurOrigArg,
2828 Ins[VA.getValNo()].PartOffset,
2829 VA.getLocMemOffset(),
2830 true /*force mutable frames*/);
2894 CCInfo, DAG, dl, Chain, CurOrigArg,
2895 CurByValIndex,
2896 Ins[VA.getValNo()].PartOffset,
2897 VA.getLocMemOffset(),
2898 true /*force mutable frames*/);
28312899 InVals.push_back(DAG.getFrameIndex(FrameIndex, getPointerTy()));
2900 CCInfo.nextInRegsParam();
28322901 } else {
28332902 int FI = MFI->CreateFixedObject(VA.getLocVT().getSizeInBits()/8,
28342903 VA.getLocMemOffset(), true);
476476 int StoreByValRegs(CCState &CCInfo, SelectionDAG &DAG,
477477 DebugLoc dl, SDValue &Chain,
478478 const Value *OrigArg,
479 unsigned InRegsParamRecordIdx,
479480 unsigned OffsetFromOrigArg,
480481 unsigned ArgOffset,
481482 bool ForceMutable) const;
486487 bool ForceMutable = false) const;
487488
488489 void computeRegArea(CCState &CCInfo, MachineFunction &MF,
490 unsigned InRegsParamRecordIdx,
489491 unsigned &ArgRegsSize,
490492 unsigned &ArgRegsSaveSize) const;
491493
0 ;PR15293: ARM codegen ice - expected larger existing stack allocation
1 ;RUN: llc -mtriple=arm-linux-gnueabihf < %s | FileCheck %s
2
3 ;CHECK: foo:
4 ;CHECK: sub sp, sp, #8
5 ;CHECK: push {r11, lr}
6 ;CHECK: str r0, [sp, #12]
7 ;CHECK: add r0, sp, #12
8 ;CHECK: bl fooUseParam
9 ;CHECK: pop {r11, lr}
10 ;CHECK: add sp, sp, #8
11 ;CHECK: mov pc, lr
12
13 ;CHECK: foo2:
14 ;CHECK: sub sp, sp, #16
15 ;CHECK: push {r11, lr}
16 ;CHECK: str r0, [sp, #12]
17 ;CHECK: add r0, sp, #12
18 ;CHECK: str r2, [sp, #16]
19 ;CHECK: bl fooUseParam
20 ;CHECK: add r0, sp, #16
21 ;CHECK: bl fooUseParam
22 ;CHECK: pop {r11, lr}
23 ;CHECK: add sp, sp, #16
24 ;CHECK: mov pc, lr
25
26 ;CHECK: doFoo:
27 ;CHECK: push {r11, lr}
28 ;CHECK: ldr r0,
29 ;CHECK: ldr r0, [r0]
30 ;CHECK: bl foo
31 ;CHECK: pop {r11, lr}
32 ;CHECK: mov pc, lr
33
34
35 ;CHECK: doFoo2:
36 ;CHECK: push {r11, lr}
37 ;CHECK: ldr r0,
38 ;CHECK: mov r1, #0
39 ;CHECK: ldr r0, [r0]
40 ;CHECK: mov r2, r0
41 ;CHECK: bl foo2
42 ;CHECK: pop {r11, lr}
43 ;CHECK: mov pc, lr
44
45
46 %artz = type { i32 }
47 @static_val = constant %artz { i32 777 }
48
49 declare void @fooUseParam(%artz* )
50
51 define void @foo(%artz* byval %s) {
52 call void @fooUseParam(%artz* %s)
53 ret void
54 }
55
56 define void @foo2(%artz* byval %s, i32 %p, %artz* byval %s2) {
57 call void @fooUseParam(%artz* %s)
58 call void @fooUseParam(%artz* %s2)
59 ret void
60 }
61
62
63 define void @doFoo() {
64 call void @foo(%artz* byval @static_val)
65 ret void
66 }
67
68 define void @doFoo2() {
69 call void @foo2(%artz* byval @static_val, i32 0, %artz* byval @static_val)
70 ret void
71 }
72
0 ;Check AAPCS, 5.5 Parameters Passing, C4 and C5 rules.
1 ;Check case when NSAA != 0, and NCRN < R4, NCRN+ParamSize < R4
2 ;RUN: llc -mtriple=thumbv7-linux-gnueabihf -float-abi=hard < %s | FileCheck %s
3
4 %st_t = type { i32, i32 }
5 @static_val = constant %st_t { i32 777, i32 888}
6
7 declare void @fooUseStruct(%st_t*)
8
9 define void @foo(double %vfp0, ; --> D0, NSAA=SP
10 double %vfp1, ; --> D1, NSAA=SP
11 double %vfp2, ; --> D2, NSAA=SP
12 double %vfp3, ; --> D3, NSAA=SP
13 double %vfp4, ; --> D4, NSAA=SP
14 double %vfp5, ; --> D5, NSAA=SP
15 double %vfp6, ; --> D6, NSAA=SP
16 double %vfp7, ; --> D7, NSAA=SP
17 double %vfp8, ; --> SP, NSAA=SP+8 (!)
18 i32 %p0, ; --> R0, NSAA=SP+8
19 %st_t* byval %p1, ; --> R1, R2, NSAA=SP+8
20 i32 %p2, ; --> R3, NSAA=SP+8
21 i32 %p3) #0 { ; --> SP+4, NSAA=SP+12
22 entry:
23 ;CHECK: sub sp, #8
24 ;CHECK: push.w {r11, lr}
25 ;CHECK: add r0, sp, #16
26 ;CHECK: str r2, [sp, #20]
27 ;CHECK: str r1, [sp, #16]
28 ;CHECK: bl fooUseStruct
29 call void @fooUseStruct(%st_t* %p1)
30 ret void
31 }
32
33 define void @doFoo() {
34 entry:
35 call void @foo(double 23.0,
36 double 23.1,
37 double 23.2,
38 double 23.3,
39 double 23.4,
40 double 23.5,
41 double 23.6,
42 double 23.7,
43 double 23.8,
44 i32 0, %st_t* byval @static_val, i32 1, i32 2)
45 ret void
46 }
47
0 ;Check AAPCS, 5.5 Parameters Passing, C4 and C5 rules.
1 ;Check case when NSAA != 0, and NCRN < R4, NCRN+ParamSize > R4
2 ;RUN: llc -mtriple=thumbv7-linux-gnueabihf -float-abi=hard < %s | FileCheck %s
3
4 %st_t = type { i32, i32, i32, i32 }
5 @static_val = constant %st_t { i32 777, i32 888, i32 787, i32 878}
6
7 define void @foo(double %vfp0, ; --> D0, NSAA=SP
8 double %vfp1, ; --> D1, NSAA=SP
9 double %vfp2, ; --> D2, NSAA=SP
10 double %vfp3, ; --> D3, NSAA=SP
11 double %vfp4, ; --> D4, NSAA=SP
12 double %vfp5, ; --> D5, NSAA=SP
13 double %vfp6, ; --> D6, NSAA=SP
14 double %vfp7, ; --> D7, NSAA=SP
15 double %vfp8, ; --> SP, NSAA=SP+8 (!)
16 i32 %p0, ; --> R0, NSAA=SP+8
17 %st_t* byval %p1, ; --> SP+8, 4 words NSAA=SP+24
18 i32 %p2) #0 { ; --> SP+24, NSAA=SP+24
19
20 entry:
21 ;CHECK: push.w {r11, lr}
22 ;CHECK: ldr r0, [sp, #32]
23 ;CHECK: bl fooUseI32
24 call void @fooUseI32(i32 %p2)
25 ret void
26 }
27
28 declare void @fooUseI32(i32)
29
30 define void @doFoo() {
31 entry:
32 call void @foo(double 23.0,
33 double 23.1,
34 double 23.2,
35 double 23.3,
36 double 23.4,
37 double 23.5,
38 double 23.6,
39 double 23.7,
40 double 23.8,
41 i32 0, %st_t* byval @static_val, i32 1)
42 ret void
43 }
44