Skip to content

Initial Metal backend support to dcompute - #5118

Open
asindarov wants to merge 29 commits into
ldc-developers:masterfrom
asindarov:metal-backend
Open

Initial Metal backend support to dcompute#5118
asindarov wants to merge 29 commits into
ldc-developers:masterfrom
asindarov:metal-backend

Conversation

@asindarov

Copy link
Copy Markdown
Contributor

No description provided.

Comment thread gen/dcompute/targetMetal.cpp Outdated
@gulugulubing

Copy link
Copy Markdown
Contributor

In cmakelist, LDC_LLVM_VER was removed. Try LLVM_VERSION_MAJOR.

Comment thread gen/dcompute/targetMetal.cpp Outdated
Comment thread gen/dcompute/targetMetal.cpp
Comment thread gen/abi/metal.cpp Outdated
Comment thread gen/abi/abi.cpp Outdated
Comment thread driver/dcomputecodegenerator.cpp Outdated
Comment thread gen/dcompute/target.h Outdated
Comment thread gen/dcompute/targetMetal.cpp Outdated
Comment thread gen/dcompute/targetMetal.cpp
Comment thread runtime/druntime/src/ldc/dcompute.d
Comment thread gen/dcompute/targetMetal.cpp Outdated

@thewilsonator thewilsonator left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. Needs some tests.

}
};

struct DcomputeMetalScalarRewrite : ABIRewrite {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this now unused?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it still used in abi/metal.cpp file:

struct MetalABI : TargetABI {
    DComputePointerRewrite pointerRewite;
    DcomputeMetalScalarRewrite metalScalarRewrite;
.....

It is used to make scalar objects turn into a pointer in constant memory address space. Although it turns out scalars can also be stored in device level address space which will be Global in the dcompute term as I understand

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scalar arguments to kernels are just passed as parameters

Comment thread gen/dcompute/abi-rewrites.h Outdated
Comment thread driver/dcomputecodegenerator.cpp Outdated
* 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto, also could you update the documentation here to specify what are the valid versions of Metal (e.g. 400)?

Comment thread runtime/druntime/src/ldc/dcompute.d Outdated
Comment thread driver/toobj.cpp Outdated
Comment thread driver/toobj.cpp
if (useIR2ObjCache) {
cache::cacheObjectFile(filename, moduleHash);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace

Comment thread dmd/globals.h Outdated
@asindarov
asindarov marked this pull request as ready for review July 28, 2026 12:45
@asindarov
asindarov requested a review from thewilsonator July 28, 2026 13:00
@@ -0,0 +1,33 @@
// REQUIRES: target_AArch64
// REQUIRES: llvm20

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be an "atleast" requirement

@asindarov asindarov Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it should be REQUIRES because currently there are outstanding issues with the new changes in llvm versions after llvm20. Therefore current changes work only with llvm20.

@asindarov asindarov Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the issue was in newer llvm versions some attributes got changed. Here you can find nocapture got changed to captures. Apple llvm bitcode writer does not know captures attribute's existence, it expects nocapture instead.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't understand. Which LLVM bitcode writer?
Upon changing the attribute, ofcourse the bitcode writer is also updated to work with that?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you mean that we have to match the test with the available xcrun version on the tester. Can we rewrite the test to not rely on running xcrun? Just use the outputted object file (that is passed to xcrun) instead?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(we don't need to test xcrun, we need to test our output that goes into xcrun, right?)

@asindarov asindarov Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I have not provided more context on this. In the upstream LLVM there is no official Apple Metal backend target, I reverse engineered the necessary metadata that Apple Metal Shading Language compiler (which is old fork of LLVM with adjustments by Apple) expects for the kernel functions. The pipeline is as follows:

DCompute kernel code ->  LLVM IR bitcode with reverse engineered metadata ->
                         xcrun -sdk macosx metallib .... -> 
                         generates <filename>.metallib binary file

We use that <filename>.metallib binary file to execute kernels at the dcompute host code part as follows:

...
        auto program = Program.fromFile(device, "./kernels_metal400_64.metallib");

        Program.globalProgram = program;
        
        auto deviceX = device.makeBuffer!float(x);
        auto deviceY = device.makeBuffer!float(y);
        auto deviceRes = device.makeBuffer!float(res);

        auto queue = Queue(device);

        queue.enqueue!(saxpy)
                ([N,1,1],[1,1,1])
                (deviceRes, alpha, deviceX, deviceY, N);

        queue.finish();
...

The bitcode issue is that xcrun uses Apple toolchain which is closed source LLVM fork. Current changes offer basic support. What I am thinking to do afterwards is to explore to write custom bitcode writer as done in Julia compiler (https://github.com/JuliaLLVM/llvm-downgrade/blob/main/src/BitcodeWriter140.cpp). They mostly copy & pasted older llvm bitcode writer and adjusted a bit for their codegen. They also did similar changes to emit correct metadata as in this PR. (https://github.com/JuliaGPU/GPUCompiler.jl/blob/main/src/metal.jl)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just that this is quite flaky... and this test will start to fail when the system xcode version changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, it will not. In the test I only tested what would be generated as LLVM IR from ldc compiler not the output of xcrun.

@asindarov asindarov Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the metadata is added at the ldc side and llvm bitcode is emitted and sent over to xcrun afterwards. Tests in this PR only tested the emitted metadata from ldc side only. But once xcrun -sdk macosx metallib compiler changes what it expects from the Apple Metal Shading language compiler (closed old fork of LLVM), only then we might have to adjust the metal target metadata codegen part in ldc and these tests.

Comment thread tests/codegen/dcompute_metal_argument_metadata.d Outdated
Comment thread driver/toobj.cpp

#ifdef LDC_LLVM_SUPPORTED_TARGET_AArch64
if (cb == ComputeBackend::METAL) {
#ifdef __APPLE__

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ifdef would mean LDC cannot produce metal bitcode output on other platforms, and instead will silently not create any output.

I think this code should be included on any LDC build (it is a cross-compiler after all). xcrun would not be found, perhaps make that a warning instead of a fatal error. Generating the object file could still be useful, no?

Comment thread driver/toobj.cpp
int status = executeToolAndWait(Loc(), args[0], args);

if (status < 0) {
error(Loc(), "program received signal %d (%s)", -status,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"program" can be very ambiguous here. Why not call it "xcrun" ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants