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