[llvm-branch-commits] [llvm] [4/7][PISA] Add PISA GlobalISel lowering, legalization and combiners (PR #214373)
Kacper Doga via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Sep 14 01:17:14 PDT 2026
================
@@ -0,0 +1,758 @@
+//===-- PISACallLowering.cpp - Call lowering ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISACallLowering.h"
+#include "MCTargetDesc/PISABaseInfo.h"
+#include "PISA.h"
+#include "PISAISelLowering.h"
+#include "PISAMachineFunctionInfo.h"
+#include "PISARegisterInfo.h"
+#include "PISASubtarget.h"
+#include "PISAUtils.h"
+#include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Support/ModRef.h"
+
+using namespace llvm;
+
+PISACallLowering::PISACallLowering(const PISATargetLowering &TLI)
+ : CallLowering(&TLI) {}
+
+bool PISACallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
+ const Value *Val, ArrayRef<Register> VRegs,
+ FunctionLoweringInfo &FLI,
+ Register SwiftErrorVReg) const {
+ // FIXME: Currently the return support is only for registers.
+ // Pending:
+ // - return immediates: fold immediates to return operand
+ if (VRegs.size() > 1)
+ return false;
+ if (Val) {
+ auto &DL = MIRBuilder.getDataLayout();
+ const auto &STI = MIRBuilder.getMF().getSubtarget();
+ unsigned Op = 0;
+ auto *Ty = Val->getType();
+ auto VReg = VRegs[0];
+ if (Ty->isVectorTy()) {
+ auto *VTy = cast<FixedVectorType>(Ty);
+ unsigned NumElts = VTy->getNumElements();
+ unsigned EltSize = DL.getTypeSizeInBits(Ty->getScalarType());
+ switch (EltSize) {
+ case 8:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i8_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i8_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i8_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ case 16:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i16_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i16_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i16_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ case 32:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i32_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i32_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i32_r;
+ break;
+ case 5:
+ Op = PISA::retValue_v5i32_r;
+ break;
+ case 6:
+ Op = PISA::retValue_v6i32_r;
+ break;
+ case 7:
+ Op = PISA::retValue_v7i32_r;
+ break;
+ case 8:
+ Op = PISA::retValue_v8i32_r;
+ break;
+ case 16:
+ Op = PISA::retValue_v16i32_r;
+ break;
+ case 32:
+ Op = PISA::retValue_v32i32_r;
+ break;
+ case 64:
+ Op = PISA::retValue_v64i32_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ case 64:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i64_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i64_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i64_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ default:
+ llvm_unreachable("Unknown return size!");
+ break;
+ }
+ } else {
+ unsigned BitSize = DL.getTypeSizeInBits(Ty);
+ switch (BitSize) {
+ case 1: // change i1 to i16 (see lowerCall())
+ {
+ const LLT I16 = LLT::integer(16);
+ auto Dst = MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
+ auto &MF = MIRBuilder.getMF();
+ auto &F = MF.getFunction();
+ const DataLayout &DL = MF.getDataLayout();
+ ArgInfo RetInfo(VReg, *Val, 0);
+ setArgFlags(RetInfo, AttributeList::ReturnIndex, DL, F);
+ auto Sext = llvm::any_of(
+ RetInfo.Flags, [](const auto &Flag) { return Flag.isSExt(); });
+ auto Zext = llvm::any_of(
+ RetInfo.Flags, [](const auto &Flag) { return Flag.isZExt(); });
+ if (Sext) {
+ MIRBuilder.buildSExt(Dst, VReg);
+ } else if (Zext) {
+ MIRBuilder.buildZExt(Dst, VReg);
+ } else {
+ MIRBuilder.buildAnyExt(Dst, VReg);
+ }
+ VReg = Dst;
+ }
+ Op = PISA::retValue_i16_r;
+ break;
+ case 8:
+ Op = PISA::retValue_i8_r;
+ break;
+ case 16:
+ Op = PISA::retValue_i16_r;
+ break;
+ case 32:
+ Op = PISA::retValue_i32_r;
+ break;
+ case 64:
+ Op = PISA::retValue_i64_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return size!");
+ break;
+ }
+ }
+ // Backend-defined opcodes, e.g. retValue* must have a register
+ // class assigned to their 'source' register. During instruction
+ // combine, a preceeding instructions may be combined, with a new
+ // 'dest' register being assigned. Attempt to replace 'source'
+ // with 'dest' will trigger an assertion in canReplaceReg(), since
+ // there is an expectation of no register class being assigned.
+ // Having an extra copy here eliminates the problem; copy itself
+ // will be removed during instruction selection.
+ auto *MRI = MIRBuilder.getMRI();
+ auto Tmp = MRI->createGenericVirtualRegister(MRI->getType(VReg));
+ MIRBuilder.buildCopy(Tmp, VReg);
+ MIRBuilder.buildInstr(Op).addUse(Tmp).constrainAllUses(
+ MIRBuilder.getTII(), *STI.getRegisterInfo(), *STI.getRegBankInfo());
+ return true;
+ }
+ MIRBuilder.buildInstr(PISA::ret);
+ return true;
+}
+
+bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
+ const Function &F,
+ ArrayRef<ArrayRef<Register>> VRegs,
+ FunctionLoweringInfo &FLI) const {
+ auto *MRI = MIRBuilder.getMRI();
+ auto &MF = MIRBuilder.getMF();
+ auto &Ctx = F.getContext();
+ auto *MFInfo = MF.getInfo<PISAMachineFunctionInfo>();
+ auto &DL = F.getParent()->getDataLayout();
+ bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
+ for (const auto [i, Arg] : llvm::enumerate(F.args())) {
+ assert(VRegs[i].size() == 1 && "Formal arg has multiple vregs");
+
+ ArgInfo OrigArg{VRegs[i], Arg, static_cast<unsigned>(i)};
+ setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, F);
+ auto *ArgType = OrigArg.OrigValue->getType();
+ const bool IsByRef = ArgType->isPointerTy() && OrigArg.Flags[0].isByRef();
+ const unsigned ArgSize = IsByRef
+ ? OrigArg.Flags[0].getByRefSize()
+ : MRI->getType(VRegs[i][0]).getSizeInBytes();
+ MFInfo->setArgInfo(i, ArgSize, IsByRef);
+
+ if (IsKernel && Arg.use_empty())
+ continue;
+
+ unsigned Op = 0;
+ if (IsByRef) {
+ assert(IsKernel && "'byref' is only used in kernel!");
+ Op = PISA::G_PISA_PARAM_SLOT;
+ loadParamWithOpcode(MIRBuilder, F, VRegs[i][0], ArgType, Op,
+ Arg.getArgNo(), 0);
+ } else if (IsKernel && ArgType->isVectorTy()) {
+ auto *VectorTy = cast<FixedVectorType>(ArgType);
+ auto NumElts = VectorTy->getNumElements();
+ auto *EltTy = VectorTy->getElementType();
+ auto EltSize = DL.getTypeSizeInBits(EltTy);
+ auto Split = (NumElts > 4) || ((NumElts == 3) && (EltSize != 32)) ||
+ ((NumElts == 4) && (EltSize == 64)) || (NumElts == 1);
+ if (Split) {
+ // handle odd-sized and large kernel args, e.g.
+ // <3 x i8> <16 x i16>
+ // loadParam_8b @[arg+0] loadParam_v4_32b @[arg+0]
+ // loadParam_8b @[arg+1] loadParam_v4_32b @[arg+16]
+ // loadParam_8b @[arg+2] buildVector(<8 x i32>)
+ // buildVector(<3 x i8>) bitcast(<16 x i16)
+ auto TargetReg = VRegs[i][0];
+
+ const auto *EltRegClass =
+ (EltSize == 8
+ ? &PISA::Reg8bRegClass
+ : (EltSize == 16 ? &PISA::Reg16bRegClass
+ : (EltSize == 32 ? &PISA::Reg32bRegClass
+ : &PISA::Reg64bRegClass)));
+ auto TotalSize = NumElts * EltSize;
+ auto EltLLT = LLT::integer(EltSize);
+ auto I32 = LLT::integer(32);
+ if (NumElts <= 4) {
+ // do not group
+ } else if (TotalSize % 128 == 0) { // 4 x i32
+ EltTy = FixedVectorType::get(Type::getInt32Ty(Ctx), 4);
+ EltRegClass = &PISA::RegV4_32bRegClass;
+ EltLLT = LLT::vector(ElementCount::getFixed(4), I32);
+ TargetReg = MRI->createGenericVirtualRegister(
+ LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ NumElts = TotalSize / 128;
+ } else if (TotalSize % 64 == 0) { // 2 x i32
+ EltTy = FixedVectorType::get(Type::getInt32Ty(Ctx), 2);
+ EltRegClass = &PISA::RegV2_32bRegClass;
+ EltLLT = LLT::vector(ElementCount::getFixed(2), I32);
+ TargetReg = MRI->createGenericVirtualRegister(
+ LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ NumElts = TotalSize / 64;
+ } else if (TotalSize % 32 == 0) { // 1 x i32
+ EltTy = Type::getInt32Ty(Ctx);
+ EltRegClass = &PISA::Reg32bRegClass;
+ EltLLT = LLT::integer(32);
+ TargetReg = MRI->createGenericVirtualRegister(
+ LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ NumElts = TotalSize / 32;
+ }
----------------
varev-dev wrote:
```suggestion
} else if (TotalSize % 32 == 0) { // 1 x i32
EltTy = Type::getInt32Ty(Ctx);
EltRegClass = &PISA::Reg32bRegClass;
EltLLT = LLT::integer(32);
TargetReg = MRI->createGenericVirtualRegister(
LLT::scalarOrVector(ElementCount::getFixed(TotalSize / 32), I32));
NumElts = TotalSize / 32;
}
```
https://github.com/llvm/llvm-project/pull/214373
More information about the llvm-branch-commits
mailing list