Skip to content

Commit 3817ea6

Browse files
committed
[Dcompute] Initial support for Vulkan
1 parent 46bbe8b commit 3817ea6

File tree

6 files changed

+206
-1
lines changed

6 files changed

+206
-1
lines changed

driver/dcomputecodegenerator.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ DComputeCodeGenManager::~DComputeCodeGenManager() {}
2828

2929
DComputeTarget *
3030
DComputeCodeGenManager::createComputeTarget(const std::string &s) {
31+
if (s.substr(0, 6) == "vulkan") {
32+
#if LDC_LLVM_SUPPORTED_TARGET_SPIRV
33+
//TODO version this for vulkan 1.3/1.4
34+
return createVulkanTarget(ctx, 0);
35+
#else
36+
error(Loc(), "LDC was not built with Vulkan DCompute support.");
37+
#endif
38+
}
3139
if (s.substr(0, 4) == "ocl-") {
3240
#if LDC_LLVM_SUPPORTED_TARGET_SPIRV
3341
#define OCL_VALID_VER_INIT 100, 110, 120, 200, 210, 220

gen/abi/spirv.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,12 @@ struct SPIRVTargetABI : TargetABI {
5353
};
5454

5555
TargetABI *createSPIRVABI() { return new SPIRVTargetABI(); }
56+
57+
struct SPIRVVulkanTargetABI : SPIRVTargetABI {
58+
59+
llvm::CallingConv::ID callingConv(FuncDeclaration *fdecl) override {
60+
// The synthesised wrapper is SPIR_KERNEL
61+
return llvm::CallingConv::SPIR_FUNC;
62+
}
63+
};
64+
TargetABI *createSPIRVVulkanABI() { return new SPIRVVulkanTargetABI(); }

gen/abi/targets.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ TargetABI *getRISCV64TargetABI();
3131

3232
TargetABI *createSPIRVABI();
3333

34+
TargetABI *createSPIRVVulkanABI();
35+
3436
TargetABI *getWin64TargetABI();
3537

3638
TargetABI *getX86_64TargetABI();

gen/dcompute/target.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class DComputeTarget {
2727
public:
2828
llvm::LLVMContext &ctx;
2929
int tversion; // OpenCL or CUDA CC version:major*100 + minor*10
30-
enum class ID { Host = 0, OpenCL = 1, CUDA = 2 };
30+
enum class ID { Host = 0, OpenCL = 1, CUDA = 2, Vulkan = 3 };
3131
ID target; // ID for codegen time conditional compilation.
3232
const char *short_name;
3333
const char *binSuffix;
@@ -60,4 +60,5 @@ DComputeTarget *createCUDATarget(llvm::LLVMContext &c, int sm);
6060

6161
#if LDC_LLVM_SUPPORTED_TARGET_SPIRV
6262
DComputeTarget *createOCLTarget(llvm::LLVMContext &c, int oclver);
63+
DComputeTarget *createVulkanTarget(llvm::LLVMContext &c, int ver);
6364
#endif

gen/dcompute/targetVulkan.cpp

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
//===-- gen/dcomputetargetOCL.cpp -----------------------------------------===//
2+
//
3+
// LDC – the LLVM D compiler
4+
//
5+
// Parts of this file are adapted from CodeGenFunction.cpp (Clang, LLVM).
6+
// Therefore, this file is distributed under the LLVM license.
7+
// See the LICENSE file for details.
8+
//===----------------------------------------------------------------------===//
9+
10+
#if LDC_LLVM_SUPPORTED_TARGET_SPIRV
11+
12+
#include "dmd/id.h"
13+
#include "dmd/identifier.h"
14+
#include "dmd/template.h"
15+
#include "dmd/mangle.h"
16+
#include "dmd/module.h"
17+
#include "gen/abi/targets.h"
18+
#include "gen/dcompute/target.h"
19+
#include "gen/dcompute/druntime.h"
20+
#include "gen/logger.h"
21+
#include "gen/optimizer.h"
22+
#include "driver/targetmachine.h"
23+
#include "llvm/Transforms/Scalar.h"
24+
#include "llvm/Target/TargetMachine.h"
25+
#include <cstring>
26+
#include <string>
27+
28+
using namespace dmd;
29+
30+
namespace {
31+
class TargetVulkan : public DComputeTarget {
32+
public:
33+
TargetVulkan(llvm::LLVMContext &c, int ver)
34+
: DComputeTarget(c, ver, ID::Vulkan, "vulkan", "spv", createSPIRVVulkanABI(),
35+
{{0, 1, 2, 3, 4}}) {
36+
37+
_ir = new IRState("dcomputeTargetVulkan", ctx);
38+
// "spirv-vulkan-foo"? foo = library, pixel, etc
39+
std::string targTriple = "spirv1.6-unknown-vulkan1.3-compute";
40+
_ir->module.setTargetTriple(llvm::Triple(targTriple));
41+
42+
#if LDC_LLVM_VER >= 1600
43+
auto floatABI = ::FloatABI::Hard;
44+
targetMachine = createTargetMachine(
45+
targTriple, "spirv", "", {},
46+
ExplicitBitness::None, floatABI,
47+
llvm::Reloc::Static, llvm::CodeModel::Medium, codeGenOptLevel(), false);
48+
#endif
49+
_ir->module.setDataLayout(targetMachine->createDataLayout());
50+
51+
_ir->dcomputetarget = this;
52+
}
53+
54+
void addMetadata() override {}
55+
56+
llvm::AttrBuilder buildKernAttrs(StructLiteralExp *kernAttr) {
57+
auto b = llvm::AttrBuilder(ctx);
58+
b.addAttribute("hlsl.shader", "compute");
59+
Expressions* elts = static_cast<ArrayLiteralExp*>((*(kernAttr->elements))[0])->elements;
60+
std::string numthreads = "";
61+
numthreads += std::to_string((*elts)[0]->toInteger()) + ",";
62+
numthreads += std::to_string((*elts)[1]->toInteger()) + ",";
63+
numthreads += std::to_string((*elts)[2]->toInteger());
64+
65+
b.addAttribute("hlsl.numthreads", numthreads);
66+
// ? "hlsl.wavesize"="8,128,64"
67+
// ? "hlsl.export"
68+
return b;
69+
}
70+
llvm::Function *buildFunction(FuncDeclaration *fd) {
71+
auto *void_func_void = llvm::FunctionType::get(llvm::Type::getVoidTy(ctx),{}, false);
72+
auto linkage = llvm::GlobalValue::LinkageTypes::ExternalLinkage;
73+
auto name = llvm::Twine(mangleExact(fd)) + llvm::Twine("_kernel");
74+
auto *f = llvm::Function::Create(void_func_void, linkage, name, _ir->module);
75+
f->setCallingConv(llvm::CallingConv::SPIR_KERNEL);
76+
return f;
77+
}
78+
llvm::Type *buildArgType(llvm::Function *llf, llvm::SmallVector<llvm::Type *, 8> &args, llvm::StringRef name) {
79+
IF_LOG {
80+
Logger::cout() << "buildArgType: " << *llf << std::endl;
81+
}
82+
llvm::FunctionType *tf = llf->getFunctionType();
83+
for (unsigned int i = 0; i < tf->getNumParams(); i++) {
84+
llvm::Type *t = tf->getParamType(i);
85+
if (t->isPointerTy())
86+
t = getI64Type();
87+
args[i] = t;
88+
}
89+
90+
IF_LOG {
91+
for (auto *arg : args) {
92+
Logger::cout() << *arg;
93+
}
94+
}
95+
return llvm::StructType::create(ctx, args, name);
96+
}
97+
llvm::TargetExtType *buildTargetType(llvm::Type *argType) {
98+
// TODO: Do we need to bother with a "spirv.Layout" here?
99+
//auto *layout = llvm::TargetExtType::get(ctx, "spirv.Layout", ElemType,{});
100+
auto * ArrayType = llvm::ArrayType::get(argType, 0);
101+
return llvm::TargetExtType::get(ctx, "spirv.VulkanBuffer",
102+
{ArrayType},
103+
{12/*StorageClass*/, 0 /*isWritable*/});
104+
}
105+
106+
llvm::Value *buildIntrinsicCall(IRBuilder<>& builder, llvm::StringRef dbg,llvm::StringRef name,
107+
llvm::ArrayRef<llvm::Type *> types, llvm::ArrayRef<llvm::Value *> args) {
108+
IF_LOG {
109+
Logger::println("buildIntrinsicCall: %s", name.data());
110+
}
111+
LOG_SCOPE
112+
llvm::Function *intrinsic = llvm::Intrinsic::getOrInsertDeclaration(&_ir->module,
113+
llvm::Intrinsic::lookupIntrinsicID(name),
114+
types);
115+
IF_LOG {
116+
Logger::cout() << "intrinsic = " << *intrinsic << std::endl;
117+
Logger::println("args:");
118+
LOG_SCOPE
119+
for (auto* arg : args) {
120+
Logger::cout() << *arg << std::endl;
121+
}
122+
}
123+
124+
return builder.CreateCall(intrinsic->getFunctionType(), intrinsic, args, dbg);
125+
}
126+
127+
void addKernelMetadata(FuncDeclaration *fd, llvm::Function *llf, StructLiteralExp *kernAttr) override {
128+
// Fake being HLSL
129+
llvm::Function *f = buildFunction(fd);
130+
f->addFnAttrs(buildKernAttrs(kernAttr));
131+
132+
llvm::SmallVector<llvm::Type *, 8> argTypes(llf->getFunctionType()->getNumParams());
133+
auto name = llvm::Twine(mangleExact(fd)) + llvm::Twine("_args");
134+
auto *argType = buildArgType(llf, argTypes, name.str());
135+
llvm::Type *targetType = buildTargetType(argType);
136+
137+
auto bb = llvm::BasicBlock::Create(ctx, "", f);
138+
llvm::IRBuilder<> builder(ctx);
139+
builder.SetInsertPoint(bb);
140+
141+
llvm::Value *i32zero = llvm::ConstantInt::get(getI32Type(), 0, false);
142+
llvm::Value *i32one = llvm::ConstantInt::get(getI32Type(), 1, false);
143+
llvm::Value *i1false = llvm::ConstantInt::get(llvm::Type::getInt1Ty(ctx), 0, false);
144+
145+
auto *handle = buildIntrinsicCall(builder, "handle","llvm.spv.resource.handlefrombinding",
146+
{targetType}, {i32zero, i32zero, i32one, i32zero, i1false});
147+
auto *p11 = llvm::PointerType::get(ctx, 11);
148+
auto *pointer = buildIntrinsicCall(builder, "pointer", "llvm.spv.resource.getpointer",
149+
{p11, targetType}, {handle, i32one});
150+
llvm::FunctionType *tf = llf->getFunctionType();
151+
IF_LOG {
152+
Logger::cout() << "load pointer: " << *pointer << std::endl;
153+
Logger::cout() << _ir->module.getDataLayout().getABITypeAlign(argType).value() << std::endl;
154+
Logger::cout() << tf->getParamType(0)->getTypeID() << std::endl;
155+
Logger::cout() << "done" << std::endl;
156+
}
157+
LOG_SCOPE
158+
llvm::SmallVector<llvm::Value *, 8> args(tf->getNumParams());
159+
160+
auto *arg = builder.CreateAlignedLoad(argType, pointer, _ir->module.getDataLayout().getABITypeAlign(argType), false);
161+
IF_LOG {
162+
// Logger::cout() << "load elements from " << *arg << std::endl;
163+
// Logger::cout() << "of type " << *argType << std::endl;
164+
}
165+
for (unsigned int i = 0; i < tf->getNumParams(); i++) {
166+
args[i] = builder.CreateExtractValue(arg, {i});
167+
llvm::Type *t = tf->getParamType(i);
168+
if (t->isPointerTy())
169+
args[i] = builder.CreateIntToPtr(args[i],t);
170+
}
171+
172+
builder.CreateCall(llf->getFunctionType(), llf, args);
173+
builder.CreateRetVoid();
174+
IF_LOG Logger::cout() << *f << std::endl;
175+
}
176+
177+
};
178+
} // anonymous namespace.
179+
180+
DComputeTarget *createVulkanTarget(llvm::LLVMContext &c, int ver) {
181+
return new TargetVulkan(c, ver);
182+
}
183+
184+
#endif // LDC_LLVM_SUPPORTED_TARGET_SPIRV

runtime/druntime/src/ldc/dcompute.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ enum ReflectTarget : uint
1414
Host = 0,
1515
OpenCL = 1,
1616
CUDA = 2,
17+
Vulkan = 3,
1718
}
1819
/**
1920
* The pseudo conditional compilation function.

0 commit comments

Comments
 (0)