diff --git a/driver/dcomputecodegenerator.cpp b/driver/dcomputecodegenerator.cpp index 0b95fcaa36d..4a86ff03753 100644 --- a/driver/dcomputecodegenerator.cpp +++ b/driver/dcomputecodegenerator.cpp @@ -17,7 +17,7 @@ #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 *) {} @@ -43,6 +43,20 @@ DComputeCodeGenManager::createComputeTarget(const std::string &s) { #endif } + if (s.substr(0, 6) == "metal-") { +#if LDC_LLVM_SUPPORTED_TARGET_AArch64 //&& LDC_LLVM_VER >= 2100 +#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 + } + 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,\ @@ -64,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 @@ -119,4 +137,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/driver/targetmachine.cpp b/driver/targetmachine.cpp index 3a1ff377127..fd9d6d225b0 100644 --- a/driver/targetmachine.cpp +++ b/driver/targetmachine.cpp @@ -666,6 +666,16 @@ 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; + + +#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/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..79de3acfd72 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -35,10 +35,10 @@ #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 -#include namespace llvm { namespace codegen { @@ -58,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, @@ -73,6 +107,64 @@ void codegenModule(llvm::TargetMachine &Target, llvm::Module &m, #endif } +#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) { + error(Loc(), "cannot write file '%s': %s", filename, + errinfo.message().c_str()); + fatal(); + } + + llvm::WriteBitcodeToFile(m, out.os()); + + out.keep(); + + // Terminate upon errors during the LLVM passes. + if (global.errors || global.warnings) { + error(Loc(), "Aborting because of errors/warnings during LLVM passes!"); + fatal(); + } + } + + auto xcrunpath = llvm::sys::findProgramByName("xcrun"); + if (!xcrunpath) { + warning(Loc(), "xcrun not found - XCode should be installed first! Skipping .metallib generation"); + return; + } + + 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) { +#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(); + } + + return; + } +#endif + std::error_code errinfo; llvm::ToolOutputFile out(filename, errinfo, llvm::sys::fs::OF_None); if (errinfo) { @@ -303,6 +395,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(); diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index ab9c51665b9..d4eceb50b2d 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" @@ -24,7 +22,6 @@ #include "gen/tollvm.h" #include "ir/irfunction.h" #include "ir/irfuncty.h" -#include using namespace dmd; @@ -302,6 +299,13 @@ 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(); + } + // fallthrough + 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..bb3196e1fb4 --- /dev/null +++ b/gen/abi/metal.cpp @@ -0,0 +1,60 @@ +//===-- 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 "gen/abi/abi.h" +#include "gen/dcompute/druntime.h" +#include "gen/dcompute/abi-rewrites.h" +#include "ir/irfuncty.h" +#include "dmd/mtype.h" +#include + +using namespace dmd; + +struct MetalABI : TargetABI { + DComputePointerRewrite pointerRewite; + DcomputeMetalScalarRewrite metalScalarRewrite; + + auto returnInArg(TypeFunction *tf, bool needsThis) -> bool override { + return false; + } + + auto passByVal(TypeFunction *tf, Type*t) -> bool override { + 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); + + 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); + } + + if (dmd::isScalar(ty)) { + metalScalarRewrite.applyTo(arg); + } + } +}; + +TargetABI* createMetalABI() { return new MetalABI(); } diff --git a/gen/abi/targets.h b/gen/abi/targets.h index 49098fe2579..afc6c64fe18 100644 --- a/gen/abi/targets.h +++ b/gen/abi/targets.h @@ -40,3 +40,5 @@ TargetABI *getX86TargetABI(); TargetABI *getLoongArch64TargetABI(); TargetABI *getWasmTargetABI(); + +TargetABI* createMetalABI(); diff --git a/gen/dcompute/abi-rewrites.h b/gen/dcompute/abi-rewrites.h index 1821df81b0c..ead84da2875 100644 --- a/gen/dcompute/abi-rewrites.h +++ b/gen/dcompute/abi-rewrites.h @@ -32,3 +32,19 @@ struct DComputePointerRewrite : ABIRewrite { return ptr->toLLVMType(true); } }; + +struct DcomputeMetalScalarRewrite : ABIRewrite { + LLType *type(Type* t) override { + // XXX: 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; + } +}; 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 1c44d1de7d2..ac958c4c1f9 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 +DComputeTarget* createMetalTarget(llvm::LLVMContext &c, int version); +#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..6d2f6632c46 --- /dev/null +++ b/gen/dcompute/targetMetal.cpp @@ -0,0 +1,219 @@ +//===-- 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 "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" +#include "dmd/identifier.h" + +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}}) { + + _ir = new IRState("dcomputeTargetMetal", ctx); + std::string tripleString = "air64_v28-apple-macosx26.0.0"; + +#if LLVM_VERSION_MAJOR >= 21 + _ir->module.setTargetTriple(llvm::Triple(tripleString)); +#else + _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" + "-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 = 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 = { + metaString("Metal"), + metaInt(tversion / 100), + metaInt((tversion / 10) % 10), + metaInt(tversion % 10), + }; + + 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"); + + 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( + metaInt(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( + 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) { + 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( + metaString("air.arg_type_size")); + argumentMetadata.push_back( + metaInt(dmd::size(type, vd->loc))); + + argumentMetadata.push_back( + metaString("air.arg_type_align_size")); + argumentMetadata.push_back( + metaInt(dmd::alignsize(type))); + + argumentMetadata.push_back( + metaString("air.arg_type_name")); + // TODO(asadbek): 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())); + } + + 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 { + 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. + +DComputeTarget* createMetalTarget(llvm::LLVMContext &c, int version) { + return new TargetMetal(c, version); +}; + +#endif // LDC_LLVM_SUPPORTED_TARGET_AArch64 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) { diff --git a/runtime/druntime/src/ldc/dcompute.d b/runtime/druntime/src/ldc/dcompute.d index 04002a43d91..ec807b58041 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. @@ -23,8 +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 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 * given target and version, but could also be used to tailor for * performance characteristics. See dcompute.std.index for an example diff --git a/tests/codegen/dcompute_metal_argument_metadata.d b/tests/codegen/dcompute_metal_argument_metadata.d new file mode 100644 index 00000000000..92d460bbec2 --- /dev/null +++ b/tests/codegen/dcompute_metal_argument_metadata.d @@ -0,0 +1,33 @@ +// REQUIRES: target_AArch64 +// REQUIRES: atleast_llvm20 + +// 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; + +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"} + diff --git a/tests/codegen/dcompute_metal_kernel_metadata.d b/tests/codegen/dcompute_metal_kernel_metadata.d new file mode 100644 index 00000000000..ec444c4b9ae --- /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: atleast_llvm20 + +// 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; + +// AIR-LABEL: define {{.*}} @{{.*}}test_kernel{{.*}}( +@kernel() +void test_kernel(GlobalPointer!float data) { + data[0] = 42.0; +} + +// 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]] = !{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} diff --git a/tests/codegen/dcompute_metal_multiple_kernels.d b/tests/codegen/dcompute_metal_multiple_kernels.d new file mode 100644 index 00000000000..3a07c6208f6 --- /dev/null +++ b/tests/codegen/dcompute_metal_multiple_kernels.d @@ -0,0 +1,42 @@ +// REQUIRES: target_AArch64 +// REQUIRES: atleast_llvm20 + +// 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; + +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"} +