From dcdbcfead915e778615929867cda3bc18399fc06 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Wed, 15 Apr 2026 15:36:06 +0200 Subject: [PATCH 01/30] Implement initial support for Metal GPU backend --- driver/dcomputecodegenerator.cpp | 11 ++ gen/abi/abi.cpp | 13 ++ gen/abi/metal.cpp | 52 +++++++ gen/abi/targets.h | 2 + gen/dcompute/druntime.h | 2 +- gen/dcompute/target.cpp | 2 + gen/dcompute/target.h | 6 +- gen/dcompute/targetMetal.cpp | 215 ++++++++++++++++++++++++++++ runtime/druntime/src/ldc/dcompute.d | 3 +- 9 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 gen/abi/metal.cpp create mode 100644 gen/dcompute/targetMetal.cpp diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index 0b95fcaa36d..549c3b0a28c 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -11,6 +11,7 @@ #include "driver/cl_options.h" #include "driver/cl_helpers.h" #include "dmd/errors.h" +#include "globals.h" #include "ir/irdsymbol.h" #include "llvm/Support/CommandLine.h" #include @@ -43,6 +44,15 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } + if (s.substr(0, 6) == "air64-") { +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 //&& LDC_LLVM_VER >= 2100 + return createMetalTarget(ctx, 64); +#else + error(Loc(), "LDC was not built with Apple Metal Dcompute support!"); +#endif + } + + if (s.substr(0, 5) == "cuda-") { #if LDC_LLVM_SUPPORTED_TARGET_NVPTX #define CUDA_VALID_VER_INIT 100, 110, 120, 130, 200, 210, 300, 350, 370,\ @@ -59,6 +69,7 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } + #define STR(...) #__VA_ARGS__ #define XSTR(x) STR(x) diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index ab9c51665b9..09343371706 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -22,8 +22,10 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/tollvm.h" +#include "globals.h" #include "ir/irfunction.h" #include "ir/irfuncty.h" +#include "llvm/TargetParser/Triple.h" #include using namespace dmd; @@ -302,6 +304,17 @@ TargetABI *TargetABI::getTarget() { case llvm::Triple::wasm32: case llvm::Triple::wasm64: return getWasmTargetABI(); + + case llvm::Triple::UnknownArch: + if (global.params.targetTriple->getArchName() == "air64") { + return createMetalABI(); + } + + warning(Loc(), + "unknown target ABI, falling back to generic implementation. C/C++ " + "interop will almost certainly NOT work."); + return new TargetABI; + default: warning(Loc(), "unknown target ABI, falling back to generic implementation. C/C++ " diff --git a/gen/abi/metal.cpp b/gen/abi/metal.cpp new file mode 100644 index 00000000000..9455a4064b1 --- /dev/null +++ b/gen/abi/metal.cpp @@ -0,0 +1,52 @@ +//===-- gen/abi-metal.cpp ---------------------------------------*- C++ -*-===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#include "dmd/identifier.h" +#include "dmd/nspace.h" +#include "gen/abi/abi.h" +#include "gen/abi/generic.h" +#include "gen/dcompute/druntime.h" +#include "ir/irfuncty.h" +#include "gen/dcompute/abi-rewrites.h" +#include "mtype.h" +#include "llvm/Support/raw_ostream.h" +#include + + +using namespace dmd; + +struct MetalABI : TargetABI { + DComputePointerRewrite pointerRewite; + + auto returnInArg(TypeFunction *tf, bool needsThis) -> bool override { + return false; + } + + auto passByVal(TypeFunction *tf, Type*t) -> bool override { + return false; + } + + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override { + TargetABI::rewriteArgument(fty, arg); + + if (arg.rewrite) { + return; + } + + Type *ty = arg.type->toBasetype(); + std::optional ptr; + + if (ty->ty == TY::Tstruct && + (ptr = toDcomputePointer(static_cast(ty)->sym))) { + pointerRewite.applyTo(arg); + } + } +}; + +auto createMetalABI() -> TargetABI* { return new MetalABI(); } diff --git a/gen/abi/targets.h b/gen/abi/targets.h index 49098fe2579..ecede93331d 100644 --- a/gen/abi/targets.h +++ b/gen/abi/targets.h @@ -40,3 +40,5 @@ TargetABI *getX86TargetABI(); TargetABI *getLoongArch64TargetABI(); TargetABI *getWasmTargetABI(); + +auto createMetalABI() -> TargetABI*; \ No newline at end of file diff --git a/gen/dcompute/druntime.h b/gen/dcompute/druntime.h index 673eb24951f..5f41d101255 100644 --- a/gen/dcompute/druntime.h +++ b/gen/dcompute/druntime.h @@ -38,4 +38,4 @@ struct DcomputePointer { return LLPointerType::get(getGlobalContext(), as); } }; -std::optional toDcomputePointer(StructDeclaration *sd); +auto toDcomputePointer(StructDeclaration *sd) -> std::optional; diff --git a/gen/dcompute/target.cpp b/gen/dcompute/target.cpp index 3825c8b7f82..6f2eefd7ddb 100644 --- a/gen/dcompute/target.cpp +++ b/gen/dcompute/target.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/raw_ostream.h" #if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX #include "dmd/dsymbol.h" diff --git a/gen/dcompute/target.h b/gen/dcompute/target.h index 1c44d1de7d2..22c90529b86 100644 --- a/gen/dcompute/target.h +++ b/gen/dcompute/target.h @@ -28,7 +28,7 @@ class DComputeTarget { public: llvm::LLVMContext &ctx; int tversion; // OpenCL or CUDA CC version:major*100 + minor*10 - enum class ID { Host = 0, OpenCL = 1, CUDA = 2 }; + enum class ID { Host = 0, OpenCL = 1, CUDA = 2, Metal = 3 }; ID target; // ID for codegen time conditional compilation. const char *short_name; const char *binSuffix; @@ -64,6 +64,10 @@ class DComputeTarget { DComputeTarget *createCUDATarget(llvm::LLVMContext &c, int sm); #endif +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 +auto createMetalTarget(llvm::LLVMContext &c, int version) -> DComputeTarget *; +#endif + #if LDC_LLVM_SUPPORTED_TARGET_SPIRV DComputeTarget *createOCLTarget(llvm::LLVMContext &c, int oclver); #endif diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp new file mode 100644 index 00000000000..fad5f34373a --- /dev/null +++ b/gen/dcompute/targetMetal.cpp @@ -0,0 +1,215 @@ +//===-- gen/dcompute/targetCUDA.cpp ---------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#include "declaration.h" +#include "gen/dcompute/druntime.h" +#include "mtype.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include "dmd/identifier.h" + +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 + +#include "gen/dcompute/target.h" +#include "gen/abi/targets.h" +#include "gen/logger.h" +#include "gen/optimizer.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/Scalar.h" +#include "driver/targetmachine.h" +#include + +namespace { +class TargetMetal : public DComputeTarget { +public: + TargetMetal(llvm::LLVMContext &c, int version) + : DComputeTarget( + c, version, ID::Metal, "metal", "air", createMetalABI(), + + // DCompute Order: [Private, Global, Shared, Constant, Generic] + // AIR equivalents: Private=0, Device/Global=1, Threadgroup/Shared=3, Constant=2 + {{0, 1, 3, 2, 0}}) { + const bool is64 = global.params.targetTriple->isArch64Bit(); + + _ir = new IRState("dcomputeTargetMetal", ctx); + // TODO: need to find 32-bit triple + auto tripleString = "air64_v28-apple-macosx26.0.0"; + + // std::string targTripleStr = is64 ? SPIR_TARGETTRIPLE64 + // : SPIR_TARGETTRIPLE32; + #if LDC_LLVM_VER >= 2100 + llvm::Triple targTriple = llvm::Triple(tripleString); + #else + std::string targTriple = tripleString; + #endif + _ir->module.setTargetTriple(targTriple); + + llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" + "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" + "v512:512:512-v1024:1024:1024-n8:16:32"; + + auto floatABI = ::FloatABI::Hard; + // targetMachine = createTargetMachine( + // targTriple, + // is64 ? "" : "", + // "", {}, + // is64 ? ExplicitBitness::M64 : ExplicitBitness::M32, floatABI, + // llvm::Reloc::Static, llvm::CodeModel::Medium, codeGenOptLevel(), false); + _ir->module.setDataLayout(is64 ? dataLayout: /* TODO: need to find 32-bit data layout */dataLayout); + _ir->dcomputetarget = this; + } + + void addMetadata() override { + llvm::NamedMDNode *airVersion = _ir->module.getOrInsertNamedMetadata("air.version"); + llvm::Metadata *major = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 2)); + llvm::Metadata *minor = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 8)); + llvm::Metadata *patch = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0)); + std::array arr = {major, minor, patch}; + airVersion->addOperand(llvm::MDTuple::get(ctx, arr)); + + llvm::NamedMDNode *airLangVersion = _ir->module.getOrInsertNamedMetadata("air.language_version"); + std::array langArr = { + llvm::MDString::get(ctx, "Metal"), + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 4)), + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0)), + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0)) + }; + airLangVersion->addOperand(llvm::MDTuple::get(ctx, langArr)); + } + + void addKernelMetadata(FuncDeclaration *df, llvm::Function *llf, StructLiteralExp *_unused_) override { + llvm::errs() << "\n\nAdding kernel metadata...............\n\n"; + llvm::NamedMDNode *kernels = _ir->module.getOrInsertNamedMetadata("air.kernel"); + + std::vector kernelMetadataArguments; + kernelMetadataArguments.push_back(llvm::ConstantAsMetadata::get(llf)); + + // XXX: unknown, not sure why we need this, Metal backend expects it + kernelMetadataArguments.push_back( + llvm::MDNode::get(ctx, {}) + ); + + std::vector argumentMetadata = addArgumentMetadata(df, llf); + + kernelMetadataArguments.push_back( + llvm::MDNode::get(ctx, argumentMetadata) + ); + + llvm::MDTuple *kernelTuple = llvm::MDTuple::get(ctx, kernelMetadataArguments); + + kernels->addOperand(kernelTuple); + } + + auto addArgumentMetadata(FuncDeclaration *df, llvm::Function *llf) -> std::vector { + std::vector kernelMetadataArguments; + int locationIndex = 0; + + for(auto &arg: llf->args()) { + std::vector argumentMetadata; + + argumentMetadata.push_back( + llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get( + llvm::IntegerType::get(ctx, 32), locationIndex))); + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.buffer")); + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.location_index")); + argumentMetadata.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), locationIndex) + )); + + // XXX: unknown, not sure why we need this, Metal backend expects it + argumentMetadata.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 1) + )); + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.read_write")); + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.address_space")); + + if (arg.getType()->isPointerTy()){ + unsigned addressSpace = arg.getType()->getPointerAddressSpace(); + argumentMetadata.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), addressSpace) + )); + } else { + argumentMetadata.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0) + )); + } + + VarDeclaration *vd = (*df->parameters)[locationIndex]; + addArgumentTypeInformation(vd, argumentMetadata); + + if (!argumentMetadata.empty()) { + kernelMetadataArguments.push_back(llvm::MDTuple::get(ctx, argumentMetadata)); + } + + locationIndex++; + } + + return kernelMetadataArguments; + } + + void addArgumentTypeInformation(VarDeclaration *vd, std::vector &argumentMetadata) { + Type *type = nullptr; + std::optional ptr; + if (vd->type->ty == TY::Tstruct && (ptr = toDcomputePointer(static_cast(vd->type)->sym))){ + type = ptr->type; + } else { + type = vd->type; + } + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_type_size")); + argumentMetadata.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), dmd::size(type, vd->loc)) + )); + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_type_align_size")); + argumentMetadata.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), type->alignsize()) + )); + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_type_name")); + // TODO: check if using char needed instead of int8 as in ocl target implementation + argumentMetadata.push_back(llvm::MDString::get(ctx, basicTypeToString(type))); + + argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_name")); + argumentMetadata.push_back(llvm::MDString::get(ctx, vd->ident->toChars())); + } + + auto basicTypeToString(Type *t) -> std::string { + std::stringstream ss; + auto ty = t->ty; + if (ty == TY::Tint8) { + ss << "char"; + } else if (ty == TY::Tuns8) { + ss << "uchar"; + } else { + ss << t->toChars(); + } + + return ss.str(); + } + +}; +} // anonymous namespace. + +auto createMetalTarget(llvm::LLVMContext &c, int version) -> DComputeTarget * { + return new TargetMetal(c, version); +}; + +#endif // LDC_LLVM_SUPPORTED_TARGET_AArch64 diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index 04002a43d91..1ed33e5dc96 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -14,6 +14,7 @@ enum ReflectTarget : uint Host = 0, OpenCL = 1, CUDA = 2, + Metal = 3, } /** * The pseudo conditional compilation function. @@ -24,7 +25,7 @@ enum ReflectTarget : uint * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y * use 0 as a wildcard to match any version. - + * This is mostly used for selecting the correct intrinsic for the * given target and version, but could also be used to tailor for * performance characteristics. See dcompute.std.index for an example From 19eb201e4384362e850fc9535b126b4b4214341c Mon Sep 17 00:00:00 2001 From: Asadbek Date: Thu, 23 Apr 2026 21:15:28 +0200 Subject: [PATCH 02/30] Implement support for simple saxpy kernel. Convert scalars into pointer and load them in the function body --- gen/abi/abi.cpp | 6 +++--- gen/abi/metal.cpp | 15 +++++++++++++++ gen/dcompute/abi-rewrites.h | 25 +++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index 09343371706..15fac002dbc 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -304,17 +304,17 @@ TargetABI *TargetABI::getTarget() { case llvm::Triple::wasm32: case llvm::Triple::wasm64: return getWasmTargetABI(); - + case llvm::Triple::UnknownArch: if (global.params.targetTriple->getArchName() == "air64") { return createMetalABI(); } - + warning(Loc(), "unknown target ABI, falling back to generic implementation. C/C++ " "interop will almost certainly NOT work."); return new TargetABI; - + default: warning(Loc(), "unknown target ABI, falling back to generic implementation. C/C++ " diff --git a/gen/abi/metal.cpp b/gen/abi/metal.cpp index 9455a4064b1..081b7cdf4cc 100644 --- a/gen/abi/metal.cpp +++ b/gen/abi/metal.cpp @@ -12,6 +12,7 @@ #include "gen/abi/abi.h" #include "gen/abi/generic.h" #include "gen/dcompute/druntime.h" +#include "gen/uda.h" #include "ir/irfuncty.h" #include "gen/dcompute/abi-rewrites.h" #include "mtype.h" @@ -23,6 +24,7 @@ using namespace dmd; struct MetalABI : TargetABI { DComputePointerRewrite pointerRewite; + DcomputeMetalScalarRewrite metalScalarRewrite; auto returnInArg(TypeFunction *tf, bool needsThis) -> bool override { return false; @@ -32,6 +34,14 @@ struct MetalABI : TargetABI { return false; } + void rewriteFunctionType(IrFuncTy &fty) override { + for (auto arg : fty.args) { + if (!arg->byref) { + rewriteArgument(fty, *arg); + } + } + } + void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override { TargetABI::rewriteArgument(fty, arg); @@ -46,6 +56,11 @@ struct MetalABI : TargetABI { (ptr = toDcomputePointer(static_cast(ty)->sym))) { pointerRewite.applyTo(arg); } + + if (ty->isScalar()) { + llvm::errs() << "Applying Metal Scalar Rewrite to: " << ty->toChars() << "\n"; + metalScalarRewrite.applyTo(arg); + } } }; diff --git a/gen/dcompute/abi-rewrites.h b/gen/dcompute/abi-rewrites.h index 1821df81b0c..bcfde8b9e13 100644 --- a/gen/dcompute/abi-rewrites.h +++ b/gen/dcompute/abi-rewrites.h @@ -13,7 +13,16 @@ #pragma once +#include "gen/abi/abi.h" #include "gen/abi/generic.h" +#include "gen/dcompute/druntime.h" +#include "gen/dvalue.h" +#include "gen/irstate.h" +#include "gen/llvm.h" +#include "gen/tollvm.h" +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/Support/raw_ostream.h" struct DComputePointerRewrite : ABIRewrite { LLValue *put(DValue *v, bool isLValueExp, bool) override { @@ -32,3 +41,19 @@ struct DComputePointerRewrite : ABIRewrite { return ptr->toLLVMType(true); } }; + +struct DcomputeMetalScalarRewrite : ABIRewrite { + LLType *type(Type* t) override { + // XXXX: Scalar variables are stored in the constant memory space for Metal GPU + return llvm::PointerType::get(gIR->context(), 2/*Constant Memory space*/); + } + + LLValue *getLVal(Type *dty, LLValue *v) override { + return v; + } + + LLValue *put(DValue *v, bool isLValueExp, bool) override { + auto value = DtoRVal(v); + return value; + } +}; From 8a2b6eb6148f0f21bb11182dfdb89ac8f31ee2b8 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sat, 25 Apr 2026 10:01:49 +0200 Subject: [PATCH 03/30] Resolve comments on incorrect logging & small cleanup --- driver/dcomputecodegenerator.cpp | 8 ++++---- gen/abi/abi.cpp | 2 +- gen/abi/metal.cpp | 2 +- gen/dcompute/target.cpp | 2 -- gen/dcompute/targetMetal.cpp | 3 +-- runtime/druntime/src/ldc/dcompute.d | 31 +++++++++++++++-------------- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index 549c3b0a28c..f3f7605ff1a 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -11,7 +11,7 @@ #include "driver/cl_options.h" #include "driver/cl_helpers.h" #include "dmd/errors.h" -#include "globals.h" +#include "dmd/globals.h" #include "ir/irdsymbol.h" #include "llvm/Support/CommandLine.h" #include @@ -45,8 +45,8 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { } if (s.substr(0, 6) == "air64-") { -#if LDC_LLVM_SUPPORTED_TARGET_AArch64 //&& LDC_LLVM_VER >= 2100 - return createMetalTarget(ctx, 64); +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 //&& LDC_LLVM_VER >= 2100 + return createMetalTarget(ctx, 64); #else error(Loc(), "LDC was not built with Apple Metal Dcompute support!"); #endif @@ -69,7 +69,7 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } - + #define STR(...) #__VA_ARGS__ #define XSTR(x) STR(x) diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index 15fac002dbc..ab875c40d2e 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -22,7 +22,7 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/tollvm.h" -#include "globals.h" +#include "dmd/globals.h" #include "ir/irfunction.h" #include "ir/irfuncty.h" #include "llvm/TargetParser/Triple.h" diff --git a/gen/abi/metal.cpp b/gen/abi/metal.cpp index 081b7cdf4cc..1e10b9b72e2 100644 --- a/gen/abi/metal.cpp +++ b/gen/abi/metal.cpp @@ -12,6 +12,7 @@ #include "gen/abi/abi.h" #include "gen/abi/generic.h" #include "gen/dcompute/druntime.h" +#include "gen/logger.h" #include "gen/uda.h" #include "ir/irfuncty.h" #include "gen/dcompute/abi-rewrites.h" @@ -58,7 +59,6 @@ struct MetalABI : TargetABI { } if (ty->isScalar()) { - llvm::errs() << "Applying Metal Scalar Rewrite to: " << ty->toChars() << "\n"; metalScalarRewrite.applyTo(arg); } } diff --git a/gen/dcompute/target.cpp b/gen/dcompute/target.cpp index 6f2eefd7ddb..3825c8b7f82 100644 --- a/gen/dcompute/target.cpp +++ b/gen/dcompute/target.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/Support/raw_ostream.h" #if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX #include "dmd/dsymbol.h" diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index fad5f34373a..02c9c276c89 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -61,7 +61,7 @@ class TargetMetal : public DComputeTarget { "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" "v512:512:512-v1024:1024:1024-n8:16:32"; - auto floatABI = ::FloatABI::Hard; + // auto floatABI = ::FloatABI::Hard; // targetMachine = createTargetMachine( // targTriple, // is64 ? "" : "", @@ -91,7 +91,6 @@ class TargetMetal : public DComputeTarget { } void addKernelMetadata(FuncDeclaration *df, llvm::Function *llf, StructLiteralExp *_unused_) override { - llvm::errs() << "\n\nAdding kernel metadata...............\n\n"; llvm::NamedMDNode *kernels = _ir->module.getOrInsertNamedMetadata("air.kernel"); std::vector kernelMetadataArguments; diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index 1ed33e5dc96..6ddc38f43be 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -25,14 +25,13 @@ enum ReflectTarget : uint * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y * use 0 as a wildcard to match any version. - * This is mostly used for selecting the correct intrinsic for the * given target and version, but could also be used to tailor for * performance characteristics. See dcompute.std.index for an example * of how to select intrinsics. */ pure nothrow @nogc -extern(C) bool __dcompute_reflect(ReflectTarget t, uint _version = 0); +extern (C) bool __dcompute_reflect(ReflectTarget t, uint _version = 0); ///Readability aliases for compute enum CompileFor : int @@ -72,10 +71,12 @@ struct compute + } + --- +/ -private struct _kernel { +private struct _kernel +{ size_t[3] bounds; } -_kernel kernel(size_t[3] a = [1,1,1]) => _kernel(a); + +_kernel kernel(size_t[3] a = [1, 1, 1]) => _kernel(a); /++ + DCompute has the notion of adress spaces, provide by the magic structs below. @@ -104,18 +105,18 @@ struct Variable(AddrSpace as, T) enum AddrSpace : uint { - Private = 0, - Global = 1, - Shared = 2, + Private = 0, + Global = 1, + Shared = 2, Constant = 3, - Generic = 4, + Generic = 4, } -alias PrivatePointer(T) = Pointer!(AddrSpace.Private, T); -alias GlobalPointer(T) = Pointer!(AddrSpace.Global, T); -alias SharedPointer(T) = Pointer!(AddrSpace.Shared, T); +alias PrivatePointer(T) = Pointer!(AddrSpace.Private, T); +alias GlobalPointer(T) = Pointer!(AddrSpace.Global, T); +alias SharedPointer(T) = Pointer!(AddrSpace.Shared, T); alias ConstantPointer(T) = Pointer!(AddrSpace.Constant, immutable(T)); -alias GenericPointer(T) = Pointer!(AddrSpace.Generic, T); +alias GenericPointer(T) = Pointer!(AddrSpace.Generic, T); // N.B private variables are declared on the stack and so cannot be declared // at module scope. @@ -124,6 +125,6 @@ alias GenericPointer(T) = Pointer!(AddrSpace.Generic, T); // // The __gshared below does not work. It is kludged into place in `DtoResolveVariable` -alias Global(T) = /*__gshared*/ Variable!(AddrSpace.Global, T); -alias Shared(T) = shared Variable!(AddrSpace.Shared, T); -alias Constant(T) = immutable Variable!(AddrSpace.Constant, T); +alias Global(T) = /*__gshared*/ Variable!(AddrSpace.Global, T); +alias Shared(T) = shared Variable!(AddrSpace.Shared, T); +alias Constant(T) = immutable Variable!(AddrSpace.Constant, T); From 8352f3db1e15011f4a5351d847ea016ffc753c87 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sat, 25 Apr 2026 10:22:46 +0200 Subject: [PATCH 04/30] Fix build error on CI --- gen/dcompute/targetMetal.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 02c9c276c89..7b4129d8579 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -51,11 +51,10 @@ class TargetMetal : public DComputeTarget { // std::string targTripleStr = is64 ? SPIR_TARGETTRIPLE64 // : SPIR_TARGETTRIPLE32; #if LDC_LLVM_VER >= 2100 - llvm::Triple targTriple = llvm::Triple(tripleString); + _ir->module.setTargetTriple(llvm::Triple(tripleString)); #else - std::string targTriple = tripleString; + _ir->module.setTargetTriple(tripleString); #endif - _ir->module.setTargetTriple(targTriple); llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" From 63714b0b18000d445c609f98d6d512e776392b6b Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sat, 25 Apr 2026 11:16:47 +0200 Subject: [PATCH 05/30] Fix CI build error --- gen/dcompute/targetMetal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 7b4129d8579..d5e34653d3c 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -51,11 +51,11 @@ class TargetMetal : public DComputeTarget { // std::string targTripleStr = is64 ? SPIR_TARGETTRIPLE64 // : SPIR_TARGETTRIPLE32; #if LDC_LLVM_VER >= 2100 - _ir->module.setTargetTriple(llvm::Triple(tripleString)); + llvm::Triple targTriple = llvm::Triple(tripleString); #else - _ir->module.setTargetTriple(tripleString); + std::string targTriple = tripleString; #endif - + _ir->module.setTargetTriple(tripleString); llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" "v512:512:512-v1024:1024:1024-n8:16:32"; From 34080e847f2a656974d4a11ef5bfdd9a51e47744 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sat, 25 Apr 2026 11:23:47 +0200 Subject: [PATCH 06/30] Fix CI build error --- gen/dcompute/targetMetal.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index d5e34653d3c..873cf22419d 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -48,14 +48,12 @@ class TargetMetal : public DComputeTarget { // TODO: need to find 32-bit triple auto tripleString = "air64_v28-apple-macosx26.0.0"; - // std::string targTripleStr = is64 ? SPIR_TARGETTRIPLE64 - // : SPIR_TARGETTRIPLE32; #if LDC_LLVM_VER >= 2100 llvm::Triple targTriple = llvm::Triple(tripleString); #else std::string targTriple = tripleString; #endif - _ir->module.setTargetTriple(tripleString); + _ir->module.setTargetTriple(targTriple); llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" "v512:512:512-v1024:1024:1024-n8:16:32"; From 5d356c31c4928c814f94502321f0b1aee50dfb48 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sat, 25 Apr 2026 11:47:55 +0200 Subject: [PATCH 07/30] Fix CI build error --- gen/dcompute/targetMetal.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 873cf22419d..670b2fdc7ba 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include #include #include "dmd/identifier.h" @@ -46,14 +47,13 @@ class TargetMetal : public DComputeTarget { _ir = new IRState("dcomputeTargetMetal", ctx); // TODO: need to find 32-bit triple - auto tripleString = "air64_v28-apple-macosx26.0.0"; + llvm::StringRef tripleString = "air64_v28-apple-macosx26.0.0"; #if LDC_LLVM_VER >= 2100 - llvm::Triple targTriple = llvm::Triple(tripleString); + _ir->module.setTargetTriple(llvm::Triple(tripleString)); #else - std::string targTriple = tripleString; + _ir->module.setTargetTriple(tripleString); #endif - _ir->module.setTargetTriple(targTriple); llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" "v512:512:512-v1024:1024:1024-n8:16:32"; From 17a11642f40ec3b8efb7aaac3e7ed6a8819a92a3 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sat, 25 Apr 2026 12:01:53 +0200 Subject: [PATCH 08/30] Fix CI build error --- gen/dcompute/targetMetal.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 670b2fdc7ba..17b5679b6d2 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -49,11 +49,14 @@ class TargetMetal : public DComputeTarget { // TODO: need to find 32-bit triple llvm::StringRef tripleString = "air64_v28-apple-macosx26.0.0"; - #if LDC_LLVM_VER >= 2100 - _ir->module.setTargetTriple(llvm::Triple(tripleString)); - #else - _ir->module.setTargetTriple(tripleString); - #endif + _ir->module.setTargetTriple(llvm::Triple(tripleString)); + + + // #if LDC_LLVM_VER >= 2100 + // _ir->module.setTargetTriple(llvm::Triple(tripleString)); + // #else + // _ir->module.setTargetTriple(tripleString); + // #endif llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" "v512:512:512-v1024:1024:1024-n8:16:32"; From 2579403a5f1c921a1e43915fb737f3741bfda8f8 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Tue, 28 Apr 2026 13:49:47 +0200 Subject: [PATCH 09/30] Fix CI build error --- gen/dcompute/targetMetal.cpp | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 17b5679b6d2..c2834a24475 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -6,6 +6,7 @@ // file for details. // //===----------------------------------------------------------------------===// +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 #include "declaration.h" #include "gen/dcompute/druntime.h" @@ -21,9 +22,6 @@ #include #include #include "dmd/identifier.h" - -#if LDC_LLVM_SUPPORTED_TARGET_AArch64 - #include "gen/dcompute/target.h" #include "gen/abi/targets.h" #include "gen/logger.h" @@ -49,25 +47,16 @@ class TargetMetal : public DComputeTarget { // TODO: need to find 32-bit triple llvm::StringRef tripleString = "air64_v28-apple-macosx26.0.0"; - _ir->module.setTargetTriple(llvm::Triple(tripleString)); - + #if LLVM_VERSION_MAJOR>= 21 + _ir->module.setTargetTriple(llvm::Triple(tripleString)); + #else + _ir->module.setTargetTriple(tripleString); + #endif - // #if LDC_LLVM_VER >= 2100 - // _ir->module.setTargetTriple(llvm::Triple(tripleString)); - // #else - // _ir->module.setTargetTriple(tripleString); - // #endif llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" "v512:512:512-v1024:1024:1024-n8:16:32"; - // auto floatABI = ::FloatABI::Hard; - // targetMachine = createTargetMachine( - // targTriple, - // is64 ? "" : "", - // "", {}, - // is64 ? ExplicitBitness::M64 : ExplicitBitness::M32, floatABI, - // llvm::Reloc::Static, llvm::CodeModel::Medium, codeGenOptLevel(), false); _ir->module.setDataLayout(is64 ? dataLayout: /* TODO: need to find 32-bit data layout */dataLayout); _ir->dcomputetarget = this; } From d67b4521d18d4851f8b6cd58531acc8ae52f5863 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Wed, 29 Apr 2026 16:00:45 +0200 Subject: [PATCH 10/30] Resolve comments on unused includes & small refactoring --- driver/dcomputecodegenerator.cpp | 5 +- driver/dcomputecodegenerator.h | 2 +- gen/abi/abi.cpp | 6 +- gen/abi/metal.cpp | 12 +- gen/abi/targets.h | 2 +- gen/dcompute/druntime.h | 2 +- gen/dcompute/target.cpp | 2 +- gen/dcompute/target.h | 2 +- gen/dcompute/targetMetal.cpp | 227 +++++++++++++++---------------- gen/target.cpp | 2 +- 10 files changed, 124 insertions(+), 138 deletions(-) diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index f3f7605ff1a..ec902488259 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -11,14 +11,13 @@ #include "driver/cl_options.h" #include "driver/cl_helpers.h" #include "dmd/errors.h" -#include "dmd/globals.h" #include "ir/irdsymbol.h" #include "llvm/Support/CommandLine.h" #include #include #include -#if !(LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX) +#if !(LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX || LDC_LLVM_SUPPORTED_TARGET_AArch64) DComputeCodeGenManager::DComputeCodeGenManager(llvm::LLVMContext &c) : ctx(c) {} void DComputeCodeGenManager::emit(Module *) {} @@ -130,4 +129,4 @@ DComputeCodeGenManager::~DComputeCodeGenManager() { gABI = oldGABI; } -#endif // LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX +#endif // LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX || LDC_LLVM_SUPPORTED_TARGET_AArch64 diff --git a/driver/dcomputecodegenerator.h b/driver/dcomputecodegenerator.h index 09122faa596..42126830b0c 100644 --- a/driver/dcomputecodegenerator.h +++ b/driver/dcomputecodegenerator.h @@ -24,7 +24,7 @@ struct TargetABI; class DComputeCodeGenManager { llvm::LLVMContext &ctx; - llvm::SmallVector targets; + llvm::SmallVector targets; DComputeTarget *createComputeTarget(const std::string &s); IRState *oldGIR = nullptr; llvm::TargetMachine *oldGTargetMachine = nullptr; diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index ab875c40d2e..b714b539854 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -11,8 +11,6 @@ #include "dmd/argtypes.h" #include "dmd/expression.h" -#include "dmd/id.h" -#include "dmd/identifier.h" #include "dmd/target.h" #include "gen/abi/targets.h" #include "gen/abi/generic.h" @@ -22,11 +20,9 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/tollvm.h" -#include "dmd/globals.h" #include "ir/irfunction.h" #include "ir/irfuncty.h" -#include "llvm/TargetParser/Triple.h" -#include +// #include using namespace dmd; diff --git a/gen/abi/metal.cpp b/gen/abi/metal.cpp index 1e10b9b72e2..6035d651e48 100644 --- a/gen/abi/metal.cpp +++ b/gen/abi/metal.cpp @@ -7,17 +7,11 @@ // //===----------------------------------------------------------------------===// -#include "dmd/identifier.h" -#include "dmd/nspace.h" #include "gen/abi/abi.h" -#include "gen/abi/generic.h" #include "gen/dcompute/druntime.h" -#include "gen/logger.h" -#include "gen/uda.h" -#include "ir/irfuncty.h" #include "gen/dcompute/abi-rewrites.h" -#include "mtype.h" -#include "llvm/Support/raw_ostream.h" +#include "ir/irfuncty.h" +#include "dmd/mtype.h" #include @@ -64,4 +58,4 @@ struct MetalABI : TargetABI { } }; -auto createMetalABI() -> TargetABI* { return new MetalABI(); } +TargetABI* createMetalABI() { return new MetalABI(); } diff --git a/gen/abi/targets.h b/gen/abi/targets.h index ecede93331d..afc6c64fe18 100644 --- a/gen/abi/targets.h +++ b/gen/abi/targets.h @@ -41,4 +41,4 @@ TargetABI *getLoongArch64TargetABI(); TargetABI *getWasmTargetABI(); -auto createMetalABI() -> TargetABI*; \ No newline at end of file +TargetABI* createMetalABI(); diff --git a/gen/dcompute/druntime.h b/gen/dcompute/druntime.h index 5f41d101255..673eb24951f 100644 --- a/gen/dcompute/druntime.h +++ b/gen/dcompute/druntime.h @@ -38,4 +38,4 @@ struct DcomputePointer { return LLPointerType::get(getGlobalContext(), as); } }; -auto toDcomputePointer(StructDeclaration *sd) -> std::optional; +std::optional toDcomputePointer(StructDeclaration *sd); diff --git a/gen/dcompute/target.cpp b/gen/dcompute/target.cpp index 3825c8b7f82..5b069580595 100644 --- a/gen/dcompute/target.cpp +++ b/gen/dcompute/target.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX +#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX || LDC_LLVM_SUPPORTED_TARGET_AArch64 #include "dmd/dsymbol.h" #include "dmd/errors.h" diff --git a/gen/dcompute/target.h b/gen/dcompute/target.h index 22c90529b86..ac958c4c1f9 100644 --- a/gen/dcompute/target.h +++ b/gen/dcompute/target.h @@ -65,7 +65,7 @@ DComputeTarget *createCUDATarget(llvm::LLVMContext &c, int sm); #endif #if LDC_LLVM_SUPPORTED_TARGET_AArch64 -auto createMetalTarget(llvm::LLVMContext &c, int version) -> DComputeTarget *; +DComputeTarget* createMetalTarget(llvm::LLVMContext &c, int version); #endif #if LDC_LLVM_SUPPORTED_TARGET_SPIRV diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index c2834a24475..eafc43bb4c2 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -8,175 +8,173 @@ //===----------------------------------------------------------------------===// #if LDC_LLVM_SUPPORTED_TARGET_AArch64 -#include "declaration.h" #include "gen/dcompute/druntime.h" -#include "mtype.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Metadata.h" -#include "llvm/Support/raw_ostream.h" -#include -#include -#include -#include -#include "dmd/identifier.h" #include "gen/dcompute/target.h" #include "gen/abi/targets.h" -#include "gen/logger.h" -#include "gen/optimizer.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Transforms/Scalar.h" -#include "driver/targetmachine.h" -#include +#include "dmd/identifier.h" namespace { class TargetMetal : public DComputeTarget { public: TargetMetal(llvm::LLVMContext &c, int version) - : DComputeTarget( - c, version, ID::Metal, "metal", "air", createMetalABI(), + : DComputeTarget(c, version, ID::Metal, "metal", "air", createMetalABI(), - // DCompute Order: [Private, Global, Shared, Constant, Generic] - // AIR equivalents: Private=0, Device/Global=1, Threadgroup/Shared=3, Constant=2 - {{0, 1, 3, 2, 0}}) { - const bool is64 = global.params.targetTriple->isArch64Bit(); + // DCompute Order: [Private, Global, Shared, Constant, + // Generic] AIR equivalents: Private=0, Device/Global=1, + // Threadgroup/Shared=3, Constant=2 + {{0, 1, 3, 2, 0}}) { _ir = new IRState("dcomputeTargetMetal", ctx); - // TODO: need to find 32-bit triple llvm::StringRef tripleString = "air64_v28-apple-macosx26.0.0"; - #if LLVM_VERSION_MAJOR>= 21 - _ir->module.setTargetTriple(llvm::Triple(tripleString)); - #else - _ir->module.setTargetTriple(tripleString); - #endif - - llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64" - "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-" - "v512:512:512-v1024:1024:1024-n8:16:32"; - - _ir->module.setDataLayout(is64 ? dataLayout: /* TODO: need to find 32-bit data layout */dataLayout); - _ir->dcomputetarget = this; +#if LLVM_VERSION_MAJOR >= 21 + _ir->module.setTargetTriple(llvm::Triple(tripleString)); +#else + _ir->module.setTargetTriple(tripleString); +#endif + + llvm::StringRef dataLayout = + "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-" + "f64:64:64" + "-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:" + "128:128-v192:256:256-v256:256:256-" + "v512:512:512-v1024:1024:1024-n8:16:32"; + + _ir->module.setDataLayout(dataLayout); + _ir->dcomputetarget = this; } void addMetadata() override { - llvm::NamedMDNode *airVersion = _ir->module.getOrInsertNamedMetadata("air.version"); - llvm::Metadata *major = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 2)); - llvm::Metadata *minor = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 8)); - llvm::Metadata *patch = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0)); - std::array arr = {major, minor, patch}; + llvm::NamedMDNode *airVersion = + _ir->module.getOrInsertNamedMetadata("air.version"); + llvm::Metadata *major = metaInt(2); + llvm::Metadata *minor = metaInt(8); + llvm::Metadata *patch = metaInt(0); + + std::array arr = {major, minor, patch}; airVersion->addOperand(llvm::MDTuple::get(ctx, arr)); - llvm::NamedMDNode *airLangVersion = _ir->module.getOrInsertNamedMetadata("air.language_version"); - std::array langArr = { - llvm::MDString::get(ctx, "Metal"), - llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 4)), - llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0)), - llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0)) + llvm::NamedMDNode *airLangVersion = + _ir->module.getOrInsertNamedMetadata("air.language_version"); + std::array langArr = { + metaString("Metal"), + metaInt(4), + metaInt(0), + metaInt(0), }; + airLangVersion->addOperand(llvm::MDTuple::get(ctx, langArr)); } - void addKernelMetadata(FuncDeclaration *df, llvm::Function *llf, StructLiteralExp *_unused_) override { - llvm::NamedMDNode *kernels = _ir->module.getOrInsertNamedMetadata("air.kernel"); + void addKernelMetadata(FuncDeclaration *df, llvm::Function *llf, + StructLiteralExp *_unused_) override { + llvm::NamedMDNode *kernels = + _ir->module.getOrInsertNamedMetadata("air.kernel"); std::vector kernelMetadataArguments; kernelMetadataArguments.push_back(llvm::ConstantAsMetadata::get(llf)); // XXX: unknown, not sure why we need this, Metal backend expects it - kernelMetadataArguments.push_back( - llvm::MDNode::get(ctx, {}) - ); + kernelMetadataArguments.push_back(llvm::MDNode::get(ctx, {})); - std::vector argumentMetadata = addArgumentMetadata(df, llf); + std::vector argumentMetadata = + addArgumentMetadata(df, llf); - kernelMetadataArguments.push_back( - llvm::MDNode::get(ctx, argumentMetadata) - ); + kernelMetadataArguments.push_back(llvm::MDNode::get(ctx, argumentMetadata)); - llvm::MDTuple *kernelTuple = llvm::MDTuple::get(ctx, kernelMetadataArguments); + llvm::MDTuple *kernelTuple = + llvm::MDTuple::get(ctx, kernelMetadataArguments); kernels->addOperand(kernelTuple); } - auto addArgumentMetadata(FuncDeclaration *df, llvm::Function *llf) -> std::vector { + auto addArgumentMetadata(FuncDeclaration *df, llvm::Function *llf) + -> std::vector { std::vector kernelMetadataArguments; int locationIndex = 0; - for(auto &arg: llf->args()) { - std::vector argumentMetadata; + for (auto &arg : llf->args()) { + std::vector argumentMetadata; + + argumentMetadata.push_back( + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( + llvm::IntegerType::get(ctx, 32), locationIndex))); + + argumentMetadata.push_back(metaString("air.buffer")); + argumentMetadata.push_back( + metaString("air.location_index")); + argumentMetadata.push_back( + metaInt(locationIndex)); + + // XXX: unknown, not sure why we need this, Metal backend expects it + argumentMetadata.push_back(metaInt(1)); + argumentMetadata.push_back(metaString("air.read_write")); + + argumentMetadata.push_back(metaString("air.address_space")); + + if (arg.getType()->isPointerTy()) { + unsigned addressSpace = arg.getType()->getPointerAddressSpace(); argumentMetadata.push_back( - llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get( - llvm::IntegerType::get(ctx, 32), locationIndex))); - - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.buffer")); - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.location_index")); - argumentMetadata.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), locationIndex) - )); - - // XXX: unknown, not sure why we need this, Metal backend expects it - argumentMetadata.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 1) - )); - - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.read_write")); - - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.address_space")); - - if (arg.getType()->isPointerTy()){ - unsigned addressSpace = arg.getType()->getPointerAddressSpace(); - argumentMetadata.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), addressSpace) - )); - } else { - argumentMetadata.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), 0) - )); - } - - VarDeclaration *vd = (*df->parameters)[locationIndex]; - addArgumentTypeInformation(vd, argumentMetadata); - - if (!argumentMetadata.empty()) { - kernelMetadataArguments.push_back(llvm::MDTuple::get(ctx, argumentMetadata)); - } - - locationIndex++; + metaInt(addressSpace)); + } else { + // XXX: 0 - generic address space + argumentMetadata.push_back(metaInt(0)); + } + + VarDeclaration *vd = (*df->parameters)[locationIndex]; + addArgumentTypeInformation(vd, argumentMetadata); + + if (!argumentMetadata.empty()) { + kernelMetadataArguments.push_back( + llvm::MDTuple::get(ctx, argumentMetadata)); + } + + locationIndex++; } return kernelMetadataArguments; } - void addArgumentTypeInformation(VarDeclaration *vd, std::vector &argumentMetadata) { + void + addArgumentTypeInformation(VarDeclaration *vd, + std::vector &argumentMetadata) { Type *type = nullptr; std::optional ptr; - if (vd->type->ty == TY::Tstruct && (ptr = toDcomputePointer(static_cast(vd->type)->sym))){ + if (vd->type->ty == TY::Tstruct && + (ptr = toDcomputePointer(static_cast(vd->type)->sym))) { type = ptr->type; } else { type = vd->type; } - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_type_size")); - argumentMetadata.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), dmd::size(type, vd->loc)) - )); + argumentMetadata.push_back(metaString("air.arg_type_size")); + argumentMetadata.push_back( + metaInt(dmd::size(type, vd->loc))); - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_type_align_size")); - argumentMetadata.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), type->alignsize()) - )); + argumentMetadata.push_back( + metaString("air.arg_type_align_size")) + ; + argumentMetadata.push_back( + metaInt(type->alignsize())); - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_type_name")); + argumentMetadata.push_back(metaString("air.arg_type_name")); // TODO: check if using char needed instead of int8 as in ocl target implementation - argumentMetadata.push_back(llvm::MDString::get(ctx, basicTypeToString(type))); + argumentMetadata.push_back( + metaString(basicTypeToString(type))); - argumentMetadata.push_back(llvm::MDString::get(ctx, "air.arg_name")); - argumentMetadata.push_back(llvm::MDString::get(ctx, vd->ident->toChars())); + argumentMetadata.push_back(metaString("air.arg_name")); + argumentMetadata.push_back(metaString(vd->ident->toChars())); + } + + llvm::Metadata *metaInt(int n) { + return llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::IntegerType::get(ctx, 32), n) + ); + } + + llvm::Metadata *metaString(llvm::StringRef s) { + return llvm::MDString::get(ctx, s); } auto basicTypeToString(Type *t) -> std::string { @@ -192,7 +190,6 @@ class TargetMetal : public DComputeTarget { return ss.str(); } - }; } // anonymous namespace. diff --git a/gen/target.cpp b/gen/target.cpp index 16622a99227..e6453c9aac7 100644 --- a/gen/target.cpp +++ b/gen/target.cpp @@ -401,7 +401,7 @@ Expression *Target::getTargetInfo(const char *name_, Loc loc) { return IntegerExp::create(loc, static_cast(cet), Type::tint32); } -#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX +#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX || LDC_LLVM_SUPPORTED_TARGET_AArch64 if (name == "dcomputeTargets") { Expressions* exps = createExpressions(); for (auto &targ : opts::dcomputeTargets) { From 828aab8faafc72361fd6b9da5478e3b76a17b530 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Wed, 29 Apr 2026 16:13:38 +0200 Subject: [PATCH 11/30] Remove unused includes --- gen/abi/abi.cpp | 1 - gen/dcompute/abi-rewrites.h | 8 ------- gen/dcompute/targetMetal.cpp | 34 ++++++++++++++++------------- runtime/druntime/src/ldc/dcompute.d | 31 +++++++++++++------------- 4 files changed, 34 insertions(+), 40 deletions(-) diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index b714b539854..8c845ee82af 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -22,7 +22,6 @@ #include "gen/tollvm.h" #include "ir/irfunction.h" #include "ir/irfuncty.h" -// #include using namespace dmd; diff --git a/gen/dcompute/abi-rewrites.h b/gen/dcompute/abi-rewrites.h index bcfde8b9e13..0a159b67633 100644 --- a/gen/dcompute/abi-rewrites.h +++ b/gen/dcompute/abi-rewrites.h @@ -13,16 +13,8 @@ #pragma once -#include "gen/abi/abi.h" #include "gen/abi/generic.h" #include "gen/dcompute/druntime.h" -#include "gen/dvalue.h" -#include "gen/irstate.h" -#include "gen/llvm.h" -#include "gen/tollvm.h" -#include "llvm/IR/AssemblyAnnotationWriter.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/Support/raw_ostream.h" struct DComputePointerRewrite : ABIRewrite { LLValue *put(DValue *v, bool isLValueExp, bool) override { diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index eafc43bb4c2..e81f38c8617 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -97,21 +97,23 @@ class TargetMetal : public DComputeTarget { std::vector argumentMetadata; argumentMetadata.push_back( - llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( - llvm::IntegerType::get(ctx, 32), locationIndex))); + metaInt(locationIndex)); - argumentMetadata.push_back(metaString("air.buffer")); + argumentMetadata.push_back( + metaString("air.buffer")); argumentMetadata.push_back( metaString("air.location_index")); argumentMetadata.push_back( metaInt(locationIndex)); // XXX: unknown, not sure why we need this, Metal backend expects it - argumentMetadata.push_back(metaInt(1)); - - argumentMetadata.push_back(metaString("air.read_write")); + argumentMetadata.push_back( + metaInt(1)); - argumentMetadata.push_back(metaString("air.address_space")); + argumentMetadata.push_back( + metaString("air.read_write")); + argumentMetadata.push_back( + metaString("air.address_space")); if (arg.getType()->isPointerTy()) { unsigned addressSpace = arg.getType()->getPointerAddressSpace(); @@ -136,8 +138,7 @@ class TargetMetal : public DComputeTarget { return kernelMetadataArguments; } - void - addArgumentTypeInformation(VarDeclaration *vd, + void addArgumentTypeInformation(VarDeclaration *vd, std::vector &argumentMetadata) { Type *type = nullptr; std::optional ptr; @@ -148,23 +149,26 @@ class TargetMetal : public DComputeTarget { type = vd->type; } - argumentMetadata.push_back(metaString("air.arg_type_size")); + argumentMetadata.push_back( + metaString("air.arg_type_size")); argumentMetadata.push_back( metaInt(dmd::size(type, vd->loc))); argumentMetadata.push_back( - metaString("air.arg_type_align_size")) - ; + metaString("air.arg_type_align_size")); argumentMetadata.push_back( metaInt(type->alignsize())); - argumentMetadata.push_back(metaString("air.arg_type_name")); + argumentMetadata.push_back( + metaString("air.arg_type_name")); // TODO: check if using char needed instead of int8 as in ocl target implementation argumentMetadata.push_back( metaString(basicTypeToString(type))); - argumentMetadata.push_back(metaString("air.arg_name")); - argumentMetadata.push_back(metaString(vd->ident->toChars())); + argumentMetadata.push_back( + metaString("air.arg_name")); + argumentMetadata.push_back( + metaString(vd->ident->toChars())); } llvm::Metadata *metaInt(int n) { diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index 6ddc38f43be..1ed33e5dc96 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -25,13 +25,14 @@ enum ReflectTarget : uint * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y * use 0 as a wildcard to match any version. + * This is mostly used for selecting the correct intrinsic for the * given target and version, but could also be used to tailor for * performance characteristics. See dcompute.std.index for an example * of how to select intrinsics. */ pure nothrow @nogc -extern (C) bool __dcompute_reflect(ReflectTarget t, uint _version = 0); +extern(C) bool __dcompute_reflect(ReflectTarget t, uint _version = 0); ///Readability aliases for compute enum CompileFor : int @@ -71,12 +72,10 @@ struct compute + } + --- +/ -private struct _kernel -{ +private struct _kernel { size_t[3] bounds; } - -_kernel kernel(size_t[3] a = [1, 1, 1]) => _kernel(a); +_kernel kernel(size_t[3] a = [1,1,1]) => _kernel(a); /++ + DCompute has the notion of adress spaces, provide by the magic structs below. @@ -105,18 +104,18 @@ struct Variable(AddrSpace as, T) enum AddrSpace : uint { - Private = 0, - Global = 1, - Shared = 2, + Private = 0, + Global = 1, + Shared = 2, Constant = 3, - Generic = 4, + Generic = 4, } -alias PrivatePointer(T) = Pointer!(AddrSpace.Private, T); -alias GlobalPointer(T) = Pointer!(AddrSpace.Global, T); -alias SharedPointer(T) = Pointer!(AddrSpace.Shared, T); +alias PrivatePointer(T) = Pointer!(AddrSpace.Private, T); +alias GlobalPointer(T) = Pointer!(AddrSpace.Global, T); +alias SharedPointer(T) = Pointer!(AddrSpace.Shared, T); alias ConstantPointer(T) = Pointer!(AddrSpace.Constant, immutable(T)); -alias GenericPointer(T) = Pointer!(AddrSpace.Generic, T); +alias GenericPointer(T) = Pointer!(AddrSpace.Generic, T); // N.B private variables are declared on the stack and so cannot be declared // at module scope. @@ -125,6 +124,6 @@ alias GenericPointer(T) = Pointer!(AddrSpace.Generic, T); // // The __gshared below does not work. It is kludged into place in `DtoResolveVariable` -alias Global(T) = /*__gshared*/ Variable!(AddrSpace.Global, T); -alias Shared(T) = shared Variable!(AddrSpace.Shared, T); -alias Constant(T) = immutable Variable!(AddrSpace.Constant, T); +alias Global(T) = /*__gshared*/ Variable!(AddrSpace.Global, T); +alias Shared(T) = shared Variable!(AddrSpace.Shared, T); +alias Constant(T) = immutable Variable!(AddrSpace.Constant, T); From 74076a9787b2cb74fcbba785e93b80426701a33d Mon Sep 17 00:00:00 2001 From: Asadbek Date: Wed, 29 Apr 2026 16:21:32 +0200 Subject: [PATCH 12/30] Add falltrough comment and remove unnecessary change --- driver/dcomputecodegenerator.cpp | 1 - gen/abi/abi.cpp | 6 +----- runtime/druntime/src/ldc/dcompute.d | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index ec902488259..3d18cdbb6a3 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -51,7 +51,6 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } - if (s.substr(0, 5) == "cuda-") { #if LDC_LLVM_SUPPORTED_TARGET_NVPTX #define CUDA_VALID_VER_INIT 100, 110, 120, 130, 200, 210, 300, 350, 370,\ diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index 8c845ee82af..d4eceb50b2d 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -304,11 +304,7 @@ TargetABI *TargetABI::getTarget() { if (global.params.targetTriple->getArchName() == "air64") { return createMetalABI(); } - - warning(Loc(), - "unknown target ABI, falling back to generic implementation. C/C++ " - "interop will almost certainly NOT work."); - return new TargetABI; + // fallthrough default: warning(Loc(), diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index 1ed33e5dc96..a8d8394ac1e 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -25,7 +25,6 @@ enum ReflectTarget : uint * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y * use 0 as a wildcard to match any version. - * This is mostly used for selecting the correct intrinsic for the * given target and version, but could also be used to tailor for * performance characteristics. See dcompute.std.index for an example From 7665f5ce90288ffb969cf515dbd560f9c0422759 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Thu, 30 Apr 2026 15:19:06 +0200 Subject: [PATCH 13/30] Remove unused include and whitespace --- driver/dcomputecodegenerator.cpp | 1 - gen/dcompute/abi-rewrites.h | 1 - gen/dcompute/targetMetal.cpp | 2 +- runtime/druntime/src/ldc/dcompute.d | 2 ++ 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index 3d18cdbb6a3..0e3423e1c94 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -67,7 +67,6 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } - #define STR(...) #__VA_ARGS__ #define XSTR(x) STR(x) diff --git a/gen/dcompute/abi-rewrites.h b/gen/dcompute/abi-rewrites.h index 0a159b67633..f3b2292179f 100644 --- a/gen/dcompute/abi-rewrites.h +++ b/gen/dcompute/abi-rewrites.h @@ -14,7 +14,6 @@ #pragma once #include "gen/abi/generic.h" -#include "gen/dcompute/druntime.h" struct DComputePointerRewrite : ABIRewrite { LLValue *put(DValue *v, bool isLValueExp, bool) override { diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index e81f38c8617..751f805585d 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -197,7 +197,7 @@ class TargetMetal : public DComputeTarget { }; } // anonymous namespace. -auto createMetalTarget(llvm::LLVMContext &c, int version) -> DComputeTarget * { +DComputeTarget* createMetalTarget(llvm::LLVMContext &c, int version) { return new TargetMetal(c, version); }; diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index a8d8394ac1e..bde43b44237 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -24,7 +24,9 @@ enum ReflectTarget : uint * arguments MUST be compiletime constants * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y + * and for Metal is 4.0.0 * use 0 as a wildcard to match any version. + * This is mostly used for selecting the correct intrinsic for the * given target and version, but could also be used to tailor for * performance characteristics. See dcompute.std.index for an example From b060909beee8289b5af11f172e83572cf2ec2d52 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Thu, 30 Apr 2026 15:54:01 +0200 Subject: [PATCH 14/30] Update command line option for metal dcompute target --- driver/dcomputecodegenerator.cpp | 16 +++++++++++++--- gen/dcompute/targetMetal.cpp | 7 ++++--- runtime/druntime/src/ldc/dcompute.d | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index 0e3423e1c94..4a86ff03753 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -43,9 +43,15 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } - if (s.substr(0, 6) == "air64-") { + if (s.substr(0, 6) == "metal-") { #if LDC_LLVM_SUPPORTED_TARGET_AArch64 //&& LDC_LLVM_VER >= 2100 - return createMetalTarget(ctx, 64); +#define METAL_VALID_VER_INIT 400 + const std::array valid_metal_versions = {{METAL_VALID_VER_INIT}}; + const int v = atoi(s.c_str() + 6); + if (std::find(valid_metal_versions.begin(), valid_metal_versions.end(), v) != + valid_metal_versions.end()) { + return createMetalTarget(ctx, v); + } #else error(Loc(), "LDC was not built with Apple Metal Dcompute support!"); #endif @@ -72,14 +78,18 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { error(Loc(), "Unrecognised or invalid DCompute targets: the format is ocl-xy0 " - "for OpenCl x.y and cuda-xy0 for CUDA CC x.y." + "for OpenCl x.y and cuda-xy0 for CUDA CC x.y and metal-xy0 for Metal x.y." #if LDC_LLVM_SUPPORTED_TARGET_SPIRV " Valid version strings for OpenCl are ocl-{" XSTR(OCL_VALID_VER_INIT) "}." #endif #if LDC_LLVM_SUPPORTED_TARGET_NVPTX " Valid version strings for CUDA are cuda-{" XSTR(CUDA_VALID_VER_INIT) "}." +#endif +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 + "Valid version strings for Metal are metal-{" XSTR(METAL_VALID_VER_INIT) "}" #endif ); + #undef XSTR #undef STR diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 751f805585d..c8312e39c38 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -56,11 +56,12 @@ class TargetMetal : public DComputeTarget { llvm::NamedMDNode *airLangVersion = _ir->module.getOrInsertNamedMetadata("air.language_version"); + std::array langArr = { metaString("Metal"), - metaInt(4), - metaInt(0), - metaInt(0), + metaInt(tversion / 100), + metaInt((tversion / 10) % 10), + metaInt(tversion % 10), }; airLangVersion->addOperand(llvm::MDTuple::get(ctx, langArr)); diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index bde43b44237..4f47dd38896 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -24,7 +24,7 @@ enum ReflectTarget : uint * arguments MUST be compiletime constants * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y - * and for Metal is 4.0.0 + * and for Metal is 400 * use 0 as a wildcard to match any version. * This is mostly used for selecting the correct intrinsic for the From ebc302b191363bb1c46bbc2523a1a30200f64513 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Thu, 30 Apr 2026 15:55:02 +0200 Subject: [PATCH 15/30] Update commented doc about metal dcompute target version --- runtime/druntime/src/ldc/dcompute.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index 4f47dd38896..ec807b58041 100644 --- a/runtime/druntime/src/ldc/dcompute.d +++ b/runtime/druntime/src/ldc/dcompute.d @@ -24,7 +24,7 @@ enum ReflectTarget : uint * arguments MUST be compiletime constants * valid values of _version are for OpenCL 100 110 120 200 210 * and for CUDA are x*100 + y*10 for x any valid values of sm x.y - * and for Metal is 400 + * and 400 for Metal 4.0.0 * use 0 as a wildcard to match any version. * This is mostly used for selecting the correct intrinsic for the From 330735febe0109ae28dfac4b0f1558bd92c19205 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sun, 3 May 2026 00:03:03 +0200 Subject: [PATCH 16/30] Add codegen for metallib through xcrun -sdk macosx metallib terminal linker --- dmd/globals.h | 1 + driver/targetmachine.cpp | 10 +++++-- driver/targetmachine.h | 2 +- driver/toobj.cpp | 59 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/dmd/globals.h b/dmd/globals.h index 2f85ea88343..a954754696a 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -403,6 +403,7 @@ const DString ll_ext = "ll"; const DString mlir_ext = "mlir"; const DString bc_ext = "bc"; const DString s_ext = "s"; +const DString metallib_ext = "metallib"; #endif struct CompileEnv diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 3a1ff377127..5191b4f70ad 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -666,6 +666,12 @@ ComputeBackend::Type getComputeTargetType(llvm::Module* m) { return ComputeBackend::SPIRV; else if (a == llvm::Triple::nvptx || a == llvm::Triple::nvptx64) return ComputeBackend::NVPTX; - else - return ComputeBackend::None; + + llvm::StringRef tripleString = m->getTargetTriple(); + + + if (tripleString.starts_with("air64")) + return ComputeBackend::METAL; + + return ComputeBackend::None; } diff --git a/driver/targetmachine.h b/driver/targetmachine.h index e0c21ee1407..bf5c97625ea 100644 --- a/driver/targetmachine.h +++ b/driver/targetmachine.h @@ -41,7 +41,7 @@ class Module; } namespace ComputeBackend { -enum Type { None, SPIRV, NVPTX }; +enum Type { None, SPIRV, NVPTX, METAL }; } ComputeBackend::Type getComputeTargetType(llvm::Module*); diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 211d8d88f46..4ee92725abb 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -38,7 +38,6 @@ #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/IR/Module.h" #include -#include namespace llvm { namespace codegen { @@ -73,6 +72,62 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #endif } +#ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 + if (cb == ComputeBackend::METAL) { + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println("Aborting because of errors/warnings during LLVM passes"); + fatal(); + } + + { + std::error_code errinfo; + llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); + if (errinfo) { + error(Loc(), "cannot write file '%s': %s", filename, + errinfo.message().c_str()); + fatal(); + } + + llvm::WriteBitcodeToFile(m, out.os()); + + out.keep(); + } + + uint64_t fileSize = 0; + llvm::sys::fs::file_size(filename, fileSize); + + auto xcrunpath = llvm::sys::findProgramByName("xcrun"); + if (!xcrunpath) { + error(Loc(), "xcrun not found - XCode should be installed first!"); + fatal(); + } + + llvm::SmallString<256> metallibOutPath; + llvm::sys::fs::current_path(metallibOutPath); + llvm::sys::path::append(metallibOutPath, llvm::sys::path::filename(filename)); + llvm::sys::path::replace_extension(metallibOutPath, "metallib"); + + + std::vector args = { + xcrunpath.get(), "-sdk", "macosx", "metallib", filename, "-o", metallibOutPath.c_str() + }; + + std::string errorMsg; + + int status = executeToolAndWait(Loc(), args[0], args); + + if (status < 0) { + error(Loc(), "program received signal %d (%s)", -status, + strsignal(-status)); + fatal(); + } + + return; + } +#endif + std::error_code errinfo; llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); if (errinfo) { @@ -468,5 +523,5 @@ void writeModule(llvm::Module *m, const char *filename) { if (useIR2ObjCache) { cache::cacheObjectFile(filename, moduleHash); } - } + } } From d059c1cf8cf48e4143dba99134c4c67de7ce2ed0 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sun, 3 May 2026 00:07:49 +0200 Subject: [PATCH 17/30] Move the error handling to line after writing the bitcode --- driver/toobj.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 4ee92725abb..9d4c005dd96 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -74,13 +74,6 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 if (cb == ComputeBackend::METAL) { - - // Terminate upon errors during the LLVM passes. - if (global.errors || global.warnings) { - Logger::println("Aborting because of errors/warnings during LLVM passes"); - fatal(); - } - { std::error_code errinfo; llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); @@ -93,6 +86,12 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, llvm::WriteBitcodeToFile(m, out.os()); out.keep(); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + Logger::println("Aborting because of errors/warnings during LLVM passes"); + fatal(); + } } uint64_t fileSize = 0; From 8d6da31bceccba30eed8bf2ab02ef20c1517e8ba Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sun, 3 May 2026 00:09:10 +0200 Subject: [PATCH 18/30] remove empty spaces and unused variables --- driver/targetmachine.cpp | 1 - driver/toobj.cpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 5191b4f70ad..740b7536f18 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -668,7 +668,6 @@ ComputeBackend::Type getComputeTargetType(llvm::Module* m) { return ComputeBackend::NVPTX; llvm::StringRef tripleString = m->getTargetTriple(); - if (tripleString.starts_with("air64")) return ComputeBackend::METAL; diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 9d4c005dd96..d0d2d4916b4 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -94,9 +94,6 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, } } - uint64_t fileSize = 0; - llvm::sys::fs::file_size(filename, fileSize); - auto xcrunpath = llvm::sys::findProgramByName("xcrun"); if (!xcrunpath) { error(Loc(), "xcrun not found - XCode should be installed first!"); From 63300d099d201b83faa26066ced6776465887881 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Sun, 3 May 2026 00:13:21 +0200 Subject: [PATCH 19/30] remove unnecessary whitespaces --- driver/toobj.cpp | 1 - gen/abi/metal.cpp | 1 - gen/dcompute/abi-rewrites.h | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/driver/toobj.cpp b/driver/toobj.cpp index d0d2d4916b4..a81ac219e4b 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -105,7 +105,6 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, llvm::sys::path::append(metallibOutPath, llvm::sys::path::filename(filename)); llvm::sys::path::replace_extension(metallibOutPath, "metallib"); - std::vector args = { xcrunpath.get(), "-sdk", "macosx", "metallib", filename, "-o", metallibOutPath.c_str() }; diff --git a/gen/abi/metal.cpp b/gen/abi/metal.cpp index 6035d651e48..904f3e88082 100644 --- a/gen/abi/metal.cpp +++ b/gen/abi/metal.cpp @@ -14,7 +14,6 @@ #include "dmd/mtype.h" #include - using namespace dmd; struct MetalABI : TargetABI { diff --git a/gen/dcompute/abi-rewrites.h b/gen/dcompute/abi-rewrites.h index f3b2292179f..ead84da2875 100644 --- a/gen/dcompute/abi-rewrites.h +++ b/gen/dcompute/abi-rewrites.h @@ -35,7 +35,7 @@ struct DComputePointerRewrite : ABIRewrite { struct DcomputeMetalScalarRewrite : ABIRewrite { LLType *type(Type* t) override { - // XXXX: Scalar variables are stored in the constant memory space for Metal GPU + // XXX: Scalar variables are stored in the constant memory space for Metal GPU return llvm::PointerType::get(gIR->context(), 2/*Constant Memory space*/); } From bd882b7d397d926c72f8c28755277bd8d050e61b Mon Sep 17 00:00:00 2001 From: Asadbek Date: Mon, 11 May 2026 10:43:26 +0200 Subject: [PATCH 20/30] Fix CI error and add function inline pass for non-kernel functions --- dmd/globals.h | 1 - driver/targetmachine.cpp | 11 +++++--- driver/toobj.cpp | 55 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/dmd/globals.h b/dmd/globals.h index a954754696a..2f85ea88343 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -403,7 +403,6 @@ const DString ll_ext = "ll"; const DString mlir_ext = "mlir"; const DString bc_ext = "bc"; const DString s_ext = "s"; -const DString metallib_ext = "metallib"; #endif struct CompileEnv diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp index 740b7536f18..fd9d6d225b0 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -667,10 +667,15 @@ ComputeBackend::Type getComputeTargetType(llvm::Module* m) { else if (a == llvm::Triple::nvptx || a == llvm::Triple::nvptx64) return ComputeBackend::NVPTX; - llvm::StringRef tripleString = m->getTargetTriple(); - + +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 && LLVM_VERSION_MAJOR >= 21 + llvm::StringRef tripleString = m->getTargetTriple().str(); +#else + llvm::StringRef tripleString = m->getTargetTriple(); +#endif + + if (tripleString.starts_with("air64")) return ComputeBackend::METAL; - return ComputeBackend::None; } diff --git a/driver/toobj.cpp b/driver/toobj.cpp index a81ac219e4b..91b176f4cd3 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -35,6 +35,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/IR/Module.h" #include @@ -57,6 +58,40 @@ void runDLLImportRelocationPass(llvm::TargetMachine &Target, llvm::Module &m) { pm.run(m); } +void inlineDComputeKernelFunctions(llvm::Module *m) { + // Create a PassManager to hold and optimize the collection of passes we are + // about to build. + llvm::legacy::PassManager Passes; + + llvm::SmallPtrSet kernelFunctions; + + // Extract all the kernel functions + if (auto *kernelMetadata = m->getNamedMetadata("air.kernel")) { + for(auto *op: kernelMetadata->operands()) { + if (auto *F = llvm::mdconst::dyn_extract(op->getOperand(0))) { + kernelFunctions.insert(F); + } + } + } + + // Prepare non-kernel functions to be inlined + for(auto& F: *m) { + if (!F.isDeclaration() && !kernelFunctions.contains(&F)) { + F.addFnAttr(llvm::Attribute::AlwaysInline); + } + } + + Passes.add(llvm::createAlwaysInlinerLegacyPass()); + + Passes.run(*m); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + error(Loc(), "Aborting because of errors/warnings during LLVM passes"); + fatal(); + } +} + // based on llc code, University of Illinois Open Source License void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, const char *filename, @@ -75,6 +110,9 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 if (cb == ComputeBackend::METAL) { { + // Inline non-kernel functions for Metal dcompute target + inlineDComputeKernelFunctions(&m); + std::error_code errinfo; llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); if (errinfo) { @@ -82,14 +120,14 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, errinfo.message().c_str()); fatal(); } - + llvm::WriteBitcodeToFile(m, out.os()); out.keep(); // Terminate upon errors during the LLVM passes. if (global.errors || global.warnings) { - Logger::println("Aborting because of errors/warnings during LLVM passes"); + error(Loc(), "Aborting because of errors/warnings during LLVM passes!"); fatal(); } } @@ -104,7 +142,7 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, llvm::sys::fs::current_path(metallibOutPath); llvm::sys::path::append(metallibOutPath, llvm::sys::path::filename(filename)); llvm::sys::path::replace_extension(metallibOutPath, "metallib"); - + std::vector args = { xcrunpath.get(), "-sdk", "macosx", "metallib", filename, "-o", metallibOutPath.c_str() }; @@ -353,6 +391,15 @@ std::string replaceExtensionWith(const DArray &ext, } void writeModule(llvm::Module *m, const char *filename) { +// Inline non-kernel functions for Metal dcompute target +#ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 + const ComputeBackend::Type cb = getComputeTargetType(m); + + if (cb == ComputeBackend::METAL) { + inlineDComputeKernelFunctions(m); + } +#endif + const bool doLTO = opts::isUsingLTO(); const bool outputObj = shouldOutputObjectFile(); const bool assembleExternally = shouldAssembleExternally(); @@ -518,5 +565,5 @@ void writeModule(llvm::Module *m, const char *filename) { if (useIR2ObjCache) { cache::cacheObjectFile(filename, moduleHash); } - } + } } From 42771eb57eaa277e595874557646e103e4bbaaac Mon Sep 17 00:00:00 2001 From: Asadbek Date: Mon, 6 Jul 2026 23:07:01 +0200 Subject: [PATCH 21/30] Add predefined global dcompute versions for cuda, ocl, metal --- driver/main.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/driver/main.cpp b/driver/main.cpp index a01260fc1ce..02eb2a3e802 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -1017,6 +1017,28 @@ void registerPredefinedVersions() { } #endif + for(auto& dcomputeTarget: dcomputeTargets) { + auto targetInfo = llvm::StringRef(dcomputeTarget); + + #if LDC_LLVM_SUPPORTED_TARGET_NVPTX + if (targetInfo.starts_with("cuda")) { + VersionCondition::addPredefinedGlobalIdent("LDC_DCompute_CUDA"); + } + #endif + + #if LDC_LLVM_SUPPORTED_TARGET_SPIRV + if (targetInfo.starts_with("ocl")) { + VersionCondition::addPredefinedGlobalIdent("LDC_DCompute_OCL"); + } + #endif + + #if LDC_LLVM_SUPPORTED_TARGET_NVPTX + if (targetInfo.starts_with("metal")) { + VersionCondition::addPredefinedGlobalIdent("LDC_DCompute_METAL"); + } + #endif + } + if (global.params.ddoc.doOutput) { VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); } From a4a519ae6f242899fbd5a6dfd3f8c6508c5eb4a4 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Mon, 27 Jul 2026 16:33:04 +0200 Subject: [PATCH 22/30] Add test case for metal GPU kernel metadata --- gen/dcompute/targetMetal.cpp | 2 +- .../codegen/dcompute_metal_kernel_metadata.d | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/codegen/dcompute_metal_kernel_metadata.d diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index c8312e39c38..765c7afe4c7 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -162,7 +162,7 @@ class TargetMetal : public DComputeTarget { argumentMetadata.push_back( metaString("air.arg_type_name")); - // TODO: check if using char needed instead of int8 as in ocl target implementation + // TODO(asadbek): check if using char needed instead of int8 as in ocl target implementation argumentMetadata.push_back( metaString(basicTypeToString(type))); diff --git a/tests/codegen/dcompute_metal_kernel_metadata.d b/tests/codegen/dcompute_metal_kernel_metadata.d new file mode 100644 index 00000000000..ffce07b8d23 --- /dev/null +++ b/tests/codegen/dcompute_metal_kernel_metadata.d @@ -0,0 +1,30 @@ +// REQUIRES: target_AArch64 + +// COM: There are certain breaking changes in llvm upstream that are not yet supported by MSL compiler +// COM: hence it is required to run on past versions of llvm +// REQUIRES: llvm20 + +// RUN: %ldc -c -mdcompute-targets=metal-400 -output-ll -of=%t.ll %s +// RUN: FileCheck %s --check-prefix=AIR < kernels_metal400_64.air +@compute(CompileFor.deviceOnly) module kernels; +import ldc.dcompute; + +// AIR-LABEL: define {{.*}} @{{.*}}test_kernel{{.*}}( +@kernel() +void test_kernel(GlobalPointer!float data) { + data[0] = 42.0; +} + +// AIR-DAG: !air.kernel = !{[[KERNEL_LIST:![0-9]+]]} +// AIR-DAG: !air.version = !{[[AIR_VERSION:![0-9]+]]} +// AIR-DAG: !air.language_version = !{[[AIR_LANGUAGE_VERSION:![0-9]+]]} +// AIR-DAG: [[KERNEL_LIST]] = !{ptr @{{.*}}test_kernel{{.*}}, [[EMPTY:![0-9]+]], [[ARGS_ROOT:![0-9]+]]} + +// AIR-DAG: [[EMPTY]] = !{} + +// AIR-DAG: [[ARGS_ROOT]] = !{[[ARG_0:![0-9]+]]} + +// AIR-DAG: [[ARG_0]] = !{i32 0, !"air.buffer", !"air.location_index", i32 0, i32 1, !"air.read_write", !"air.address_space", i32 1, !"air.arg_type_size", i32 4, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"float", !"air.arg_name", !"data"} + +// AIR-DAG: [[AIR_VERSION]] = !{i32 2, i32 8, i32 0} +// AIR-DAG: [[AIR_LANGUAGE_VERSION]] = !{!"Metal", i32 4, i32 0, i32 0} From 3a643749b93071751a6eb8707a9fa564b1854bf0 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Mon, 27 Jul 2026 23:24:12 +0200 Subject: [PATCH 23/30] rebase and resolve conflicts --- .../dcompute_metal_argument_metadata.d | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/codegen/dcompute_metal_argument_metadata.d diff --git a/tests/codegen/dcompute_metal_argument_metadata.d b/tests/codegen/dcompute_metal_argument_metadata.d new file mode 100644 index 00000000000..af85ec11907 --- /dev/null +++ b/tests/codegen/dcompute_metal_argument_metadata.d @@ -0,0 +1,33 @@ +// REQUIRES: target_AArch64 +// REQUIRES: llvm20 + +// RUN: %ldc -c -mdcompute-targets=metal-400 -output-ll -of=%t.ll %s +// RUN: FileCheck %s --check-prefix=AIR < kernels_metal400_64.air + +@compute(CompileFor.deviceOnly) module kernels; + +import ldc.dcompute; + +// AIR-LABEL: define {{.*}} @{{.*}}test_multi_arg_kernel{{.*}}( +@kernel() +void test_multi_arg_kernel(GlobalPointer!float data, SharedPointer!float shared_data, float scalar_value) +{ + data[0] = scalar_value; + shared_data[0] = scalar_value; +} + +// AIR-DAG: !air.kernel = !{[[KERNEL:![0-9]+]]} + +// AIR-DAG: [[KERNEL]] = !{ptr @{{.*}}test_multi_arg_kernel{{.*}}, [[EMPTY:![0-9]+]], [[ARGS_ROOT:![0-9]+]]} + +// AIR-DAG: [[ARGS_ROOT]] = !{[[ARG_0:![0-9]+]], [[ARG_1:![0-9]+]], [[ARG_2:![0-9]+]]} + +// COM: expects address_space: 1 for global pointer +// AIR-DAG: [[ARG_0]] = !{i32 0, !"air.buffer", !"air.location_index", i32 0, i32 1, !"air.read_write", !"air.address_space", i32 1, !"air.arg_type_size", i32 4, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"float", !"air.arg_name", !"data"} + +// COM: expects address_space: 3 for shared pointer +// AIR-DAG: [[ARG_1]] = !{i32 1, !"air.buffer", !"air.location_index", i32 1, i32 1, !"air.read_write", !"air.address_space", i32 3, !"air.arg_type_size", i32 4, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"float", !"air.arg_name", !"shared_data"} + +// COM: expects address_space: 2 for scalar values +// AIR-DAG: [[ARG_2]] = !{i32 2, !"air.buffer", !"air.location_index", i32 2, i32 1, !"air.read_write", !"air.address_space", i32 2, !"air.arg_type_size", i32 4, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"float", !"air.arg_name", !"scalar_value"} + From 764172165b91d073ff7df9df541849aae61d10e6 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Mon, 27 Jul 2026 23:17:03 +0200 Subject: [PATCH 24/30] Add more tests --- .../codegen/dcompute_metal_kernel_metadata.d | 4 +- .../codegen/dcompute_metal_multiple_kernels.d | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/codegen/dcompute_metal_multiple_kernels.d diff --git a/tests/codegen/dcompute_metal_kernel_metadata.d b/tests/codegen/dcompute_metal_kernel_metadata.d index ffce07b8d23..3c84f2defbf 100644 --- a/tests/codegen/dcompute_metal_kernel_metadata.d +++ b/tests/codegen/dcompute_metal_kernel_metadata.d @@ -15,10 +15,10 @@ void test_kernel(GlobalPointer!float data) { data[0] = 42.0; } -// AIR-DAG: !air.kernel = !{[[KERNEL_LIST:![0-9]+]]} +// AIR-DAG: !air.kernel = !{[[KERNEL:![0-9]+]]} // AIR-DAG: !air.version = !{[[AIR_VERSION:![0-9]+]]} // AIR-DAG: !air.language_version = !{[[AIR_LANGUAGE_VERSION:![0-9]+]]} -// AIR-DAG: [[KERNEL_LIST]] = !{ptr @{{.*}}test_kernel{{.*}}, [[EMPTY:![0-9]+]], [[ARGS_ROOT:![0-9]+]]} +// AIR-DAG: [[KERNEL]] = !{ptr @{{.*}}test_kernel{{.*}}, [[EMPTY:![0-9]+]], [[ARGS_ROOT:![0-9]+]]} // AIR-DAG: [[EMPTY]] = !{} diff --git a/tests/codegen/dcompute_metal_multiple_kernels.d b/tests/codegen/dcompute_metal_multiple_kernels.d new file mode 100644 index 00000000000..184fa7cd80d --- /dev/null +++ b/tests/codegen/dcompute_metal_multiple_kernels.d @@ -0,0 +1,42 @@ +// REQUIRES: target_AArch64 +// REQUIRES: llvm20 + +// RUN: %ldc -c -mdcompute-targets=metal-400 -output-ll -of=%t.ll %s +// RUN: FileCheck %s --check-prefix=AIR < kernels_metal400_64.air + +@compute(CompileFor.deviceOnly) module kernels; + +import ldc.dcompute; + +// AIR-LABEL: define {{.*}} @{{.*}}test_kernel_1{{.*}}( +@kernel() +void test_kernel_1(GlobalPointer!float data, float scalar_value) +{ + data[0] = scalar_value + 1; + + data[1] += scalar_value; +} + + +// AIR-LABEL: define {{.*}} @{{.*}}test_kernel_2{{.*}}( +@kernel() +void test_kernel_2(GlobalPointer!float data) +{ + data[0] = 0.0; +} + +// AIR-DAG: !air.kernel = !{[[KERNEL_0:![0-9]+]], [[KERNEL_1:![0-9]+]]} + +// AIR-DAG: [[KERNEL_0]] = !{ptr @{{.*}}test_kernel_1{{.*}}, [[EMPTY:![0-9]+]], [[ARGS_ROOT_OF_KERNEL_0:![0-9]+]]} + +// AIR-DAG: [[KERNEL_1]] = !{ptr @{{.*}}test_kernel_2{{.*}}, [[EMPTY]], [[ARGS_ROOT_OF_KERNEL_1:![0-9]+]]} + +// AIR-DAG: [[EMPTY]] = !{} + +// AIR-DAG: [[ARGS_ROOT_OF_KERNEL_0]] = !{[[SHARED_DATA_ARG:![0-9]+]], [[SCALAR_ARG:![0-9]+]]} +// AIR-DAG: [[ARGS_ROOT_OF_KERNEL_1]] = !{[[SHARED_DATA_ARG]]} + +// AIR-DAG: [[SHARED_DATA_ARG]] = !{i32 0, !"air.buffer", !"air.location_index", i32 0, i32 1, !"air.read_write", !"air.address_space", i32 1, !"air.arg_type_size", i32 4, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"float", !"air.arg_name", !"data"} + +// AIR-DAG: [[SCALAR_ARG]] = !{i32 1, !"air.buffer", !"air.location_index", i32 1, i32 1, !"air.read_write", !"air.address_space", i32 2, !"air.arg_type_size", i32 4, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"float", !"air.arg_name", !"scalar_value"} + From a0f976d4af79be53da24cd658c9492e2814f0f28 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Tue, 28 Jul 2026 00:02:57 +0200 Subject: [PATCH 25/30] Fix build errors --- gen/abi/metal.cpp | 2 +- gen/dcompute/targetMetal.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gen/abi/metal.cpp b/gen/abi/metal.cpp index 904f3e88082..bb3196e1fb4 100644 --- a/gen/abi/metal.cpp +++ b/gen/abi/metal.cpp @@ -51,7 +51,7 @@ struct MetalABI : TargetABI { pointerRewite.applyTo(arg); } - if (ty->isScalar()) { + if (dmd::isScalar(ty)) { metalScalarRewrite.applyTo(arg); } } diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 765c7afe4c7..386532d9f97 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -6,6 +6,7 @@ // file for details. // //===----------------------------------------------------------------------===// +#include "mtype.h" #if LDC_LLVM_SUPPORTED_TARGET_AArch64 #include "gen/dcompute/druntime.h" @@ -158,7 +159,7 @@ class TargetMetal : public DComputeTarget { argumentMetadata.push_back( metaString("air.arg_type_align_size")); argumentMetadata.push_back( - metaInt(type->alignsize())); + metaInt(dmd::alignsize(type))); argumentMetadata.push_back( metaString("air.arg_type_name")); From 5687c2ad053ea684eb4797962c02096b4b730afc Mon Sep 17 00:00:00 2001 From: Asadbek Date: Tue, 28 Jul 2026 14:01:03 +0200 Subject: [PATCH 26/30] Fix build error --- driver/toobj.cpp | 1 + gen/dcompute/targetMetal.cpp | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 91b176f4cd3..c49fcb206c6 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -38,6 +38,7 @@ #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/IR/Module.h" +#include <_string.h> #include namespace llvm { diff --git a/gen/dcompute/targetMetal.cpp b/gen/dcompute/targetMetal.cpp index 386532d9f97..6d2f6632c46 100644 --- a/gen/dcompute/targetMetal.cpp +++ b/gen/dcompute/targetMetal.cpp @@ -6,9 +6,13 @@ // file for details. // //===----------------------------------------------------------------------===// -#include "mtype.h" +#include "llvm/Support/CodeGen.h" #if LDC_LLVM_SUPPORTED_TARGET_AArch64 +#include "driver/targetmachine.h" +#include "driver/targetmachine.h" +#include "gen/optimizer.h" +#include "mtype.h" #include "gen/dcompute/druntime.h" #include "gen/dcompute/target.h" #include "gen/abi/targets.h" @@ -26,7 +30,7 @@ class TargetMetal : public DComputeTarget { {{0, 1, 3, 2, 0}}) { _ir = new IRState("dcomputeTargetMetal", ctx); - llvm::StringRef tripleString = "air64_v28-apple-macosx26.0.0"; + std::string tripleString = "air64_v28-apple-macosx26.0.0"; #if LLVM_VERSION_MAJOR >= 21 _ir->module.setTargetTriple(llvm::Triple(tripleString)); @@ -34,6 +38,15 @@ class TargetMetal : public DComputeTarget { _ir->module.setTargetTriple(tripleString); #endif + auto floatABI = ::FloatABI::Hard; + + targetMachine = createTargetMachine( + tripleString, + "arm64", + "", {}, + ExplicitBitness::M64, floatABI, + llvm::Reloc::Static, llvm::CodeModel::Small, codeGenOptLevel(), false); + llvm::StringRef dataLayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-" "f64:64:64" From 99e347f7b0f19329a5b51f217746203586fad890 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Tue, 28 Jul 2026 14:12:56 +0200 Subject: [PATCH 27/30] Fix build error --- driver/toobj.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/driver/toobj.cpp b/driver/toobj.cpp index c49fcb206c6..df69ea5f135 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -38,7 +38,6 @@ #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/IR/Module.h" -#include <_string.h> #include namespace llvm { @@ -111,6 +110,7 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 if (cb == ComputeBackend::METAL) { { +#ifdef __APPLE__ // Inline non-kernel functions for Metal dcompute target inlineDComputeKernelFunctions(&m); @@ -158,6 +158,8 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, fatal(); } +#endif + return; } #endif From 2f4622716a062b80d1dfaf7ef46ac6ebef92fe60 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Tue, 28 Jul 2026 14:22:49 +0200 Subject: [PATCH 28/30] Fix build error --- driver/toobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/toobj.cpp b/driver/toobj.cpp index df69ea5f135..0b696d54836 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -109,8 +109,8 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 if (cb == ComputeBackend::METAL) { - { #ifdef __APPLE__ + { // Inline non-kernel functions for Metal dcompute target inlineDComputeKernelFunctions(&m); From 0d3b1ebc45583a4b6e896f2552288a02047210e6 Mon Sep 17 00:00:00 2001 From: Asadbek Date: Thu, 30 Jul 2026 14:19:47 +0200 Subject: [PATCH 29/30] Fix output llvm ir file name generation & remove unnecessary chunk of duplicate code --- driver/main.cpp | 22 ------------------- .../dcompute_metal_argument_metadata.d | 4 ++-- .../codegen/dcompute_metal_kernel_metadata.d | 4 ++-- .../codegen/dcompute_metal_multiple_kernels.d | 4 ++-- 4 files changed, 6 insertions(+), 28 deletions(-) diff --git a/driver/main.cpp b/driver/main.cpp index 02eb2a3e802..a01260fc1ce 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -1017,28 +1017,6 @@ void registerPredefinedVersions() { } #endif - for(auto& dcomputeTarget: dcomputeTargets) { - auto targetInfo = llvm::StringRef(dcomputeTarget); - - #if LDC_LLVM_SUPPORTED_TARGET_NVPTX - if (targetInfo.starts_with("cuda")) { - VersionCondition::addPredefinedGlobalIdent("LDC_DCompute_CUDA"); - } - #endif - - #if LDC_LLVM_SUPPORTED_TARGET_SPIRV - if (targetInfo.starts_with("ocl")) { - VersionCondition::addPredefinedGlobalIdent("LDC_DCompute_OCL"); - } - #endif - - #if LDC_LLVM_SUPPORTED_TARGET_NVPTX - if (targetInfo.starts_with("metal")) { - VersionCondition::addPredefinedGlobalIdent("LDC_DCompute_METAL"); - } - #endif - } - if (global.params.ddoc.doOutput) { VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); } diff --git a/tests/codegen/dcompute_metal_argument_metadata.d b/tests/codegen/dcompute_metal_argument_metadata.d index af85ec11907..d9bbb2e4afc 100644 --- a/tests/codegen/dcompute_metal_argument_metadata.d +++ b/tests/codegen/dcompute_metal_argument_metadata.d @@ -1,8 +1,8 @@ // REQUIRES: target_AArch64 // REQUIRES: llvm20 -// RUN: %ldc -c -mdcompute-targets=metal-400 -output-ll -of=%t.ll %s -// RUN: FileCheck %s --check-prefix=AIR < kernels_metal400_64.air +// RUN: %ldc -c -mdcompute-targets=metal-400 --mdcompute-file-prefix=argument_metadata -output-ll -of=%t.ll %s +// RUN: FileCheck %s --check-prefix=AIR < argument_metadata_metal400_64.air @compute(CompileFor.deviceOnly) module kernels; diff --git a/tests/codegen/dcompute_metal_kernel_metadata.d b/tests/codegen/dcompute_metal_kernel_metadata.d index 3c84f2defbf..3008c4ec7fc 100644 --- a/tests/codegen/dcompute_metal_kernel_metadata.d +++ b/tests/codegen/dcompute_metal_kernel_metadata.d @@ -4,8 +4,8 @@ // COM: hence it is required to run on past versions of llvm // REQUIRES: llvm20 -// RUN: %ldc -c -mdcompute-targets=metal-400 -output-ll -of=%t.ll %s -// RUN: FileCheck %s --check-prefix=AIR < kernels_metal400_64.air +// RUN: %ldc -c -mdcompute-targets=metal-400 --mdcompute-file-prefix=kernel_metadata -output-ll -of=%t.ll %s +// RUN: FileCheck %s --check-prefix=AIR < kernel_metadata_metal400_64.air @compute(CompileFor.deviceOnly) module kernels; import ldc.dcompute; diff --git a/tests/codegen/dcompute_metal_multiple_kernels.d b/tests/codegen/dcompute_metal_multiple_kernels.d index 184fa7cd80d..42f0f767fd8 100644 --- a/tests/codegen/dcompute_metal_multiple_kernels.d +++ b/tests/codegen/dcompute_metal_multiple_kernels.d @@ -1,8 +1,8 @@ // REQUIRES: target_AArch64 // REQUIRES: llvm20 -// RUN: %ldc -c -mdcompute-targets=metal-400 -output-ll -of=%t.ll %s -// RUN: FileCheck %s --check-prefix=AIR < kernels_metal400_64.air +// RUN: %ldc -c -mdcompute-targets=metal-400 --mdcompute-file-prefix=multiple_kernels -output-ll -of=%t.ll %s +// RUN: FileCheck %s --check-prefix=AIR < multiple_kernels_metal400_64.air @compute(CompileFor.deviceOnly) module kernels; From 33a641b7d4b63ed1862fb21bbb4254c1e5e753ea Mon Sep 17 00:00:00 2001 From: Asadbek Date: Fri, 31 Jul 2026 12:16:45 +0200 Subject: [PATCH 30/30] Instead of silently returning, give warning on other platforms that xcrun is not found --- driver/toobj.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/driver/toobj.cpp b/driver/toobj.cpp index 0b696d54836..79de3acfd72 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -109,7 +109,6 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64 if (cb == ComputeBackend::METAL) { -#ifdef __APPLE__ { // Inline non-kernel functions for Metal dcompute target inlineDComputeKernelFunctions(&m); @@ -135,8 +134,8 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, auto xcrunpath = llvm::sys::findProgramByName("xcrun"); if (!xcrunpath) { - error(Loc(), "xcrun not found - XCode should be installed first!"); - fatal(); + warning(Loc(), "xcrun not found - XCode should be installed first! Skipping .metallib generation"); + return; } llvm::SmallString<256> metallibOutPath; @@ -153,13 +152,15 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, int status = executeToolAndWait(Loc(), args[0], args); if (status < 0) { - error(Loc(), "program received signal %d (%s)", -status, +#if defined(_MSC_VER) || defined(__MINGW32__) + error(Loc(), "xcrun received signal %d", -status); +#else + error(Loc(), "xcrun received signal %d (%s)", -status, strsignal(-status)); +#endif fatal(); } -#endif - return; } #endif