|  | 
|  | 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 && LDC_LLVM_VER >= 2100 | 
|  | 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 | +    auto floatABI = ::FloatABI::Hard; | 
|  | 43 | +    targetMachine = createTargetMachine( | 
|  | 44 | +            targTriple, "spirv", "", {}, | 
|  | 45 | +            ExplicitBitness::None, floatABI, | 
|  | 46 | +            llvm::Reloc::Static, llvm::CodeModel::Medium, codeGenOptLevel(), false); | 
|  | 47 | + | 
|  | 48 | +    _ir->module.setDataLayout(targetMachine->createDataLayout()); | 
|  | 49 | +      | 
|  | 50 | +    _ir->dcomputetarget = this; | 
|  | 51 | +  } | 
|  | 52 | + | 
|  | 53 | +  void addMetadata() override {} | 
|  | 54 | + | 
|  | 55 | +  llvm::AttrBuilder buildKernAttrs(StructLiteralExp *kernAttr) { | 
|  | 56 | +    auto b = llvm::AttrBuilder(ctx); | 
|  | 57 | +    b.addAttribute("hlsl.shader", "compute"); | 
|  | 58 | +    Expressions* elts = static_cast<ArrayLiteralExp*>((*(kernAttr->elements))[0])->elements; | 
|  | 59 | +    std::string numthreads = ""; | 
|  | 60 | +    numthreads += std::to_string((*elts)[0]->toInteger()) + ","; | 
|  | 61 | +    numthreads += std::to_string((*elts)[1]->toInteger()) + ","; | 
|  | 62 | +    numthreads += std::to_string((*elts)[2]->toInteger()); | 
|  | 63 | + | 
|  | 64 | +    b.addAttribute("hlsl.numthreads", numthreads); | 
|  | 65 | +    //  ?  "hlsl.wavesize"="8,128,64" | 
|  | 66 | +    //  ?  "hlsl.export" | 
|  | 67 | +    return b; | 
|  | 68 | +  } | 
|  | 69 | +  llvm::Function *buildFunction(FuncDeclaration *fd) { | 
|  | 70 | +    auto *void_func_void = llvm::FunctionType::get(llvm::Type::getVoidTy(ctx),{}, false); | 
|  | 71 | +    auto linkage = llvm::GlobalValue::LinkageTypes::ExternalLinkage; | 
|  | 72 | +    auto name = llvm::Twine(mangleExact(fd)) + llvm::Twine("_kernel"); | 
|  | 73 | +    auto *f = llvm::Function::Create(void_func_void, linkage, name, _ir->module); | 
|  | 74 | +    f->setCallingConv(llvm::CallingConv::SPIR_KERNEL); | 
|  | 75 | +    return f; | 
|  | 76 | +  } | 
|  | 77 | +  llvm::Type *buildArgType(llvm::Function *llf, llvm::SmallVector<llvm::Type *, 8> &args, llvm::StringRef name) { | 
|  | 78 | +    IF_LOG { | 
|  | 79 | +      Logger::cout() << "buildArgType: " << *llf << std::endl; | 
|  | 80 | +    } | 
|  | 81 | +    llvm::FunctionType *tf = llf->getFunctionType(); | 
|  | 82 | +    for (unsigned int i = 0; i < tf->getNumParams(); i++) { | 
|  | 83 | +      llvm::Type *t = tf->getParamType(i); | 
|  | 84 | +      if (t->isPointerTy()) | 
|  | 85 | +        t = getI64Type(); // FIXME: 32 bit pointers on 32 but systems? | 
|  | 86 | +      args[i] = t; | 
|  | 87 | +    } | 
|  | 88 | + | 
|  | 89 | +    IF_LOG { | 
|  | 90 | +      for (auto *arg : args) { | 
|  | 91 | +        Logger::cout() << *arg; | 
|  | 92 | +      } | 
|  | 93 | +    } | 
|  | 94 | +    return llvm::StructType::create(ctx, args, name); | 
|  | 95 | +  } | 
|  | 96 | +  llvm::TargetExtType *buildTargetType(llvm::Type *argType) { | 
|  | 97 | +    // TODO: Do we need to bother with a "spirv.Layout" here? | 
|  | 98 | +    //auto *layout = llvm::TargetExtType::get(ctx, "spirv.Layout", ElemType,{}); | 
|  | 99 | +    auto * ArrayType = llvm::ArrayType::get(argType, 0); | 
|  | 100 | +    return llvm::TargetExtType::get(ctx, "spirv.VulkanBuffer", | 
|  | 101 | +                                    {ArrayType}, | 
|  | 102 | +                                    {12/*StorageClass*/, 0 /*isWritable*/}); | 
|  | 103 | +  } | 
|  | 104 | + | 
|  | 105 | +  llvm::Value *buildIntrinsicCall(IRBuilder<>& builder, llvm::StringRef dbg,llvm::StringRef name, | 
|  | 106 | +                                     llvm::ArrayRef<llvm::Type *> types, llvm::ArrayRef<llvm::Value *> args) { | 
|  | 107 | +    IF_LOG { | 
|  | 108 | +      Logger::println("buildIntrinsicCall: %s", name.data()); | 
|  | 109 | +    } | 
|  | 110 | +    LOG_SCOPE | 
|  | 111 | +    llvm::Function *intrinsic = llvm::Intrinsic::getOrInsertDeclaration(&_ir->module, | 
|  | 112 | +                                                   llvm::Intrinsic::lookupIntrinsicID(name), | 
|  | 113 | +                                                   types); | 
|  | 114 | +    IF_LOG { | 
|  | 115 | +      Logger::cout() << "intrinsic = " << *intrinsic << std::endl; | 
|  | 116 | +      Logger::println("args:"); | 
|  | 117 | +      LOG_SCOPE | 
|  | 118 | +      for (auto* arg : args) { | 
|  | 119 | +        Logger::cout() << *arg << std::endl; | 
|  | 120 | +      } | 
|  | 121 | +    } | 
|  | 122 | +     | 
|  | 123 | +    return builder.CreateCall(intrinsic->getFunctionType(), intrinsic, args, dbg); | 
|  | 124 | +  } | 
|  | 125 | + | 
|  | 126 | +  void addKernelMetadata(FuncDeclaration *fd, llvm::Function *llf, StructLiteralExp *kernAttr) override { | 
|  | 127 | +    // Fake being HLSL | 
|  | 128 | +    llvm::Function *f = buildFunction(fd); | 
|  | 129 | +    f->addFnAttrs(buildKernAttrs(kernAttr)); | 
|  | 130 | + | 
|  | 131 | +    llvm::SmallVector<llvm::Type *, 8> argTypes(llf->getFunctionType()->getNumParams()); | 
|  | 132 | +    auto name = llvm::Twine(mangleExact(fd)) + llvm::Twine("_args"); | 
|  | 133 | +    auto *argType = buildArgType(llf, argTypes, name.str()); | 
|  | 134 | +    llvm::Type *targetType = buildTargetType(argType); | 
|  | 135 | +   | 
|  | 136 | +    auto bb = llvm::BasicBlock::Create(ctx, "", f); | 
|  | 137 | +    llvm::IRBuilder<> builder(ctx); | 
|  | 138 | +    builder.SetInsertPoint(bb); | 
|  | 139 | + | 
|  | 140 | +    llvm::Value *i32zero = llvm::ConstantInt::get(getI32Type(), 0, false); | 
|  | 141 | +    llvm::Value *i32one  = llvm::ConstantInt::get(getI32Type(), 1, false); | 
|  | 142 | +    llvm::Value *i1false = llvm::ConstantInt::get(llvm::Type::getInt1Ty(ctx),  0, false); | 
|  | 143 | +     | 
|  | 144 | +    // We can't use `DtoConstCString` here because it ends up in the wrong address space, So we use | 
|  | 145 | +    // `getCachedStringLiteral` directly with an explicitly supplied addrspace of `0`. | 
|  | 146 | +    // FIXME: call should have `notnull` attribute on pointer? | 
|  | 147 | +    auto *handle = buildIntrinsicCall(builder, "handle","llvm.spv.resource.handlefrombinding", | 
|  | 148 | +                                      {targetType}, | 
|  | 149 | +                                      {i32zero, i32zero, i32one, i32zero, i1false, _ir->getCachedStringLiteral(name.str(), 0) }); | 
|  | 150 | +    auto *p11 = llvm::PointerType::get(ctx, 11); | 
|  | 151 | +    auto *pointer = buildIntrinsicCall(builder, "pointer", "llvm.spv.resource.getpointer", | 
|  | 152 | +                                       {p11, targetType}, {handle, i32one}); | 
|  | 153 | +    llvm::FunctionType *tf = llf->getFunctionType(); | 
|  | 154 | +    IF_LOG  { | 
|  | 155 | +      Logger::cout() << "load pointer: " << *pointer << std::endl; | 
|  | 156 | +      Logger::cout() << _ir->module.getDataLayout().getABITypeAlign(argType).value() << std::endl; | 
|  | 157 | +      Logger::cout() << tf->getParamType(0)->getTypeID() << std::endl; | 
|  | 158 | +      Logger::cout() << "done" << std::endl; | 
|  | 159 | +    } | 
|  | 160 | +    LOG_SCOPE | 
|  | 161 | +    llvm::SmallVector<llvm::Value *, 8> args(tf->getNumParams()); | 
|  | 162 | + | 
|  | 163 | +    auto *arg = builder.CreateAlignedLoad(argType, pointer, _ir->module.getDataLayout().getABITypeAlign(argType), false); | 
|  | 164 | +    IF_LOG { | 
|  | 165 | +    //  Logger::cout() << "load elements from " << *arg << std::endl; | 
|  | 166 | +    //  Logger::cout() << "of type " << *argType << std::endl; | 
|  | 167 | +    } | 
|  | 168 | +    for (unsigned int i = 0; i < tf->getNumParams(); i++) { | 
|  | 169 | +      args[i] = builder.CreateExtractValue(arg, {i}); | 
|  | 170 | +      llvm::Type *t = tf->getParamType(i); | 
|  | 171 | +      if (t->isPointerTy()) | 
|  | 172 | +        args[i] = builder.CreateIntToPtr(args[i],t); | 
|  | 173 | +    } | 
|  | 174 | + | 
|  | 175 | +    builder.CreateCall(llf->getFunctionType(), llf, args); | 
|  | 176 | +    builder.CreateRetVoid(); | 
|  | 177 | +    IF_LOG Logger::cout() << *f << std::endl; | 
|  | 178 | +  } | 
|  | 179 | + | 
|  | 180 | +}; | 
|  | 181 | +} // anonymous namespace. | 
|  | 182 | + | 
|  | 183 | +DComputeTarget *createVulkanTarget(llvm::LLVMContext &c, int ver) { | 
|  | 184 | +  return new TargetVulkan(c, ver); | 
|  | 185 | +} | 
|  | 186 | + | 
|  | 187 | +#endif // LDC_LLVM_SUPPORTED_TARGET_SPIRV | 
0 commit comments