Skip to content

Commit 56396b2

Browse files
committed
[SPIRV-V] Add SPIR-V logical triple to llc
This commits adds the minimal required bits to build a logical SPIR-V compute shader using LLC. - Skip OpenCL-only capabilities & extensions for Logical SPIR-V. - Generate required metadata for entrypoints from HLSL frontend. - Fix execution mode to GLCompute in logical. The main issue is the lack of "vulkan" bit in the triple. This might need to be added as a vendor? Because as-is, SPIRV32/64 assumes OpenCL, and then, SPIRV assumes Vulkan. This is ok-ish today, but not correct. Differential Revision: https://reviews.llvm.org/D156424
1 parent 38eb55a commit 56396b2

13 files changed

+181
-19
lines changed

llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVMCTargetDesc.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ static MCInstrAnalysis *createSPIRVInstrAnalysis(const MCInstrInfo *Info) {
8888
}
8989

9090
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTargetMC() {
91-
for (Target *T : {&getTheSPIRV32Target(), &getTheSPIRV64Target()}) {
91+
for (Target *T : {&getTheSPIRV32Target(), &getTheSPIRV64Target(),
92+
&getTheSPIRVLogicalTarget()}) {
9293
RegisterMCAsmInfo<SPIRVMCAsmInfo> X(*T);
9394
TargetRegistry::RegisterMCInstrInfo(*T, createSPIRVMCInstrInfo);
9495
TargetRegistry::RegisterMCRegInfo(*T, createSPIRVMCRegisterInfo);

llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class SPIRVAsmPrinter : public AsmPrinter {
6666
void outputExtFuncDecls();
6767
void outputExecutionModeFromMDNode(Register Reg, MDNode *Node,
6868
SPIRV::ExecutionMode::ExecutionMode EM);
69+
void outputExecutionModeFromNumthreadsAttribute(
70+
const Register &Reg, const Attribute &Attr,
71+
SPIRV::ExecutionMode::ExecutionMode EM);
6972
void outputExecutionMode(const Module &M);
7073
void outputAnnotations(const Module &M);
7174
void outputModuleSections();
@@ -412,6 +415,29 @@ void SPIRVAsmPrinter::outputExecutionModeFromMDNode(
412415
outputMCInst(Inst);
413416
}
414417

418+
void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute(
419+
const Register &Reg, const Attribute &Attr,
420+
SPIRV::ExecutionMode::ExecutionMode EM) {
421+
assert(Attr.isValid() && "Function called with an invalid attribute.");
422+
423+
MCInst Inst;
424+
Inst.setOpcode(SPIRV::OpExecutionMode);
425+
Inst.addOperand(MCOperand::createReg(Reg));
426+
Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
427+
428+
SmallVector<StringRef> NumThreads;
429+
Attr.getValueAsString().split(NumThreads, ',');
430+
assert(NumThreads.size() == 3 && "invalid numthreads");
431+
for (uint32_t i = 0; i < 3; ++i) {
432+
uint32_t V;
433+
[[maybe_unused]] bool Result = NumThreads[i].getAsInteger(10, V);
434+
assert(!Result && "Failed to parse numthreads");
435+
Inst.addOperand(MCOperand::createImm(V));
436+
}
437+
438+
outputMCInst(Inst);
439+
}
440+
415441
void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
416442
NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode");
417443
if (Node) {
@@ -431,6 +457,9 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
431457
if (MDNode *Node = F.getMetadata("reqd_work_group_size"))
432458
outputExecutionModeFromMDNode(FReg, Node,
433459
SPIRV::ExecutionMode::LocalSize);
460+
if (Attribute Attr = F.getFnAttribute("hlsl.numthreads"); Attr.isValid())
461+
outputExecutionModeFromNumthreadsAttribute(
462+
FReg, Attr, SPIRV::ExecutionMode::LocalSize);
434463
if (MDNode *Node = F.getMetadata("work_group_size_hint"))
435464
outputExecutionModeFromMDNode(FReg, Node,
436465
SPIRV::ExecutionMode::LocalSizeHint);
@@ -447,7 +476,7 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
447476
Inst.addOperand(MCOperand::createImm(TypeCode));
448477
outputMCInst(Inst);
449478
}
450-
if (!M.getNamedMetadata("spirv.ExecutionMode") &&
479+
if (ST->isOpenCLEnv() && !M.getNamedMetadata("spirv.ExecutionMode") &&
451480
!M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
452481
MCInst Inst;
453482
Inst.setOpcode(SPIRV::OpExecutionMode);
@@ -542,4 +571,5 @@ bool SPIRVAsmPrinter::doInitialization(Module &M) {
542571
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVAsmPrinter() {
543572
RegisterAsmPrinter<SPIRVAsmPrinter> X(getTheSPIRV32Target());
544573
RegisterAsmPrinter<SPIRVAsmPrinter> Y(getTheSPIRV64Target());
574+
RegisterAsmPrinter<SPIRVAsmPrinter> Z(getTheSPIRVLogicalTarget());
545575
}

llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,38 @@ static Type *getArgType(const Function &F, unsigned ArgIdx) {
213213
: StructType::create(F.getContext(), KernelArgTypeStr);
214214
}
215215

216+
static bool isEntryPoint(const Function &F) {
217+
// OpenCL handling: any function with the SPIR_KERNEL
218+
// calling convention will be a potential entry point.
219+
if (F.getCallingConv() == CallingConv::SPIR_KERNEL)
220+
return true;
221+
222+
// HLSL handling: special attribute are emitted from the
223+
// front-end.
224+
if (F.getFnAttribute("hlsl.shader").isValid())
225+
return true;
226+
227+
return false;
228+
}
229+
230+
static SPIRV::ExecutionModel::ExecutionModel
231+
getExecutionModel(const SPIRVSubtarget &STI, const Function &F) {
232+
if (STI.isOpenCLEnv())
233+
return SPIRV::ExecutionModel::Kernel;
234+
235+
auto attribute = F.getFnAttribute("hlsl.shader");
236+
if (!attribute.isValid()) {
237+
report_fatal_error(
238+
"This entry point lacks mandatory hlsl.shader attribute.");
239+
}
240+
241+
const auto value = attribute.getValueAsString();
242+
if (value == "compute")
243+
return SPIRV::ExecutionModel::GLCompute;
244+
245+
report_fatal_error("This HLSL entry point is not supported by this backend.");
246+
}
247+
216248
bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
217249
const Function &F,
218250
ArrayRef<ArrayRef<Register>> VRegs,
@@ -336,9 +368,11 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
336368
buildOpName(FuncVReg, F.getName(), MIRBuilder);
337369

338370
// Handle entry points and function linkage.
339-
if (F.getCallingConv() == CallingConv::SPIR_KERNEL) {
371+
if (isEntryPoint(F)) {
372+
const auto &STI = MIRBuilder.getMF().getSubtarget<SPIRVSubtarget>();
373+
auto executionModel = getExecutionModel(STI, F);
340374
auto MIB = MIRBuilder.buildInstr(SPIRV::OpEntryPoint)
341-
.addImm(static_cast<uint32_t>(SPIRV::ExecutionModel::Kernel))
375+
.addImm(static_cast<uint32_t>(executionModel))
342376
.addUse(FuncVReg);
343377
addStringImm(F.getName(), MIB);
344378
} else if (F.getLinkage() == GlobalValue::LinkageTypes::ExternalLinkage ||

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,18 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
106106
MAI.Mem =
107107
static_cast<SPIRV::MemoryModel::MemoryModel>(getMetadataUInt(MemMD, 1));
108108
} else {
109-
MAI.Mem = SPIRV::MemoryModel::OpenCL;
110-
unsigned PtrSize = ST->getPointerSize();
111-
MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32
112-
: PtrSize == 64 ? SPIRV::AddressingModel::Physical64
113-
: SPIRV::AddressingModel::Logical;
109+
// TODO: Add support for VulkanMemoryModel.
110+
MAI.Mem = ST->isOpenCLEnv() ? SPIRV::MemoryModel::OpenCL
111+
: SPIRV::MemoryModel::GLSL450;
112+
if (MAI.Mem == SPIRV::MemoryModel::OpenCL) {
113+
unsigned PtrSize = ST->getPointerSize();
114+
MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32
115+
: PtrSize == 64 ? SPIRV::AddressingModel::Physical64
116+
: SPIRV::AddressingModel::Logical;
117+
} else {
118+
// TODO: Add support for PhysicalStorageBufferAddress.
119+
MAI.Addr = SPIRV::AddressingModel::Logical;
120+
}
114121
}
115122
// Get the OpenCL version number from metadata.
116123
// TODO: support other source languages.
@@ -148,9 +155,12 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
148155
MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
149156
MAI.Addr, *ST);
150157

151-
// TODO: check if it's required by default.
152-
MAI.ExtInstSetMap[static_cast<unsigned>(SPIRV::InstructionSet::OpenCL_std)] =
153-
Register::index2VirtReg(MAI.getNextID());
158+
if (ST->isOpenCLEnv()) {
159+
// TODO: check if it's required by default.
160+
MAI.ExtInstSetMap[static_cast<unsigned>(
161+
SPIRV::InstructionSet::OpenCL_std)] =
162+
Register::index2VirtReg(MAI.getNextID());
163+
}
154164
}
155165

156166
// Collect MI which defines the register in the given machine function.
@@ -516,9 +526,21 @@ void SPIRV::RequirementHandler::addAvailableCaps(const CapabilityList &ToAdd) {
516526
namespace llvm {
517527
namespace SPIRV {
518528
void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) {
519-
// TODO: Implemented for other targets other then OpenCL.
520-
if (!ST.isOpenCLEnv())
529+
if (ST.isOpenCLEnv()) {
530+
initAvailableCapabilitiesForOpenCL(ST);
531+
return;
532+
}
533+
534+
if (ST.isVulkanEnv()) {
535+
initAvailableCapabilitiesForVulkan(ST);
521536
return;
537+
}
538+
539+
report_fatal_error("Unimplemented environment for SPIR-V generation.");
540+
}
541+
542+
void RequirementHandler::initAvailableCapabilitiesForOpenCL(
543+
const SPIRVSubtarget &ST) {
522544
// Add the min requirements for different OpenCL and SPIR-V versions.
523545
addAvailableCaps({Capability::Addresses, Capability::Float16Buffer,
524546
Capability::Int16, Capability::Int8, Capability::Kernel,
@@ -558,6 +580,12 @@ void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) {
558580

559581
// TODO: add OpenCL extensions.
560582
}
583+
584+
void RequirementHandler::initAvailableCapabilitiesForVulkan(
585+
const SPIRVSubtarget &ST) {
586+
addAvailableCaps({Capability::Shader, Capability::Linkage});
587+
}
588+
561589
} // namespace SPIRV
562590
} // namespace llvm
563591

@@ -890,6 +918,11 @@ static void collectReqs(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
890918
MAI.Reqs.getAndAddRequirements(
891919
SPIRV::OperandCategory::ExecutionModeOperand,
892920
SPIRV::ExecutionMode::LocalSize, ST);
921+
if (F.getFnAttribute("hlsl.numthreads").isValid()) {
922+
MAI.Reqs.getAndAddRequirements(
923+
SPIRV::OperandCategory::ExecutionModeOperand,
924+
SPIRV::ExecutionMode::LocalSize, ST);
925+
}
893926
if (F.getMetadata("work_group_size_hint"))
894927
MAI.Reqs.getAndAddRequirements(
895928
SPIRV::OperandCategory::ExecutionModeOperand,

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ struct RequirementHandler {
6969
// recursing through their implicitly declared capabilities too.
7070
void pruneCapabilities(const CapabilityList &ToPrune);
7171

72+
void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST);
73+
void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST);
74+
7275
public:
7376
RequirementHandler() : MinVersion(0), MaxVersion(0) {}
7477
void clear() {

llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@ static unsigned computePointerSize(const Triple &TT) {
3636
const auto Arch = TT.getArch();
3737
// TODO: unify this with pointers legalization.
3838
assert(TT.isSPIRV());
39-
return Arch == Triple::spirv32 ? 32 : 64;
39+
40+
if (Arch == Triple::spirv64)
41+
return 64;
42+
43+
// TODO: this probably needs to be revisited:
44+
// AFAIU Logical SPIR-V has no pointer size. So falling-back on ID size.
45+
// Addressing mode can change how some pointers are handled
46+
// (PhysicalStorageBuffer64).
47+
return 32;
4048
}
4149

4250
SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU,
@@ -45,7 +53,7 @@ SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU,
4553
: SPIRVGenSubtargetInfo(TT, CPU, /*TuneCPU=*/CPU, FS),
4654
PointerSize(computePointerSize(TT)), SPIRVVersion(0), OpenCLVersion(0),
4755
InstrInfo(), FrameLowering(initSubtargetDependencies(CPU, FS)),
48-
TLInfo(TM, *this) {
56+
TLInfo(TM, *this), TargetTriple(TT) {
4957
// The order of initialization is important.
5058
initAvailableExtensions();
5159
initAvailableExtInstSets();
@@ -82,6 +90,8 @@ bool SPIRVSubtarget::isAtLeastSPIRVVer(uint32_t VerToCompareTo) const {
8290
}
8391

8492
bool SPIRVSubtarget::isAtLeastOpenCLVer(uint32_t VerToCompareTo) const {
93+
if (!isOpenCLEnv())
94+
return false;
8595
return isAtLeastVer(OpenCLVersion, VerToCompareTo);
8696
}
8797

llvm/lib/Target/SPIRV/SPIRVSubtarget.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/CodeGen/TargetSubtargetInfo.h"
2626
#include "llvm/IR/DataLayout.h"
2727
#include "llvm/Target/TargetMachine.h"
28+
#include "llvm/TargetParser/Triple.h"
2829

2930
#define GET_SUBTARGETINFO_HEADER
3031
#include "SPIRVGenSubtargetInfo.inc"
@@ -46,6 +47,7 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
4647
SPIRVInstrInfo InstrInfo;
4748
SPIRVFrameLowering FrameLowering;
4849
SPIRVTargetLowering TLInfo;
50+
Triple TargetTriple;
4951

5052
// GlobalISel related APIs.
5153
std::unique_ptr<CallLowering> CallLoweringInfo;
@@ -71,9 +73,13 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
7173
unsigned getPointerSize() const { return PointerSize; }
7274
bool canDirectlyComparePointers() const;
7375
// TODO: this environment is not implemented in Triple, we need to decide
74-
// how to standartize its support. For now, let's assume that we always
75-
// operate with OpenCL.
76-
bool isOpenCLEnv() const { return true; }
76+
// how to standardize its support. For now, let's assume SPIR-V with physical
77+
// addressing is OpenCL, and Logical addressing is Vulkan.
78+
bool isOpenCLEnv() const {
79+
return TargetTriple.getArch() == Triple::spirv32 ||
80+
TargetTriple.getArch() == Triple::spirv64;
81+
}
82+
bool isVulkanEnv() const { return TargetTriple.getArch() == Triple::spirv; }
7783
uint32_t getSPIRVVersion() const { return SPIRVVersion; };
7884
bool isAtLeastSPIRVVer(uint32_t VerToCompareTo) const;
7985
bool isAtLeastOpenCLVer(uint32_t VerToCompareTo) const;

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
3737
// Register the target.
3838
RegisterTargetMachine<SPIRVTargetMachine> X(getTheSPIRV32Target());
3939
RegisterTargetMachine<SPIRVTargetMachine> Y(getTheSPIRV64Target());
40+
RegisterTargetMachine<SPIRVTargetMachine> Z(getTheSPIRVLogicalTarget());
4041

4142
PassRegistry &PR = *PassRegistry::getPassRegistry();
4243
initializeGlobalISel(PR);
@@ -45,6 +46,11 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
4546

4647
static std::string computeDataLayout(const Triple &TT) {
4748
const auto Arch = TT.getArch();
49+
// TODO: this probably needs to be revisited:
50+
// Logical SPIR-V has no pointer size, so any fixed pointer size would be
51+
// wrong. The choice to default to 32 or 64 is just motivated by another
52+
// memory model used for graphics: PhysicalStorageBuffer64. But it shouldn't
53+
// mean anything.
4854
if (Arch == Triple::spirv32)
4955
return "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-"
5056
"v96:128-v192:256-v256:256-v512:512-v1024:1024";

llvm/lib/Target/SPIRV/TargetInfo/SPIRVTargetInfo.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ Target &llvm::getTheSPIRV64Target() {
1919
static Target TheSPIRV64Target;
2020
return TheSPIRV64Target;
2121
}
22+
Target &llvm::getTheSPIRVLogicalTarget() {
23+
static Target TheSPIRVLogicalTarget;
24+
return TheSPIRVLogicalTarget;
25+
}
2226

2327
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTargetInfo() {
2428
RegisterTarget<Triple::spirv32> X(getTheSPIRV32Target(), "spirv32",
2529
"SPIR-V 32-bit", "SPIRV");
2630
RegisterTarget<Triple::spirv64> Y(getTheSPIRV64Target(), "spirv64",
2731
"SPIR-V 64-bit", "SPIRV");
32+
RegisterTarget<Triple::spirv> Z(getTheSPIRVLogicalTarget(), "spirv",
33+
"SPIR-V Logical", "SPIRV");
2834
}

llvm/lib/Target/SPIRV/TargetInfo/SPIRVTargetInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Target;
1515

1616
Target &getTheSPIRV32Target();
1717
Target &getTheSPIRV64Target();
18+
Target &getTheSPIRVLogicalTarget();
1819

1920
} // namespace llvm
2021

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
2+
3+
; CHECK-DAG: OpEntryPoint GLCompute %[[#entry:]] "main"
4+
; CHECK-DAG: OpExecutionMode %[[#entry]] LocalSize 4 8 16
5+
6+
define void @main() #1 {
7+
entry:
8+
ret void
9+
}
10+
11+
attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
2+
3+
; CHECK-DAG: OpCapability Shader
4+
;; Ensure no other capability is listed.
5+
; CHECK-NOT: OpCapability
6+
7+
define void @main() #1 {
8+
entry:
9+
ret void
10+
}
11+
12+
attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
2+
3+
;; Ensure the required Capabilities are listed.
4+
; CHECK-DAG: OpCapability Shader
5+
; CHECK-DAG: OpCapability Linkage
6+
7+
;; Ensure one, and only one, OpMemoryModel is defined.
8+
; CHECK: OpMemoryModel Logical GLSL450
9+
; CHECK-NOT: OpMemoryModel

0 commit comments

Comments
 (0)