[llvm] [AMDGPU][GlobalISel] Support lowering preloaded kernel arguments (PR #205049)
Manuel Carrasco via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 04:22:49 PDT 2026
================
@@ -296,6 +297,203 @@ struct AMDGPUOutgoingArgHandler : public AMDGPUOutgoingValueHandler {
assignValueToAddress(ValVReg, Addr, MemTy, MPO, VA);
}
};
+
+// Return the virtual register and type for a preloaded physical SGPR live-in.
+static std::pair<Register, LLT> getPreloadedLiveIn(MachineIRBuilder &B,
+ MCRegister PhysReg,
+ const SIRegisterInfo &TRI) {
+ MachineFunction &MF = B.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const TargetRegisterClass *RC = TRI.getPhysRegBaseClass(PhysReg);
+ assert(RC && "expected a register class for preload SGPR");
+ LLT RegTy = LLT::scalar(TRI.getRegSizeInBits(*RC).getFixedValue());
+ return {getFunctionLiveInPhysReg(MF, *TII, PhysReg, *RC, B.getDL(), RegTy),
+ RegTy};
+}
+
+// Convert raw SGPR preload bits to the type expected by the formal argument.
+static Register adjustPreloadedArgType(MachineIRBuilder &B, Register Src,
+ LLT SrcTy, LLT DstTy) {
+ if (SrcTy == DstTy)
+ return Src;
+
+ LLT IntDstTy = (DstTy.isPointer() || DstTy.isVector())
+ ? LLT::scalar(DstTy.getSizeInBits())
+ : DstTy;
+ if (SrcTy.getSizeInBits() != IntDstTy.getSizeInBits())
+ Src = B.buildAnyExtOrTrunc(IntDstTy, Src).getReg(0);
+ else if (SrcTy != IntDstTy)
+ Src = B.buildBitcast(IntDstTy, Src).getReg(0);
+
+ if (DstTy.isPointer())
+ return B.buildIntToPtr(DstTy, Src).getReg(0);
+
+ if (DstTy.isVector())
+ return B.buildBitcast(DstTy, Src).getReg(0);
+
+ return Src;
+}
+
+// Materialize a preloaded kernarg from its assigned SGPRs into DstReg.
+static void lowerPreloadedKernArg(MachineIRBuilder &B, Register DstReg,
+ LLT DstTy, uint64_t Offset, Align Alignment,
+ ArrayRef<MCRegister> PreloadRegs,
+ const SIRegisterInfo &TRI) {
+ assert(!PreloadRegs.empty());
+
+ // Kernarg preloads are assigned in 32-bit SGPR units.
+ const LLT S32 = LLT::scalar(32);
+
+ Register Value;
+ LLT ValueTy;
+ const bool IsPackedSubDword = DstTy.getSizeInBits() < 32 && Alignment < 4;
+
+ if (PreloadRegs.size() == 1) {
+ auto [LiveIn, LiveInTy] = getPreloadedLiveIn(B, PreloadRegs[0], TRI);
+ if (IsPackedSubDword) {
+ // Extract sub-dword preloads from their containing 32-bit SGPR word.
+ Register Raw = B.buildCopy(S32, LiveIn).getReg(0);
+ uint64_t OffsetDiff = Offset - alignDown(Offset, 4);
+ Register ShiftAmt = B.buildConstant(S32, OffsetDiff * 8).getReg(0);
+ Value = B.buildLShr(S32, Raw, ShiftAmt).getReg(0);
+ ValueTy = S32;
+ } else {
+ // A single preload entry may be a 32-bit SGPR or a wider SGPR tuple.
+ ValueTy = LiveInTy;
+ Value = B.buildCopy(ValueTy, LiveIn).getReg(0);
+ }
+ } else {
+ assert(!IsPackedSubDword && "packed sub-dword preload should use one SGPR");
+ // Reconstruct wider preloads from separate 32-bit SGPR entries.
+ SmallVector<Register, 4> Regs;
+ Regs.reserve(PreloadRegs.size());
+ for (MCRegister Reg : PreloadRegs) {
+ Register LiveIn = getPreloadedLiveIn(B, Reg, TRI).first;
+ Regs.push_back(B.buildCopy(S32, LiveIn).getReg(0));
+ }
+
+ ValueTy = LLT::scalar(PreloadRegs.size() * 32);
+ Value = B.buildMergeLikeInstr(ValueTy, Regs).getReg(0);
+ }
+
+ Register Adjusted = adjustPreloadedArgType(B, Value, ValueTy, DstTy);
+ B.buildCopy(DstReg, Adjusted);
+}
+
+// CCValAssign records an MVT, so round odd-sized split argument parts to the
+// simple memory type used for kernarg layout.
+static MVT getLocVTForSplitArg(const TargetLowering &TLI, const DataLayout &DL,
+ Type *Ty, LLVMContext &Ctx) {
+ EVT LocVT = TLI.getValueType(DL, Ty, true);
+
+ if (LocVT.isVector() && LocVT.getVectorNumElements() == 1)
+ LocVT = LocVT.getScalarType();
+
+ if (LocVT.isVector() && !LocVT.isPow2VectorType())
+ LocVT = LocVT.getPow2VectorType(Ctx);
+ else if (!LocVT.isSimple() && !LocVT.isVector())
+ LocVT = LocVT.getRoundIntegerType(Ctx);
+
+ assert(LocVT.isSimple());
+ return LocVT.getSimpleVT();
+}
+
+// Assign SGPRs for the contiguous sequence of kernel argument parts marked for
+// kernarg preload.
+static void allocatePreloadKernArgSGPRs(
+ CCState &CCInfo, ArrayRef<CallLowering::ArgInfo> SplitArgs,
+ ArrayRef<CCValAssign> ArgLocs, MachineFunction &MF,
+ const GCNSubtarget &Subtarget, const SIRegisterInfo &TRI,
+ SIMachineFunctionInfo &Info) {
+ assert(SplitArgs.size() == ArgLocs.size());
+
+ const Function &F = MF.getFunction();
+ const DataLayout &DL = F.getDataLayout();
+ unsigned LastExplicitArgOffset = Subtarget.getExplicitKernelArgOffset();
+ GCNUserSGPRUsageInfo &SGPRInfo = Info.getUserSGPRInfo();
+ bool InPreloadSequence = true;
+ unsigned InIdx = 0;
+ bool AlignedForImplicitArgs = false;
+ unsigned ImplicitArgOffset = 0;
+
+ for (const Argument &Arg : F.args()) {
+ // Preload assignment follows the original argument order. Each original
+ // argument may already have been split into one or more lowered parts.
+ const bool IsByRef = Arg.hasByRefAttr();
+ Type *ArgTy = IsByRef ? Arg.getParamByRefType() : Arg.getType();
+ if (DL.getTypeAllocSize(ArgTy) == 0)
+ continue;
+
+ // Hardware preloads a contiguous prefix of the kernarg segment. Once a
+ // non-preloaded argument is reached, no later argument can be preloaded.
+ if (!InPreloadSequence || !Arg.hasInRegAttr())
+ break;
+
+ unsigned ArgIdx = Arg.getArgNo();
+ if (InIdx < SplitArgs.size() && SplitArgs[InIdx].OrigArgIndex != ArgIdx)
----------------
mgcarrasco wrote:
Sorry, just in case, I meant that there was no test. I didn't mean to say that the line was not required.
https://github.com/llvm/llvm-project/pull/205049
More information about the llvm-commits
mailing list