Skip to content

Commit aff1e13

Browse files
rovkaAna Mihajlovic
andcommitted
[AMDGPU] Dynamic VGPR support for llvm.amdgcn.cs.chain
The llvm.amdgcn.cs.chain intrinsic has a 'flags' operand which may indicate that we want to reallocate the VGPRs before performing the call. A call with the following arguments: ``` llvm.amdgcn.cs.chain %callee, %exec, %sgpr_args, %vgpr_args, /*flags*/0x1, %num_vgprs, %fallback_exec, %fallback_callee ``` is supposed to do the following: - copy the SGPR and VGPR args into their respective registers - try to change the VGPR allocation - if the allocation has succeeded, set EXEC to %exec and jump to %callee, otherwise set EXEC to %fallback_exec and jump to %fallback_callee This patch implements the dynamic VGPR behaviour by generating an S_ALLOC_VGPR followed by S_CSELECT_B32/64 instructions for the EXEC and callee. The rest of the call sequence is left undisturbed (i.e. identical to the case where the flags are 0 and we don't use dynamic VGPRs). We achieve this by introducing some new pseudos (SI_CS_CHAIN_TC_Wn_DVGPR) which are expanded in the SILateBranchLowering pass, just like the simpler SI_CS_CHAIN_TC_Wn pseudos. The main reason is so that we don't risk other passes (particularly the PostRA scheduler) introducing instructions between the S_ALLOC_VGPR and the jump. Such instructions might end up using VGPRs that have been deallocated, or the wrong EXEC mask. Once the whole backend treats S_ALLOC_VGPR and changes to EXEC as barriers for instructions that use VGPRs, we could in principle move the expansion earlier (but in the absence of a good reason for that my personal preference is to keep it later in order to make debugging easier). Since the expansion happens after register allocation, we're careful to select constants to immediate operands instead of letting ISel generate S_MOVs which could interfere with register allocation (i.e. make it look like we need more registers than we actually do). For GFX12, S_ALLOC_VGPR only works in wave32 mode, so we bail out during ISel in wave64 mode. However, we can define the pseudos for wave64 too so it's easy to handle if future generations support it. Co-authored-by: Ana Mihajlovic <[email protected]>
1 parent c29d820 commit aff1e13

11 files changed

+746
-151
lines changed

llvm/include/llvm/CodeGen/SelectionDAGISel.h

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -328,20 +328,21 @@ class SelectionDAGISel {
328328
};
329329

330330
enum {
331-
OPFL_None = 0, // Node has no chain or glue input and isn't variadic.
332-
OPFL_Chain = 1, // Node has a chain input.
333-
OPFL_GlueInput = 2, // Node has a glue input.
334-
OPFL_GlueOutput = 4, // Node has a glue output.
335-
OPFL_MemRefs = 8, // Node gets accumulated MemRefs.
336-
OPFL_Variadic0 = 1<<4, // Node is variadic, root has 0 fixed inputs.
337-
OPFL_Variadic1 = 2<<4, // Node is variadic, root has 1 fixed inputs.
338-
OPFL_Variadic2 = 3<<4, // Node is variadic, root has 2 fixed inputs.
339-
OPFL_Variadic3 = 4<<4, // Node is variadic, root has 3 fixed inputs.
340-
OPFL_Variadic4 = 5<<4, // Node is variadic, root has 4 fixed inputs.
341-
OPFL_Variadic5 = 6<<4, // Node is variadic, root has 5 fixed inputs.
342-
OPFL_Variadic6 = 7<<4, // Node is variadic, root has 6 fixed inputs.
343-
344-
OPFL_VariadicInfo = OPFL_Variadic6
331+
OPFL_None = 0, // Node has no chain or glue input and isn't variadic.
332+
OPFL_Chain = 1, // Node has a chain input.
333+
OPFL_GlueInput = 2, // Node has a glue input.
334+
OPFL_GlueOutput = 4, // Node has a glue output.
335+
OPFL_MemRefs = 8, // Node gets accumulated MemRefs.
336+
OPFL_Variadic0 = 1 << 4, // Node is variadic, root has 0 fixed inputs.
337+
OPFL_Variadic1 = 2 << 4, // Node is variadic, root has 1 fixed inputs.
338+
OPFL_Variadic2 = 3 << 4, // Node is variadic, root has 2 fixed inputs.
339+
OPFL_Variadic3 = 4 << 4, // Node is variadic, root has 3 fixed inputs.
340+
OPFL_Variadic4 = 5 << 4, // Node is variadic, root has 4 fixed inputs.
341+
OPFL_Variadic5 = 6 << 4, // Node is variadic, root has 5 fixed inputs.
342+
OPFL_Variadic6 = 7 << 4, // Node is variadic, root has 6 fixed inputs.
343+
OPFL_Variadic7 = 8 << 4, // Node is variadic, root has 7 fixed inputs.
344+
345+
OPFL_VariadicInfo = 15 << 4 // Mask for extracting the OPFL_VariadicN bits.
345346
};
346347

347348
/// getNumFixedFromVariadicInfo - Transform an EmitNode flags word into the

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7996,10 +7996,6 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
79967996
return;
79977997
}
79987998
case Intrinsic::amdgcn_cs_chain: {
7999-
assert(I.arg_size() == 5 && "Additional args not supported yet");
8000-
assert(cast<ConstantInt>(I.getOperand(4))->isZero() &&
8001-
"Non-zero flags not supported yet");
8002-
80037999
// At this point we don't care if it's amdgpu_cs_chain or
80048000
// amdgpu_cs_chain_preserve.
80058001
CallingConv::ID CC = CallingConv::AMDGPU_CS_Chain;
@@ -8026,6 +8022,15 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
80268022
assert(!Args[1].IsInReg && "VGPR args should not be marked inreg");
80278023
Args[2].IsInReg = true; // EXEC should be inreg
80288024

8025+
// Forward the flags and any additional arguments.
8026+
for (unsigned Idx = 4; Idx < I.arg_size(); ++Idx) {
8027+
TargetLowering::ArgListEntry Arg;
8028+
Arg.Node = getValue(I.getOperand(Idx));
8029+
Arg.Ty = I.getOperand(Idx)->getType();
8030+
Arg.setAttributes(&I, Idx);
8031+
Args.push_back(Arg);
8032+
}
8033+
80298034
TargetLowering::CallLoweringInfo CLI(DAG);
80308035
CLI.setDebugLoc(getCurSDLoc())
80318036
.setChain(getRoot())

llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp

Lines changed: 95 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -953,17 +953,22 @@ getAssignFnsForCC(CallingConv::ID CC, const SITargetLowering &TLI) {
953953
}
954954

955955
static unsigned getCallOpcode(const MachineFunction &CallerF, bool IsIndirect,
956-
bool IsTailCall, bool isWave32,
957-
CallingConv::ID CC) {
956+
bool IsTailCall, bool IsWave32,
957+
CallingConv::ID CC,
958+
bool IsDynamicVGPRChainCall = false) {
958959
// For calls to amdgpu_cs_chain functions, the address is known to be uniform.
959960
assert((AMDGPU::isChainCC(CC) || !IsIndirect || !IsTailCall) &&
960961
"Indirect calls can't be tail calls, "
961962
"because the address can be divergent");
962963
if (!IsTailCall)
963964
return AMDGPU::G_SI_CALL;
964965

965-
if (AMDGPU::isChainCC(CC))
966-
return isWave32 ? AMDGPU::SI_CS_CHAIN_TC_W32 : AMDGPU::SI_CS_CHAIN_TC_W64;
966+
if (AMDGPU::isChainCC(CC)) {
967+
if (IsDynamicVGPRChainCall)
968+
return IsWave32 ? AMDGPU::SI_CS_CHAIN_TC_W32_DVGPR
969+
: AMDGPU::SI_CS_CHAIN_TC_W64_DVGPR;
970+
return IsWave32 ? AMDGPU::SI_CS_CHAIN_TC_W32 : AMDGPU::SI_CS_CHAIN_TC_W64;
971+
}
967972

968973
return CC == CallingConv::AMDGPU_Gfx ? AMDGPU::SI_TCRETURN_GFX :
969974
AMDGPU::SI_TCRETURN;
@@ -972,7 +977,8 @@ static unsigned getCallOpcode(const MachineFunction &CallerF, bool IsIndirect,
972977
// Add operands to call instruction to track the callee.
973978
static bool addCallTargetOperands(MachineInstrBuilder &CallInst,
974979
MachineIRBuilder &MIRBuilder,
975-
AMDGPUCallLowering::CallLoweringInfo &Info) {
980+
AMDGPUCallLowering::CallLoweringInfo &Info,
981+
bool IsDynamicVGPRChainCall = false) {
976982
if (Info.Callee.isReg()) {
977983
CallInst.addReg(Info.Callee.getReg());
978984
CallInst.addImm(0);
@@ -983,7 +989,12 @@ static bool addCallTargetOperands(MachineInstrBuilder &CallInst,
983989
auto Ptr = MIRBuilder.buildGlobalValue(
984990
LLT::pointer(GV->getAddressSpace(), 64), GV);
985991
CallInst.addReg(Ptr.getReg(0));
986-
CallInst.add(Info.Callee);
992+
993+
if (IsDynamicVGPRChainCall)
994+
// DynamicVGPR chain calls are always indirect.
995+
CallInst.addImm(0);
996+
else
997+
CallInst.add(Info.Callee);
987998
} else
988999
return false;
9891000

@@ -1177,6 +1188,18 @@ void AMDGPUCallLowering::handleImplicitCallArguments(
11771188
}
11781189
}
11791190

1191+
namespace {
1192+
// Chain calls have special arguments that we need to handle. These have the
1193+
// same index as they do in the llvm.amdgcn.cs.chain intrinsic.
1194+
enum ChainCallArgIdx {
1195+
Exec = 1,
1196+
Flags = 4,
1197+
NumVGPRs = 5,
1198+
FallbackExec = 6,
1199+
FallbackCallee = 7,
1200+
};
1201+
} // anonymous namespace
1202+
11801203
bool AMDGPUCallLowering::lowerTailCall(
11811204
MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info,
11821205
SmallVectorImpl<ArgInfo> &OutArgs) const {
@@ -1185,6 +1208,8 @@ bool AMDGPUCallLowering::lowerTailCall(
11851208
SIMachineFunctionInfo *FuncInfo = MF.getInfo<SIMachineFunctionInfo>();
11861209
const Function &F = MF.getFunction();
11871210
MachineRegisterInfo &MRI = MF.getRegInfo();
1211+
const SIInstrInfo *TII = ST.getInstrInfo();
1212+
const SIRegisterInfo *TRI = ST.getRegisterInfo();
11881213
const SITargetLowering &TLI = *getTLI<SITargetLowering>();
11891214

11901215
// True when we're tail calling, but without -tailcallopt.
@@ -1200,34 +1225,78 @@ bool AMDGPUCallLowering::lowerTailCall(
12001225
if (!IsSibCall)
12011226
CallSeqStart = MIRBuilder.buildInstr(AMDGPU::ADJCALLSTACKUP);
12021227

1203-
unsigned Opc =
1204-
getCallOpcode(MF, Info.Callee.isReg(), true, ST.isWave32(), CalleeCC);
1228+
bool IsChainCall = AMDGPU::isChainCC(Info.CallConv);
1229+
bool IsDynamicVGPRChainCall = false;
1230+
1231+
if (IsChainCall) {
1232+
ArgInfo FlagsArg = Info.OrigArgs[ChainCallArgIdx::Flags];
1233+
const APInt &FlagsValue = cast<ConstantInt>(FlagsArg.OrigValue)->getValue();
1234+
if (FlagsValue.isZero()) {
1235+
if (Info.OrigArgs.size() != 5) {
1236+
LLVM_DEBUG(dbgs() << "No additional args allowed if flags == 0");
1237+
return false;
1238+
}
1239+
} else if (FlagsValue.isOneBitSet(0)) {
1240+
IsDynamicVGPRChainCall = true;
1241+
1242+
if (Info.OrigArgs.size() != 8) {
1243+
LLVM_DEBUG(dbgs() << "Expected 3 additional args");
1244+
return false;
1245+
}
1246+
1247+
// On GFX12, we can only change the VGPR allocation for wave32.
1248+
if (!ST.isWave32()) {
1249+
LLVM_DEBUG(dbgs() << "Dynamic VGPR mode is only supported for wave32");
1250+
return false;
1251+
}
1252+
1253+
ArgInfo FallbackExecArg = Info.OrigArgs[ChainCallArgIdx::FallbackExec];
1254+
assert(FallbackExecArg.Regs.size() == 1 &&
1255+
"Expected single register for fallback EXEC");
1256+
if (!FallbackExecArg.Ty->isIntegerTy(ST.getWavefrontSize())) {
1257+
LLVM_DEBUG(dbgs() << "Bad type for fallback EXEC");
1258+
return false;
1259+
}
1260+
}
1261+
}
1262+
1263+
unsigned Opc = getCallOpcode(MF, Info.Callee.isReg(), /*IsTailCall*/ true,
1264+
ST.isWave32(), CalleeCC, IsDynamicVGPRChainCall);
12051265
auto MIB = MIRBuilder.buildInstrNoInsert(Opc);
1206-
if (!addCallTargetOperands(MIB, MIRBuilder, Info))
1266+
if (!addCallTargetOperands(MIB, MIRBuilder, Info, IsDynamicVGPRChainCall))
12071267
return false;
12081268

12091269
// Byte offset for the tail call. When we are sibcalling, this will always
12101270
// be 0.
12111271
MIB.addImm(0);
12121272

1213-
// If this is a chain call, we need to pass in the EXEC mask.
1214-
const SIRegisterInfo *TRI = ST.getRegisterInfo();
1215-
if (AMDGPU::isChainCC(Info.CallConv)) {
1216-
ArgInfo ExecArg = Info.OrigArgs[1];
1273+
// If this is a chain call, we need to pass in the EXEC mask as well as any
1274+
// other special args.
1275+
if (IsChainCall) {
1276+
auto AddRegOrImm = [&](const ArgInfo &Arg) {
1277+
if (auto CI = dyn_cast<ConstantInt>(Arg.OrigValue)) {
1278+
MIB.addImm(CI->getSExtValue());
1279+
} else {
1280+
MIB.addReg(Arg.Regs[0]);
1281+
unsigned Idx = MIB->getNumOperands() - 1;
1282+
MIB->getOperand(Idx).setReg(constrainOperandRegClass(
1283+
MF, *TRI, MRI, *TII, *ST.getRegBankInfo(), *MIB, MIB->getDesc(),
1284+
MIB->getOperand(Idx), Idx));
1285+
}
1286+
};
1287+
1288+
ArgInfo ExecArg = Info.OrigArgs[ChainCallArgIdx::Exec];
12171289
assert(ExecArg.Regs.size() == 1 && "Too many regs for EXEC");
12181290

1219-
if (!ExecArg.Ty->isIntegerTy(ST.getWavefrontSize()))
1291+
if (!ExecArg.Ty->isIntegerTy(ST.getWavefrontSize())) {
1292+
LLVM_DEBUG(dbgs() << "Bad type for EXEC");
12201293
return false;
1221-
1222-
if (const auto *CI = dyn_cast<ConstantInt>(ExecArg.OrigValue)) {
1223-
MIB.addImm(CI->getSExtValue());
1224-
} else {
1225-
MIB.addReg(ExecArg.Regs[0]);
1226-
unsigned Idx = MIB->getNumOperands() - 1;
1227-
MIB->getOperand(Idx).setReg(constrainOperandRegClass(
1228-
MF, *TRI, MRI, *ST.getInstrInfo(), *ST.getRegBankInfo(), *MIB,
1229-
MIB->getDesc(), MIB->getOperand(Idx), Idx));
12301294
}
1295+
1296+
AddRegOrImm(ExecArg);
1297+
if (IsDynamicVGPRChainCall)
1298+
std::for_each(Info.OrigArgs.begin() + ChainCallArgIdx::NumVGPRs,
1299+
Info.OrigArgs.end(), AddRegOrImm);
12311300
}
12321301

12331302
// Tell the call which registers are clobbered.
@@ -1329,9 +1398,9 @@ bool AMDGPUCallLowering::lowerTailCall(
13291398
// FIXME: We should define regbankselectable call instructions to handle
13301399
// divergent call targets.
13311400
if (MIB->getOperand(0).isReg()) {
1332-
MIB->getOperand(0).setReg(constrainOperandRegClass(
1333-
MF, *TRI, MRI, *ST.getInstrInfo(), *ST.getRegBankInfo(), *MIB,
1334-
MIB->getDesc(), MIB->getOperand(0), 0));
1401+
MIB->getOperand(0).setReg(
1402+
constrainOperandRegClass(MF, *TRI, MRI, *TII, *ST.getRegBankInfo(),
1403+
*MIB, MIB->getDesc(), MIB->getOperand(0), 0));
13351404
}
13361405

13371406
MF.getFrameInfo().setHasTailCall();
@@ -1345,11 +1414,6 @@ bool AMDGPUCallLowering::lowerChainCall(MachineIRBuilder &MIRBuilder,
13451414
ArgInfo Callee = Info.OrigArgs[0];
13461415
ArgInfo SGPRArgs = Info.OrigArgs[2];
13471416
ArgInfo VGPRArgs = Info.OrigArgs[3];
1348-
ArgInfo Flags = Info.OrigArgs[4];
1349-
1350-
assert(cast<ConstantInt>(Flags.OrigValue)->isZero() &&
1351-
"Non-zero flags aren't supported yet.");
1352-
assert(Info.OrigArgs.size() == 5 && "Additional args aren't supported yet.");
13531417

13541418
MachineFunction &MF = MIRBuilder.getMF();
13551419
const Function &F = MF.getFunction();

llvm/lib/Target/AMDGPU/SIISelLowering.cpp

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3657,6 +3657,19 @@ bool SITargetLowering::mayBeEmittedAsTailCall(const CallInst *CI) const {
36573657
return true;
36583658
}
36593659

3660+
namespace {
3661+
// Chain calls have special arguments that we need to handle. These are
3662+
// tagging along at the end of the arguments list(s), after the SGPR and VGPR
3663+
// arguments (index 0 and 1 respectively).
3664+
enum ChainCallArgIdx {
3665+
Exec = 2,
3666+
Flags,
3667+
NumVGPRs,
3668+
FallbackExec,
3669+
FallbackCallee
3670+
};
3671+
} // anonymous namespace
3672+
36603673
// The wave scratch offset register is used as the global base pointer.
36613674
SDValue SITargetLowering::LowerCall(CallLoweringInfo &CLI,
36623675
SmallVectorImpl<SDValue> &InVals) const {
@@ -3665,37 +3678,67 @@ SDValue SITargetLowering::LowerCall(CallLoweringInfo &CLI,
36653678

36663679
SelectionDAG &DAG = CLI.DAG;
36673680

3668-
TargetLowering::ArgListEntry RequestedExec;
3669-
if (IsChainCallConv) {
3670-
// The last argument should be the value that we need to put in EXEC.
3671-
// Pop it out of CLI.Outs and CLI.OutVals before we do any processing so we
3672-
// don't treat it like the rest of the arguments.
3673-
RequestedExec = CLI.Args.back();
3674-
assert(RequestedExec.Node && "No node for EXEC");
3681+
const SDLoc &DL = CLI.DL;
3682+
SDValue Chain = CLI.Chain;
3683+
SDValue Callee = CLI.Callee;
36753684

3676-
if (!RequestedExec.Ty->isIntegerTy(Subtarget->getWavefrontSize()))
3685+
llvm::SmallVector<SDValue, 6> ChainCallSpecialArgs;
3686+
if (IsChainCallConv) {
3687+
// The last arguments should be the value that we need to put in EXEC,
3688+
// followed by the flags and any other arguments with special meanings.
3689+
// Pop them out of CLI.Outs and CLI.OutVals before we do any processing so
3690+
// we don't treat them like the "real" arguments.
3691+
auto RequestedExecIt = std::find_if(
3692+
CLI.Outs.begin(), CLI.Outs.end(),
3693+
[](const ISD::OutputArg &Arg) { return Arg.OrigArgIndex == 2; });
3694+
assert(RequestedExecIt != CLI.Outs.end() && "No node for EXEC");
3695+
3696+
size_t SpecialArgsBeginIdx = RequestedExecIt - CLI.Outs.begin();
3697+
CLI.OutVals.erase(CLI.OutVals.begin() + SpecialArgsBeginIdx,
3698+
CLI.OutVals.end());
3699+
CLI.Outs.erase(RequestedExecIt, CLI.Outs.end());
3700+
3701+
assert(CLI.Outs.back().OrigArgIndex < 2 &&
3702+
"Haven't popped all the special args");
3703+
3704+
TargetLowering::ArgListEntry RequestedExecArg =
3705+
CLI.Args[ChainCallArgIdx::Exec];
3706+
if (!RequestedExecArg.Ty->isIntegerTy(Subtarget->getWavefrontSize()))
36773707
return lowerUnhandledCall(CLI, InVals, "Invalid value for EXEC");
36783708

3679-
assert(CLI.Outs.back().OrigArgIndex == 2 && "Unexpected last arg");
3680-
CLI.Outs.pop_back();
3681-
CLI.OutVals.pop_back();
3709+
// Convert constants into TargetConstants, so they become immediate operands
3710+
// instead of being selected into S_MOV.
3711+
auto PushNodeOrTargetConstant = [&](TargetLowering::ArgListEntry Arg) {
3712+
if (auto ArgNode = dyn_cast<ConstantSDNode>(Arg.Node))
3713+
ChainCallSpecialArgs.push_back(DAG.getTargetConstant(
3714+
ArgNode->getAPIntValue(), DL, ArgNode->getValueType(0)));
3715+
else
3716+
ChainCallSpecialArgs.push_back(Arg.Node);
3717+
};
36823718

3683-
if (RequestedExec.Ty->isIntegerTy(64)) {
3684-
assert(CLI.Outs.back().OrigArgIndex == 2 && "Exec wasn't split up");
3685-
CLI.Outs.pop_back();
3686-
CLI.OutVals.pop_back();
3687-
}
3719+
PushNodeOrTargetConstant(RequestedExecArg);
3720+
3721+
// Process any other special arguments depending on the value of the flags.
3722+
TargetLowering::ArgListEntry Flags = CLI.Args[ChainCallArgIdx::Flags];
3723+
3724+
const APInt &FlagsValue = cast<ConstantSDNode>(Flags.Node)->getAPIntValue();
3725+
if (FlagsValue.isZero()) {
3726+
if (CLI.Args.size() > ChainCallArgIdx::Flags + 1)
3727+
return lowerUnhandledCall(CLI, InVals,
3728+
"No additional args allowed if flags == 0");
3729+
} else if (FlagsValue.isOneBitSet(0)) {
3730+
if (CLI.Args.size() != ChainCallArgIdx::FallbackCallee + 1) {
3731+
return lowerUnhandledCall(CLI, InVals, "Expected 3 additional args");
3732+
}
36883733

3689-
assert(CLI.Outs.back().OrigArgIndex != 2 &&
3690-
"Haven't popped all the pieces of the EXEC mask");
3734+
std::for_each(CLI.Args.begin() + ChainCallArgIdx::NumVGPRs,
3735+
CLI.Args.end(), PushNodeOrTargetConstant);
3736+
}
36913737
}
36923738

3693-
const SDLoc &DL = CLI.DL;
36943739
SmallVector<ISD::OutputArg, 32> &Outs = CLI.Outs;
36953740
SmallVector<SDValue, 32> &OutVals = CLI.OutVals;
36963741
SmallVector<ISD::InputArg, 32> &Ins = CLI.Ins;
3697-
SDValue Chain = CLI.Chain;
3698-
SDValue Callee = CLI.Callee;
36993742
bool &IsTailCall = CLI.IsTailCall;
37003743
bool IsVarArg = CLI.IsVarArg;
37013744
bool IsSibCall = false;
@@ -3983,7 +4026,8 @@ SDValue SITargetLowering::LowerCall(CallLoweringInfo &CLI,
39834026
}
39844027

39854028
if (IsChainCallConv)
3986-
Ops.push_back(RequestedExec.Node);
4029+
Ops.insert(Ops.end(), ChainCallSpecialArgs.begin(),
4030+
ChainCallSpecialArgs.end());
39874031

39884032
// Add argument registers to the end of the list so that they are known live
39894033
// into the call.

0 commit comments

Comments
 (0)