www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DCompute Metal Backend

reply Asadbek <aosindarov gmail.com> writes:
Hello everyone,

I am creating this thread to share my progress on building a 
Metal GPU target for DCompute. Apologies for the delay in sharing 
this as promised earlier!

I started working on this back in late April. I have set up basic 
compiler support to lower to correct, Apple-compliant LLVM IR 
(AIR). Since Apple uses a custom fork of LLVM, getting this to 
work involved a fair amount of reverse engineering, but the 
baseline is now functional.

Here is the draft PR on LDC: 
https://github.com/ldc-developers/ldc/pull/5118

For context, GPU programming splits code into two categories: 
device code and host code. Device code is sent to the GPU runtime 
and mostly contains shaders. Because DCompute is a compute 
library (where compute shaders are called kernels), the device 
code that DCompute sends to the GPU contains only kernels.

Host code facilitates execution between the GPU and the CPU. It 
handles copying memory buffers to the GPU address space, sending 
the device code binary to the GPU, and waiting for the results to 
process them (though it does not necessarily need to wait and 
block the running thread while waiting). I have also set up a 
basic host code implementation.

Here is the PR on DCompute for the host code: 
https://github.com/libmir/dcompute/pull/99

Currently, basic kernels can successfully execute on Apple Metal 
GPUs. I will continue development and share future updates in 
this thread.

Special thanks to Nicholas and Gaofei for their help and 
suggestions throughout the development process!
Jul 09
parent Asadbek <aosindarov gmail.com> writes:
Hello everyone,

I wanted to share an update on my progress in adding a Metal GPU 
backend to DCompute.

Over the past few weeks, I’ve been researching how languages with 
mature Metal GPU backends: such as Julia, Mojo, and AdaptiveCpp, 
handle their compilation pipelines. I decided to adopt an 
approach inspired by the Julia compiler's architecture.

For context, the fundamental challenge with targeting Apple Metal 
GPU is that Apple relies on a custom, older fork of LLVM. While 
upstream LLVM supports backward compatibility for *reading* older 
bitcode formats, the LLVM bitcode writer cannot *emit* older 
bitcode versions.

To bypass this limitation, Julia’s maintainers copied an older 
LLVM IR bitcode writer and built an independent utility to 
"downgrade" modern LLVM bitcode into the Apple-compatible format 
(Apple IR / AIR). You can see their utility 
[here](https://github.com/JuliaLLVM/llvm-downgrade).

Their high-level device code compilation pipeline looks like this:
```text
[ Julia Source Code ]
         │
         ▼ (Frontend)
     [ LLVM IR ]
         │
         ▼
[ LLVM IR + Apple-specific Metadata ]
         │
         ▼ (Custom LLVM Downgrader)
[ Metal-Compatible LLVM Bitcode (AIR) ]
```
While Julia doesn't strictly separate host and device code at the 
user level, their lower-level infrastructure still performs two 
distinct compilations, ultimately passing the downgraded device 
binary to the GPU via host buffers

Inspired by Julia's 
[LLVMDowngrader_jll.jl](https://github.com/JuliaBinaryWrappers/LLV
Downgrader_jll.jl), I have created a D alternative:
[llvm_downgrader](https://github.com/asindarov/llvm_downgrader).

With that being said, we only need the LDC compiler to generate 
device code with the necessary Apple-specific metadata attached 
(implementation 
[here](https://github.com/asindarov/ldc/blob/metal-backend/gen/dcomp
te/targetMetal.cpp) in my fork). Once LDC emits this IR, DCompute leverages the
new LLVM downgrader to translate it into Apple IR. Finally, we use Apple's
`xcrun metallib` linker to package it into a `.metallib` binary ready for
execution.

Our final compilation pass for DCompute and LDC looks like this:
```text
[ D Device Code ]
                │
                ▼ (LDC Compiler)
[ LLVM IR + Apple-specific Metadata ]
                │
                ▼ (llvm_downgrader)
[ Apple-compatible LLVM Bitcode (AIR) ]
                │
                ▼ (xcrun metallib)
       [ .metallib Binary ]
```
Link to the linker integration 
[here](https://github.com/asindarov/dcompute/blob/584160a531640fadfb6653b1d4f5c504daf5a9c3/source/dcompute/driver/metal/program.d#L40-L66).

This is now fully functional, and basic kernels are successfully 
executing on Metal GPUs.

Here are the relevant PRs for those interested:

https://github.com/ldc-developers/ldc/pull/5118)
https://github.com/libmir/dcompute/pull/99)

Thanks again to everyone who has provided feedback so far on the 
PRs! I will keep you updated as I continue to expand the 
functionality.
Aug 16