xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp (revision 753f127f3ace09432b2baeffd71a308760641a62)
181ad6265SDimitry Andric //===--- SPIRVCallLowering.cpp - Call lowering ------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // This file implements the lowering of LLVM calls to machine code calls for
1081ad6265SDimitry Andric // GlobalISel.
1181ad6265SDimitry Andric //
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric 
1481ad6265SDimitry Andric #include "SPIRVCallLowering.h"
1581ad6265SDimitry Andric #include "MCTargetDesc/SPIRVBaseInfo.h"
1681ad6265SDimitry Andric #include "SPIRV.h"
1781ad6265SDimitry Andric #include "SPIRVGlobalRegistry.h"
1881ad6265SDimitry Andric #include "SPIRVISelLowering.h"
1981ad6265SDimitry Andric #include "SPIRVRegisterInfo.h"
2081ad6265SDimitry Andric #include "SPIRVSubtarget.h"
2181ad6265SDimitry Andric #include "SPIRVUtils.h"
2281ad6265SDimitry Andric #include "llvm/CodeGen/FunctionLoweringInfo.h"
2381ad6265SDimitry Andric 
2481ad6265SDimitry Andric using namespace llvm;
2581ad6265SDimitry Andric 
2681ad6265SDimitry Andric SPIRVCallLowering::SPIRVCallLowering(const SPIRVTargetLowering &TLI,
2781ad6265SDimitry Andric                                      const SPIRVSubtarget &ST,
2881ad6265SDimitry Andric                                      SPIRVGlobalRegistry *GR)
2981ad6265SDimitry Andric     : CallLowering(&TLI), ST(ST), GR(GR) {}
3081ad6265SDimitry Andric 
3181ad6265SDimitry Andric bool SPIRVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
3281ad6265SDimitry Andric                                     const Value *Val, ArrayRef<Register> VRegs,
3381ad6265SDimitry Andric                                     FunctionLoweringInfo &FLI,
3481ad6265SDimitry Andric                                     Register SwiftErrorVReg) const {
3581ad6265SDimitry Andric   // Currently all return types should use a single register.
3681ad6265SDimitry Andric   // TODO: handle the case of multiple registers.
3781ad6265SDimitry Andric   if (VRegs.size() > 1)
3881ad6265SDimitry Andric     return false;
3981ad6265SDimitry Andric   if (Val)
4081ad6265SDimitry Andric     return MIRBuilder.buildInstr(SPIRV::OpReturnValue)
4181ad6265SDimitry Andric         .addUse(VRegs[0])
4281ad6265SDimitry Andric         .constrainAllUses(MIRBuilder.getTII(), *ST.getRegisterInfo(),
4381ad6265SDimitry Andric                           *ST.getRegBankInfo());
4481ad6265SDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpReturn);
4581ad6265SDimitry Andric   return true;
4681ad6265SDimitry Andric }
4781ad6265SDimitry Andric 
4881ad6265SDimitry Andric // Based on the LLVM function attributes, get a SPIR-V FunctionControl.
4981ad6265SDimitry Andric static uint32_t getFunctionControl(const Function &F) {
5081ad6265SDimitry Andric   uint32_t FuncControl = static_cast<uint32_t>(SPIRV::FunctionControl::None);
5181ad6265SDimitry Andric   if (F.hasFnAttribute(Attribute::AttrKind::AlwaysInline)) {
5281ad6265SDimitry Andric     FuncControl |= static_cast<uint32_t>(SPIRV::FunctionControl::Inline);
5381ad6265SDimitry Andric   }
5481ad6265SDimitry Andric   if (F.hasFnAttribute(Attribute::AttrKind::ReadNone)) {
5581ad6265SDimitry Andric     FuncControl |= static_cast<uint32_t>(SPIRV::FunctionControl::Pure);
5681ad6265SDimitry Andric   }
5781ad6265SDimitry Andric   if (F.hasFnAttribute(Attribute::AttrKind::ReadOnly)) {
5881ad6265SDimitry Andric     FuncControl |= static_cast<uint32_t>(SPIRV::FunctionControl::Const);
5981ad6265SDimitry Andric   }
6081ad6265SDimitry Andric   if (F.hasFnAttribute(Attribute::AttrKind::NoInline)) {
6181ad6265SDimitry Andric     FuncControl |= static_cast<uint32_t>(SPIRV::FunctionControl::DontInline);
6281ad6265SDimitry Andric   }
6381ad6265SDimitry Andric   return FuncControl;
6481ad6265SDimitry Andric }
6581ad6265SDimitry Andric 
6681ad6265SDimitry Andric bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
6781ad6265SDimitry Andric                                              const Function &F,
6881ad6265SDimitry Andric                                              ArrayRef<ArrayRef<Register>> VRegs,
6981ad6265SDimitry Andric                                              FunctionLoweringInfo &FLI) const {
7081ad6265SDimitry Andric   assert(GR && "Must initialize the SPIRV type registry before lowering args.");
71*753f127fSDimitry Andric   GR->setCurrentFunc(MIRBuilder.getMF());
7281ad6265SDimitry Andric 
7381ad6265SDimitry Andric   // Assign types and names to all args, and store their types for later.
7481ad6265SDimitry Andric   SmallVector<Register, 4> ArgTypeVRegs;
7581ad6265SDimitry Andric   if (VRegs.size() > 0) {
7681ad6265SDimitry Andric     unsigned i = 0;
7781ad6265SDimitry Andric     for (const auto &Arg : F.args()) {
7881ad6265SDimitry Andric       // Currently formal args should use single registers.
7981ad6265SDimitry Andric       // TODO: handle the case of multiple registers.
8081ad6265SDimitry Andric       if (VRegs[i].size() > 1)
8181ad6265SDimitry Andric         return false;
8281ad6265SDimitry Andric       auto *SpirvTy =
8381ad6265SDimitry Andric           GR->assignTypeToVReg(Arg.getType(), VRegs[i][0], MIRBuilder);
8481ad6265SDimitry Andric       ArgTypeVRegs.push_back(GR->getSPIRVTypeID(SpirvTy));
8581ad6265SDimitry Andric 
8681ad6265SDimitry Andric       if (Arg.hasName())
8781ad6265SDimitry Andric         buildOpName(VRegs[i][0], Arg.getName(), MIRBuilder);
8881ad6265SDimitry Andric       if (Arg.getType()->isPointerTy()) {
8981ad6265SDimitry Andric         auto DerefBytes = static_cast<unsigned>(Arg.getDereferenceableBytes());
9081ad6265SDimitry Andric         if (DerefBytes != 0)
9181ad6265SDimitry Andric           buildOpDecorate(VRegs[i][0], MIRBuilder,
9281ad6265SDimitry Andric                           SPIRV::Decoration::MaxByteOffset, {DerefBytes});
9381ad6265SDimitry Andric       }
9481ad6265SDimitry Andric       if (Arg.hasAttribute(Attribute::Alignment)) {
9581ad6265SDimitry Andric         buildOpDecorate(VRegs[i][0], MIRBuilder, SPIRV::Decoration::Alignment,
9681ad6265SDimitry Andric                         {static_cast<unsigned>(Arg.getParamAlignment())});
9781ad6265SDimitry Andric       }
9881ad6265SDimitry Andric       if (Arg.hasAttribute(Attribute::ReadOnly)) {
9981ad6265SDimitry Andric         auto Attr =
10081ad6265SDimitry Andric             static_cast<unsigned>(SPIRV::FunctionParameterAttribute::NoWrite);
10181ad6265SDimitry Andric         buildOpDecorate(VRegs[i][0], MIRBuilder,
10281ad6265SDimitry Andric                         SPIRV::Decoration::FuncParamAttr, {Attr});
10381ad6265SDimitry Andric       }
10481ad6265SDimitry Andric       if (Arg.hasAttribute(Attribute::ZExt)) {
10581ad6265SDimitry Andric         auto Attr =
10681ad6265SDimitry Andric             static_cast<unsigned>(SPIRV::FunctionParameterAttribute::Zext);
10781ad6265SDimitry Andric         buildOpDecorate(VRegs[i][0], MIRBuilder,
10881ad6265SDimitry Andric                         SPIRV::Decoration::FuncParamAttr, {Attr});
10981ad6265SDimitry Andric       }
11081ad6265SDimitry Andric       ++i;
11181ad6265SDimitry Andric     }
11281ad6265SDimitry Andric   }
11381ad6265SDimitry Andric 
11481ad6265SDimitry Andric   // Generate a SPIR-V type for the function.
11581ad6265SDimitry Andric   auto MRI = MIRBuilder.getMRI();
11681ad6265SDimitry Andric   Register FuncVReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
11781ad6265SDimitry Andric   MRI->setRegClass(FuncVReg, &SPIRV::IDRegClass);
118*753f127fSDimitry Andric   if (F.isDeclaration())
119*753f127fSDimitry Andric     GR->add(&F, &MIRBuilder.getMF(), FuncVReg);
12081ad6265SDimitry Andric 
12181ad6265SDimitry Andric   auto *FTy = F.getFunctionType();
12281ad6265SDimitry Andric   auto FuncTy = GR->assignTypeToVReg(FTy, FuncVReg, MIRBuilder);
12381ad6265SDimitry Andric 
12481ad6265SDimitry Andric   // Build the OpTypeFunction declaring it.
12581ad6265SDimitry Andric   Register ReturnTypeID = FuncTy->getOperand(1).getReg();
12681ad6265SDimitry Andric   uint32_t FuncControl = getFunctionControl(F);
12781ad6265SDimitry Andric 
12881ad6265SDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpFunction)
12981ad6265SDimitry Andric       .addDef(FuncVReg)
13081ad6265SDimitry Andric       .addUse(ReturnTypeID)
13181ad6265SDimitry Andric       .addImm(FuncControl)
13281ad6265SDimitry Andric       .addUse(GR->getSPIRVTypeID(FuncTy));
13381ad6265SDimitry Andric 
13481ad6265SDimitry Andric   // Add OpFunctionParameters.
13581ad6265SDimitry Andric   const unsigned NumArgs = ArgTypeVRegs.size();
13681ad6265SDimitry Andric   for (unsigned i = 0; i < NumArgs; ++i) {
13781ad6265SDimitry Andric     assert(VRegs[i].size() == 1 && "Formal arg has multiple vregs");
13881ad6265SDimitry Andric     MRI->setRegClass(VRegs[i][0], &SPIRV::IDRegClass);
13981ad6265SDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpFunctionParameter)
14081ad6265SDimitry Andric         .addDef(VRegs[i][0])
14181ad6265SDimitry Andric         .addUse(ArgTypeVRegs[i]);
142*753f127fSDimitry Andric     if (F.isDeclaration())
143*753f127fSDimitry Andric       GR->add(F.getArg(i), &MIRBuilder.getMF(), VRegs[i][0]);
14481ad6265SDimitry Andric   }
14581ad6265SDimitry Andric   // Name the function.
14681ad6265SDimitry Andric   if (F.hasName())
14781ad6265SDimitry Andric     buildOpName(FuncVReg, F.getName(), MIRBuilder);
14881ad6265SDimitry Andric 
14981ad6265SDimitry Andric   // Handle entry points and function linkage.
15081ad6265SDimitry Andric   if (F.getCallingConv() == CallingConv::SPIR_KERNEL) {
15181ad6265SDimitry Andric     auto MIB = MIRBuilder.buildInstr(SPIRV::OpEntryPoint)
15281ad6265SDimitry Andric                    .addImm(static_cast<uint32_t>(SPIRV::ExecutionModel::Kernel))
15381ad6265SDimitry Andric                    .addUse(FuncVReg);
15481ad6265SDimitry Andric     addStringImm(F.getName(), MIB);
15581ad6265SDimitry Andric   } else if (F.getLinkage() == GlobalValue::LinkageTypes::ExternalLinkage ||
15681ad6265SDimitry Andric              F.getLinkage() == GlobalValue::LinkOnceODRLinkage) {
15781ad6265SDimitry Andric     auto LnkTy = F.isDeclaration() ? SPIRV::LinkageType::Import
15881ad6265SDimitry Andric                                    : SPIRV::LinkageType::Export;
15981ad6265SDimitry Andric     buildOpDecorate(FuncVReg, MIRBuilder, SPIRV::Decoration::LinkageAttributes,
16081ad6265SDimitry Andric                     {static_cast<uint32_t>(LnkTy)}, F.getGlobalIdentifier());
16181ad6265SDimitry Andric   }
16281ad6265SDimitry Andric 
16381ad6265SDimitry Andric   return true;
16481ad6265SDimitry Andric }
16581ad6265SDimitry Andric 
16681ad6265SDimitry Andric bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
16781ad6265SDimitry Andric                                   CallLoweringInfo &Info) const {
16881ad6265SDimitry Andric   // Currently call returns should have single vregs.
16981ad6265SDimitry Andric   // TODO: handle the case of multiple registers.
17081ad6265SDimitry Andric   if (Info.OrigRet.Regs.size() > 1)
17181ad6265SDimitry Andric     return false;
17281ad6265SDimitry Andric 
173*753f127fSDimitry Andric   GR->setCurrentFunc(MIRBuilder.getMF());
17481ad6265SDimitry Andric   Register ResVReg =
17581ad6265SDimitry Andric       Info.OrigRet.Regs.empty() ? Register(0) : Info.OrigRet.Regs[0];
17681ad6265SDimitry Andric   // Emit a regular OpFunctionCall. If it's an externally declared function,
17781ad6265SDimitry Andric   // be sure to emit its type and function declaration here. It will be
17881ad6265SDimitry Andric   // hoisted globally later.
17981ad6265SDimitry Andric   if (Info.Callee.isGlobal()) {
18081ad6265SDimitry Andric     auto *CF = dyn_cast_or_null<const Function>(Info.Callee.getGlobal());
18181ad6265SDimitry Andric     // TODO: support constexpr casts and indirect calls.
18281ad6265SDimitry Andric     if (CF == nullptr)
18381ad6265SDimitry Andric       return false;
18481ad6265SDimitry Andric     if (CF->isDeclaration()) {
18581ad6265SDimitry Andric       // Emit the type info and forward function declaration to the first MBB
18681ad6265SDimitry Andric       // to ensure VReg definition dependencies are valid across all MBBs.
18781ad6265SDimitry Andric       MachineBasicBlock::iterator OldII = MIRBuilder.getInsertPt();
18881ad6265SDimitry Andric       MachineBasicBlock &OldBB = MIRBuilder.getMBB();
18981ad6265SDimitry Andric       MachineBasicBlock &FirstBB = *MIRBuilder.getMF().getBlockNumbered(0);
19081ad6265SDimitry Andric       MIRBuilder.setInsertPt(FirstBB, FirstBB.instr_end());
19181ad6265SDimitry Andric 
19281ad6265SDimitry Andric       SmallVector<ArrayRef<Register>, 8> VRegArgs;
19381ad6265SDimitry Andric       SmallVector<SmallVector<Register, 1>, 8> ToInsert;
19481ad6265SDimitry Andric       for (const Argument &Arg : CF->args()) {
19581ad6265SDimitry Andric         if (MIRBuilder.getDataLayout().getTypeStoreSize(Arg.getType()).isZero())
19681ad6265SDimitry Andric           continue; // Don't handle zero sized types.
19781ad6265SDimitry Andric         ToInsert.push_back({MIRBuilder.getMRI()->createGenericVirtualRegister(
19881ad6265SDimitry Andric             LLT::scalar(32))});
19981ad6265SDimitry Andric         VRegArgs.push_back(ToInsert.back());
20081ad6265SDimitry Andric       }
20181ad6265SDimitry Andric       // TODO: Reuse FunctionLoweringInfo.
20281ad6265SDimitry Andric       FunctionLoweringInfo FuncInfo;
20381ad6265SDimitry Andric       lowerFormalArguments(MIRBuilder, *CF, VRegArgs, FuncInfo);
20481ad6265SDimitry Andric       MIRBuilder.setInsertPt(OldBB, OldII);
20581ad6265SDimitry Andric     }
20681ad6265SDimitry Andric   }
20781ad6265SDimitry Andric 
20881ad6265SDimitry Andric   // Make sure there's a valid return reg, even for functions returning void.
20981ad6265SDimitry Andric   if (!ResVReg.isValid()) {
21081ad6265SDimitry Andric     ResVReg = MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);
21181ad6265SDimitry Andric   }
21281ad6265SDimitry Andric   SPIRVType *RetType =
21381ad6265SDimitry Andric       GR->assignTypeToVReg(Info.OrigRet.Ty, ResVReg, MIRBuilder);
21481ad6265SDimitry Andric 
21581ad6265SDimitry Andric   // Emit the OpFunctionCall and its args.
21681ad6265SDimitry Andric   auto MIB = MIRBuilder.buildInstr(SPIRV::OpFunctionCall)
21781ad6265SDimitry Andric                  .addDef(ResVReg)
21881ad6265SDimitry Andric                  .addUse(GR->getSPIRVTypeID(RetType))
21981ad6265SDimitry Andric                  .add(Info.Callee);
22081ad6265SDimitry Andric 
22181ad6265SDimitry Andric   for (const auto &Arg : Info.OrigArgs) {
22281ad6265SDimitry Andric     // Currently call args should have single vregs.
22381ad6265SDimitry Andric     if (Arg.Regs.size() > 1)
22481ad6265SDimitry Andric       return false;
22581ad6265SDimitry Andric     MIB.addUse(Arg.Regs[0]);
22681ad6265SDimitry Andric   }
22781ad6265SDimitry Andric   return MIB.constrainAllUses(MIRBuilder.getTII(), *ST.getRegisterInfo(),
22881ad6265SDimitry Andric                               *ST.getRegBankInfo());
22981ad6265SDimitry Andric }
230