llvm.org GIT mirror llvm / 9436574
musttail: Forward regparms of variadic functions on x86_64 Summary: If a variadic function body contains a musttail call, then we copy all of the remaining register parameters into virtual registers in the function prologue. We track the virtual registers through the function body, and add them as additional registers to pass to the call. Because this is all done in virtual registers, the register allocator usually gives us good code. If the function does a call, however, it will have to spill and reload all argument registers (ew). Forwarding regparms on x86_32 is not implemented because most compilers don't support varargs in 32-bit with regparms. Reviewers: majnemer Subscribers: aemerson, llvm-commits Differential Revision: http://reviews.llvm.org/D5060 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216780 91177308-0d34-0410-b5e6-96231b3b80d8 Reid Kleckner 6 years ago
5 changed file(s) with 289 addition(s) and 72 deletion(s). Raw diff Collapse all Expand all
238238 /// True if the function contains a call to the llvm.vastart intrinsic.
239239 bool HasVAStart;
240240
241 /// True if this is a varargs function that contains a musttail call.
242 bool HasMustTailInVarArgFunc;
243
241244 const TargetFrameLowering *getFrameLowering() const;
242245 public:
243246 explicit MachineFrameInfo(const TargetMachine &TM, bool RealignOpt)
259262 UseLocalStackAllocationBlock = false;
260263 HasInlineAsmWithSPAdjust = false;
261264 HasVAStart = false;
265 HasMustTailInVarArgFunc = false;
262266 }
263267
264268 /// hasStackObjects - Return true if there are any stack objects in this
482486 bool hasVAStart() const { return HasVAStart; }
483487 void setHasVAStart(bool B) { HasVAStart = B; }
484488
489 /// Returns true if the function is variadic and contains a musttail call.
490 bool hasMustTailInVarArgFunc() const { return HasMustTailInVarArgFunc; }
491 void setHasMustTailInVarArgFunc(bool B) { HasMustTailInVarArgFunc = B; }
492
485493 /// getMaxCallFrameSize - Return the maximum size of a call frame that must be
486494 /// allocated for an outgoing function call. This is only available if
487495 /// CallFrameSetup/Destroy pseudo instructions are used by the target, and
143143 if (const auto *II = dyn_cast(I)) {
144144 if (II->getIntrinsicID() == Intrinsic::vastart)
145145 MF->getFrameInfo()->setHasVAStart(true);
146 }
147
148 // If we have a musttail call in a variadic funciton, we need to ensure we
149 // forward implicit register parameters.
150 if (auto *CI = dyn_cast(I)) {
151 if (CI->isMustTailCall() && Fn->isVarArg())
152 MF->getFrameInfo()->setHasMustTailInVarArgFunc(true);
146153 }
147154
148155 // Mark values used outside their block as exported, by allocating
23252325 }
23262326 }
23272327
2328 static ArrayRef get64BitArgumentGPRs(CallingConv::ID CallConv,
2329 const X86Subtarget *Subtarget) {
2330 assert(Subtarget->is64Bit());
2331
2332 if (Subtarget->isCallingConvWin64(CallConv)) {
2333 static const MCPhysReg GPR64ArgRegsWin64[] = {
2334 X86::RCX, X86::RDX, X86::R8, X86::R9
2335 };
2336 return GPR64ArgRegsWin64;
2337 }
2338
2339 static const MCPhysReg GPR64ArgRegs64Bit[] = {
2340 X86::RDI, X86::RSI, X86::RDX, X86::RCX, X86::R8, X86::R9
2341 };
2342 return GPR64ArgRegs64Bit;
2343 }
2344
2345 static ArrayRef get64BitArgumentXMMs(MachineFunction &MF,
2346 CallingConv::ID CallConv,
2347 const X86Subtarget *Subtarget) {
2348 assert(Subtarget->is64Bit());
2349 if (Subtarget->isCallingConvWin64(CallConv)) {
2350 // The XMM registers which might contain var arg parameters are shadowed
2351 // in their paired GPR. So we only need to save the GPR to their home
2352 // slots.
2353 return None;
2354 }
2355
2356 const Function *Fn = MF.getFunction();
2357 bool NoImplicitFloatOps = Fn->getAttributes().
2358 hasAttribute(AttributeSet::FunctionIndex, Attribute::NoImplicitFloat);
2359 assert(!(MF.getTarget().Options.UseSoftFloat && NoImplicitFloatOps) &&
2360 "SSE register cannot be used when SSE is disabled!");
2361 if (MF.getTarget().Options.UseSoftFloat || NoImplicitFloatOps ||
2362 !Subtarget->hasSSE1())
2363 // Kernel mode asks for SSE to be disabled, so there are no XMM argument
2364 // registers.
2365 return None;
2366
2367 static const MCPhysReg XMMArgRegs64Bit[] = {
2368 X86::XMM0, X86::XMM1, X86::XMM2, X86::XMM3,
2369 X86::XMM4, X86::XMM5, X86::XMM6, X86::XMM7
2370 };
2371 return XMMArgRegs64Bit;
2372 }
2373
23282374 SDValue
23292375 X86TargetLowering::LowerFormalArguments(SDValue Chain,
23302376 CallingConv::ID CallConv,
24682514 // If the function takes variable number of arguments, make a frame index for
24692515 // the start of the first vararg value... for expansion of llvm.va_start. We
24702516 // can skip this if there are no va_start calls.
2471 if (isVarArg && MFI->hasVAStart()) {
2472 if (Is64Bit || (CallConv != CallingConv::X86_FastCall &&
2473 CallConv != CallingConv::X86_ThisCall)) {
2474 FuncInfo->setVarArgsFrameIndex(MFI->CreateFixedObject(1, StackSize,true));
2475 }
2476 if (Is64Bit) {
2477 unsigned TotalNumIntRegs = 0, TotalNumXMMRegs = 0;
2478
2479 // FIXME: We should really autogenerate these arrays
2480 static const MCPhysReg GPR64ArgRegsWin64[] = {
2481 X86::RCX, X86::RDX, X86::R8, X86::R9
2482 };
2483 static const MCPhysReg GPR64ArgRegs64Bit[] = {
2484 X86::RDI, X86::RSI, X86::RDX, X86::RCX, X86::R8, X86::R9
2485 };
2486 static const MCPhysReg XMMArgRegs64Bit[] = {
2487 X86::XMM0, X86::XMM1, X86::XMM2, X86::XMM3,
2488 X86::XMM4, X86::XMM5, X86::XMM6, X86::XMM7
2489 };
2490 const MCPhysReg *GPR64ArgRegs;
2491 unsigned NumXMMRegs = 0;
2492
2493 if (IsWin64) {
2494 // The XMM registers which might contain var arg parameters are shadowed
2495 // in their paired GPR. So we only need to save the GPR to their home
2496 // slots.
2497 TotalNumIntRegs = 4;
2498 GPR64ArgRegs = GPR64ArgRegsWin64;
2499 } else {
2500 TotalNumIntRegs = 6; TotalNumXMMRegs = 8;
2501 GPR64ArgRegs = GPR64ArgRegs64Bit;
2502
2503 NumXMMRegs = CCInfo.getFirstUnallocated(XMMArgRegs64Bit,
2504 TotalNumXMMRegs);
2517 if (MFI->hasVAStart() &&
2518 (Is64Bit || (CallConv != CallingConv::X86_FastCall &&
2519 CallConv != CallingConv::X86_ThisCall))) {
2520 FuncInfo->setVarArgsFrameIndex(
2521 MFI->CreateFixedObject(1, StackSize, true));
2522 }
2523
2524 // 64-bit calling conventions support varargs and register parameters, so we
2525 // have to do extra work to spill them in the prologue or forward them to
2526 // musttail calls.
2527 if (Is64Bit && isVarArg &&
2528 (MFI->hasVAStart() || MFI->hasMustTailInVarArgFunc())) {
2529 // Find the first unallocated argument registers.
2530 ArrayRef ArgGPRs = get64BitArgumentGPRs(CallConv, Subtarget);
2531 ArrayRef ArgXMMs = get64BitArgumentXMMs(MF, CallConv, Subtarget);
2532 unsigned NumIntRegs =
2533 CCInfo.getFirstUnallocated(ArgGPRs.data(), ArgGPRs.size());
2534 unsigned NumXMMRegs =
2535 CCInfo.getFirstUnallocated(ArgXMMs.data(), ArgXMMs.size());
2536 assert(!(NumXMMRegs && !Subtarget->hasSSE1()) &&
2537 "SSE register cannot be used when SSE is disabled!");
2538
2539 // Gather all the live in physical registers.
2540 SmallVector LiveGPRs;
2541 SmallVector LiveXMMRegs;
2542 SDValue ALVal;
2543 for (MCPhysReg Reg : ArgGPRs.slice(NumIntRegs)) {
2544 unsigned GPR = MF.addLiveIn(Reg, &X86::GR64RegClass);
2545 LiveGPRs.push_back(
2546 DAG.getCopyFromReg(DAG.getEntryNode(), dl, GPR, MVT::i64));
2547 }
2548 if (!ArgXMMs.empty()) {
2549 unsigned AL = MF.addLiveIn(X86::AL, &X86::GR8RegClass);
2550 ALVal = DAG.getCopyFromReg(DAG.getEntryNode(), dl, AL, MVT::i8);
2551 for (MCPhysReg Reg : ArgXMMs.slice(NumXMMRegs)) {
2552 unsigned XMMReg = MF.addLiveIn(Reg, &X86::VR128RegClass);
2553 LiveXMMRegs.push_back(
2554 DAG.getCopyFromReg(Chain, dl, XMMReg, MVT::v4f32));
25052555 }
2506 unsigned NumIntRegs = CCInfo.getFirstUnallocated(GPR64ArgRegs,
2507 TotalNumIntRegs);
2508
2509 bool NoImplicitFloatOps = Fn->getAttributes().
2510 hasAttribute(AttributeSet::FunctionIndex, Attribute::NoImplicitFloat);
2511 assert(!(NumXMMRegs && !Subtarget->hasSSE1()) &&
2512 "SSE register cannot be used when SSE is disabled!");
2513 assert(!(NumXMMRegs && MF.getTarget().Options.UseSoftFloat &&
2514 NoImplicitFloatOps) &&
2515 "SSE register cannot be used when SSE is disabled!");
2516 if (MF.getTarget().Options.UseSoftFloat || NoImplicitFloatOps ||
2517 !Subtarget->hasSSE1())
2518 // Kernel mode asks for SSE to be disabled, so don't push them
2519 // on the stack.
2520 TotalNumXMMRegs = 0;
2521
2556 }
2557
2558 // Store them to the va_list returned by va_start.
2559 if (MFI->hasVAStart()) {
25222560 if (IsWin64) {
25232561 const TargetFrameLowering &TFI = *MF.getSubtarget().getFrameLowering();
25242562 // Get to the caller-allocated home save location. Add 8 to account
25342572 // registers, then we must store them to their spots on the stack so
25352573 // they may be loaded by deferencing the result of va_next.
25362574 FuncInfo->setVarArgsGPOffset(NumIntRegs * 8);
2537 FuncInfo->setVarArgsFPOffset(TotalNumIntRegs * 8 + NumXMMRegs * 16);
2538 FuncInfo->setRegSaveFrameIndex(
2539 MFI->CreateStackObject(TotalNumIntRegs * 8 + TotalNumXMMRegs * 16, 16,
2540 false));
2575 FuncInfo->setVarArgsFPOffset(ArgGPRs.size() * 8 + NumXMMRegs * 16);
2576 FuncInfo->setRegSaveFrameIndex(MFI->CreateStackObject(
2577 ArgGPRs.size() * 8 + ArgXMMs.size() * 16, 16, false));
25412578 }
25422579
25432580 // Store the integer parameter registers.
25452582 SDValue RSFIN = DAG.getFrameIndex(FuncInfo->getRegSaveFrameIndex(),
25462583 getPointerTy());
25472584 unsigned Offset = FuncInfo->getVarArgsGPOffset();
2548 for (; NumIntRegs != TotalNumIntRegs; ++NumIntRegs) {
2585 for (SDValue Val : LiveGPRs) {
25492586 SDValue FIN = DAG.getNode(ISD::ADD, dl, getPointerTy(), RSFIN,
25502587 DAG.getIntPtrConstant(Offset));
2551 unsigned VReg = MF.addLiveIn(GPR64ArgRegs[NumIntRegs],
2552 &X86::GR64RegClass);
2553 SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, MVT::i64);
25542588 SDValue Store =
25552589 DAG.getStore(Val.getValue(1), dl, Val, FIN,
25562590 MachinePointerInfo::getFixedStack(
25602594 Offset += 8;
25612595 }
25622596
2563 if (TotalNumXMMRegs != 0 && NumXMMRegs != TotalNumXMMRegs) {
2597 if (!ArgXMMs.empty() && NumXMMRegs != ArgXMMs.size()) {
25642598 // Now store the XMM (fp + vector) parameter registers.
25652599 SmallVector SaveXMMOps;
25662600 SaveXMMOps.push_back(Chain);
2567
2568 unsigned AL = MF.addLiveIn(X86::AL, &X86::GR8RegClass);
2569 SDValue ALVal = DAG.getCopyFromReg(DAG.getEntryNode(), dl, AL, MVT::i8);
25702601 SaveXMMOps.push_back(ALVal);
2571
25722602 SaveXMMOps.push_back(DAG.getIntPtrConstant(
25732603 FuncInfo->getRegSaveFrameIndex()));
25742604 SaveXMMOps.push_back(DAG.getIntPtrConstant(
25752605 FuncInfo->getVarArgsFPOffset()));
2576
2577 for (; NumXMMRegs != TotalNumXMMRegs; ++NumXMMRegs) {
2578 unsigned VReg = MF.addLiveIn(XMMArgRegs64Bit[NumXMMRegs],
2579 &X86::VR128RegClass);
2580 SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, MVT::v4f32);
2581 SaveXMMOps.push_back(Val);
2582 }
2606 SaveXMMOps.insert(SaveXMMOps.end(), LiveXMMRegs.begin(),
2607 LiveXMMRegs.end());
25832608 MemOps.push_back(DAG.getNode(X86ISD::VASTART_SAVE_XMM_REGS, dl,
25842609 MVT::Other, SaveXMMOps));
25852610 }
25862611
25872612 if (!MemOps.empty())
25882613 Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps);
2614 } else {
2615 // TODO: Save virtual registers away some where so we can do
2616 // getCopyFromReg in the musttail call lowering bb.
2617 assert(MFI->hasMustTailInVarArgFunc());
2618 auto &Forwards = FuncInfo->getForwardedMustTailRegParms();
2619 typedef X86MachineFunctionInfo::Forward Forward;
2620
2621 // Add all GPRs, al, and XMMs to the list of forwards.
2622 for (unsigned I = 0, E = LiveGPRs.size(); I != E; ++I) {
2623 unsigned VReg =
2624 MF.getRegInfo().createVirtualRegister(&X86::GR64RegClass);
2625 Chain = DAG.getCopyToReg(Chain, dl, VReg, LiveGPRs[I]);
2626 Forwards.push_back(Forward(VReg, ArgGPRs[NumIntRegs + I], MVT::i64));
2627 }
2628
2629 if (!ArgXMMs.empty()) {
2630 unsigned ALVReg =
2631 MF.getRegInfo().createVirtualRegister(&X86::GR8RegClass);
2632 Chain = DAG.getCopyToReg(Chain, dl, ALVReg, ALVal);
2633 Forwards.push_back(Forward(ALVReg, X86::AL, MVT::i8));
2634
2635 for (unsigned I = 0, E = LiveXMMRegs.size(); I != E; ++I) {
2636 unsigned VReg =
2637 MF.getRegInfo().createVirtualRegister(&X86::VR128RegClass);
2638 Chain = DAG.getCopyToReg(Chain, dl, VReg, LiveXMMRegs[I]);
2639 Forwards.push_back(
2640 Forward(VReg, ArgXMMs[NumXMMRegs + I], MVT::v4f32));
2641 }
2642 }
25892643 }
25902644 }
25912645
26882742 bool IsWin64 = Subtarget->isCallingConvWin64(CallConv);
26892743 StructReturnType SR = callIsStructReturn(Outs);
26902744 bool IsSibcall = false;
2745 X86MachineFunctionInfo *X86Info = MF.getInfo();
26912746
26922747 if (MF.getTarget().Options.DisableTailCalls)
26932748 isTailCall = false;
27402795 int FPDiff = 0;
27412796 if (isTailCall && !IsSibcall && !IsMustTail) {
27422797 // Lower arguments at fp - stackoffset + fpdiff.
2743 X86MachineFunctionInfo *X86Info = MF.getInfo();
27442798 unsigned NumBytesCallerPushed = X86Info->getBytesToPopOnReturn();
27452799
27462800 FPDiff = NumBytesCallerPushed - NumBytes;
28832937 }
28842938 }
28852939
2886 if (Is64Bit && isVarArg && !IsWin64) {
2940 if (Is64Bit && isVarArg && !IsWin64 && !IsMustTail) {
28872941 // From AMD64 ABI document:
28882942 // For calls that may call functions that use varargs or stdargs
28892943 // (prototype-less calls or calls to functions containing ellipsis (...) in
29032957
29042958 RegsToPass.push_back(std::make_pair(unsigned(X86::AL),
29052959 DAG.getConstant(NumXMMRegs, MVT::i8)));
2960 }
2961
2962 if (Is64Bit && isVarArg && IsMustTail) {
2963 const auto &Forwards = X86Info->getForwardedMustTailRegParms();
2964 for (const auto &F : Forwards) {
2965 SDValue Val = DAG.getCopyFromReg(Chain, dl, F.VReg, F.VT);
2966 RegsToPass.push_back(std::make_pair(unsigned(F.PReg), Val));
2967 }
29062968 }
29072969
29082970 // For tail calls lower the arguments to the 'real' stack slots. Sibcalls
1414 #define LLVM_LIB_TARGET_X86_X86MACHINEFUNCTIONINFO_H
1515
1616 #include "llvm/CodeGen/MachineFunction.h"
17 #include "llvm/CodeGen/MachineValueType.h"
18 #include
1719
1820 namespace llvm {
1921
6769 unsigned ArgumentStackSize;
6870 /// NumLocalDynamics - Number of local-dynamic TLS accesses.
6971 unsigned NumLocalDynamics;
72
73 public:
74 /// Describes a register that needs to be forwarded from the prologue to a
75 /// musttail call.
76 struct Forward {
77 Forward(unsigned VReg, MCPhysReg PReg, MVT VT)
78 : VReg(VReg), PReg(PReg), VT(VT) {}
79 unsigned VReg;
80 MCPhysReg PReg;
81 MVT VT;
82 };
83
84 private:
85 /// ForwardedMustTailRegParms - A list of virtual and physical registers
86 /// that must be forwarded to every musttail call.
87 std::vector ForwardedMustTailRegParms;
7088
7189 public:
7290 X86MachineFunctionInfo() : ForceFramePointer(false),
137155 unsigned getNumLocalDynamicTLSAccesses() const { return NumLocalDynamics; }
138156 void incNumLocalDynamicTLSAccesses() { ++NumLocalDynamics; }
139157
158 std::vector &getForwardedMustTailRegParms() {
159 return ForwardedMustTailRegParms;
160 }
140161 };
141162
142163 } // End llvm namespace
0 ; RUN: llc < %s -enable-tail-merge=0 -mtriple=x86_64-linux | FileCheck %s --check-prefix=LINUX
1 ; RUN: llc < %s -enable-tail-merge=0 -mtriple=x86_64-windows | FileCheck %s --check-prefix=WINDOWS
2
3 ; Test that we actually spill and reload all arguments in the variadic argument
4 ; pack. Doing a normal call will clobber all argument registers, and we will
5 ; spill around it. A simple adjustment should not require any XMM spills.
6
7 declare void(i8*, ...)* @get_f(i8* %this)
8
9 define void @f_thunk(i8* %this, ...) {
10 %fptr = call void(i8*, ...)*(i8*)* @get_f(i8* %this)
11 musttail call void (i8*, ...)* %fptr(i8* %this, ...)
12 ret void
13 }
14
15 ; Save and restore 6 GPRs, 8 XMMs, and AL around the call.
16
17 ; LINUX-LABEL: f_thunk:
18 ; LINUX-DAG: movq %rdi, {{.*}}
19 ; LINUX-DAG: movq %rsi, {{.*}}
20 ; LINUX-DAG: movq %rdx, {{.*}}
21 ; LINUX-DAG: movq %rcx, {{.*}}
22 ; LINUX-DAG: movq %r8, {{.*}}
23 ; LINUX-DAG: movq %r9, {{.*}}
24 ; LINUX-DAG: movb %al, {{.*}}
25 ; LINUX-DAG: movaps %xmm0, {{[0-9]*}}(%rsp)
26 ; LINUX-DAG: movaps %xmm1, {{[0-9]*}}(%rsp)
27 ; LINUX-DAG: movaps %xmm2, {{[0-9]*}}(%rsp)
28 ; LINUX-DAG: movaps %xmm3, {{[0-9]*}}(%rsp)
29 ; LINUX-DAG: movaps %xmm4, {{[0-9]*}}(%rsp)
30 ; LINUX-DAG: movaps %xmm5, {{[0-9]*}}(%rsp)
31 ; LINUX-DAG: movaps %xmm6, {{[0-9]*}}(%rsp)
32 ; LINUX-DAG: movaps %xmm7, {{[0-9]*}}(%rsp)
33 ; LINUX: callq get_f
34 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm0
35 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm1
36 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm2
37 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm3
38 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm4
39 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm5
40 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm6
41 ; LINUX-DAG: movaps {{[0-9]*}}(%rsp), %xmm7
42 ; LINUX-DAG: movq {{.*}}, %rdi
43 ; LINUX-DAG: movq {{.*}}, %rsi
44 ; LINUX-DAG: movq {{.*}}, %rdx
45 ; LINUX-DAG: movq {{.*}}, %rcx
46 ; LINUX-DAG: movq {{.*}}, %r8
47 ; LINUX-DAG: movq {{.*}}, %r9
48 ; LINUX-DAG: movb {{.*}}, %al
49 ; LINUX: jmpq *{{.*}} # TAILCALL
50
51 ; WINDOWS-LABEL: f_thunk:
52 ; WINDOWS-NOT: mov{{.}}ps
53 ; WINDOWS-DAG: movq %rdx, {{.*}}
54 ; WINDOWS-DAG: movq %rcx, {{.*}}
55 ; WINDOWS-DAG: movq %r8, {{.*}}
56 ; WINDOWS-DAG: movq %r9, {{.*}}
57 ; WINDOWS-NOT: mov{{.}}ps
58 ; WINDOWS: callq get_f
59 ; WINDOWS-NOT: mov{{.}}ps
60 ; WINDOWS-DAG: movq {{.*}}, %rdx
61 ; WINDOWS-DAG: movq {{.*}}, %rcx
62 ; WINDOWS-DAG: movq {{.*}}, %r8
63 ; WINDOWS-DAG: movq {{.*}}, %r9
64 ; WINDOWS-NOT: mov{{.}}ps
65 ; WINDOWS: jmpq *{{.*}} # TAILCALL
66
67 ; This thunk shouldn't require any spills and reloads, assuming the register
68 ; allocator knows what it's doing.
69
70 define void @g_thunk(i8* %fptr_i8, ...) {
71 %fptr = bitcast i8* %fptr_i8 to void (i8*, ...)*
72 musttail call void (i8*, ...)* %fptr(i8* %fptr_i8, ...)
73 ret void
74 }
75
76 ; LINUX-LABEL: g_thunk:
77 ; LINUX-NOT: movq
78 ; LINUX: jmpq *%rdi # TAILCALL
79
80 ; WINDOWS-LABEL: g_thunk:
81 ; WINDOWS-NOT: movq
82 ; WINDOWS: jmpq *%rcx # TAILCALL
83
84 ; Do a simple multi-exit multi-bb test.
85
86 %struct.Foo = type { i1, i8*, i8* }
87
88 @g = external global i32
89
90 define void @h_thunk(%struct.Foo* %this, ...) {
91 %cond_p = getelementptr %struct.Foo* %this, i32 0, i32 0
92 %cond = load i1* %cond_p
93 br i1 %cond, label %then, label %else
94
95 then:
96 %a_p = getelementptr %struct.Foo* %this, i32 0, i32 1
97 %a_i8 = load i8** %a_p
98 %a = bitcast i8* %a_i8 to void (%struct.Foo*, ...)*
99 musttail call void (%struct.Foo*, ...)* %a(%struct.Foo* %this, ...)
100 ret void
101
102 else:
103 %b_p = getelementptr %struct.Foo* %this, i32 0, i32 2
104 %b_i8 = load i8** %b_p
105 %b = bitcast i8* %b_i8 to void (%struct.Foo*, ...)*
106 store i32 42, i32* @g
107 musttail call void (%struct.Foo*, ...)* %b(%struct.Foo* %this, ...)
108 ret void
109 }
110
111 ; LINUX-LABEL: h_thunk:
112 ; LINUX: jne
113 ; LINUX: jmpq *{{.*}} # TAILCALL
114 ; LINUX: jmpq *{{.*}} # TAILCALL
115 ; WINDOWS-LABEL: h_thunk:
116 ; WINDOWS: jne
117 ; WINDOWS: jmpq *{{.*}} # TAILCALL
118 ; WINDOWS: jmpq *{{.*}} # TAILCALL