AIR is the LLVM IR dialect used by Metal. AIR shader binaries are guaranteed to run unmodified on a future operating system release.
However, Apple explicitly chose to not document AIR, making the Metal Shading Language the only officially supported input language.
The compiler
metal, the compiler, supports two input languages under the hood. But only one of which is exposed through public API, the Metal Shading Language.
That second input language is OpenCL, usable through --driver-mode=openclc -x cl. That’s what Apple’s OpenCL implementation on M1 uses under the hood. It’s an internal implementation detail not exposed to developers.
The Metal tool set as shipped as part of Xcode does not ship with opencl-c.h however, and the resulting binaries do not seem to be loadable through clCreateProgramWithBinary.
Offline compilation
In the Metal API, offline compilation is a first-class citizen. Apple does ship metal-as as an AIR assembler, a disassembler for AIR (metal-objdump) and a linker (metallib).
At runtime, Metal doesn’t take LLVM bitcode files directly (.air, equivalent to .bc), but does take linked programs (.metallib).
Online compilation
Online compilation is also available. However, options are quite limited there, without possibility to pass compiler arguments or even custom include paths.
For the OpenCL implementation, argument filtering is also done, to block non-allowlisted arguments from being passed through to the compiler.
AIR versions
This section is a work in progress.
AIR is versioned, with the version number shared with the Metal Shading Language version
Multiple different GPUCompiler libraries ship with macOS. Version 3802, 3902, 31001
On XCode 16.0 there is a opencl-c.h:
$ find /Applications/Xcode.app/ -name “opencl-c*”
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/opencl-c-base.h
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/opencl-c.h
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/16/include/opencl-c-base.h
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/16/include/opencl-c.h
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/tapi/16/include/opencl-c-base.h
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/tapi/16/include/opencl-c.h
/Applications/Xcode.app//Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/Clang/include/opencl-c-base.h
/Applications/Xcode.app//Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/Clang/include/opencl-c.h
but still,
$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/metal —
driver-mode=openclc -x cl -DRDNA -I. -I/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/u
sr/metal/32023/lib/clang/32023.335/include/ -DVECTOR_SIZE=4 -DMORE_CLASSES -DCL_GPU_SIEVE -save-temps barrett15.cl
In file included from barrett15.cl:1:
:1:29: fatal error: module ‘opencl_c’ not found
(barrett15.cl is from the mfakto project).
Thinking that making it not using C++ may help, I tried ” -std=cl1.2″. Then I got:
error: invalid argument ‘-std=cl1.2’ not allowed with ‘C’
So nope, no idea how still.
Alright. Adding “-fno-modules” fixes the include problem, but now I’m seeing clear signs of the input not being treated as OpenCL C:
“`
$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/metal –driver-mode=openclc -x cl -DRDNA -I. -/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/ -DVECTOR_SIZE=4 -DMORE_CLASSES -DCL_GPU_SIEVE -save-temps barrett15.cl -fno-modules
In file included from barrett15.cl:1:
In file included from :1:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/opencl-c-base.h:188:9: error: unknown type name ‘half’
typedef half half2 __attribute__((ext_vector_type(2)));
[snip]
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/opencl-c-base.h:206:9: error: unknown type name ‘bool’
typedef bool bool2 __attribute__((ext_vector_type(2)));
[snip]
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/opencl-c-base.h:691:12: error: unknown type name ‘__constant’
int printf(__constant const char* st, …) __attribute__((format(printf, 1, 2)));
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/opencl-c-base.h:691:59: error: format argument not a string type
int printf(__constant const char* st, …) __attribute__((format(printf, 1, 2)));
“`
Thinking that it might not be really using “-x cl”, I tried appending “-Xclang -std=cl1.2” and got:
“`
error: invalid argument ‘-std=cl1.2’ not allowed with ‘C’
“`
“`
$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/metal -v –driver-mode=openclc -x cl -DRDNA -I. -I/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/ -DVECTOR_SIZE=4 -DMORE_CLASSES -DCL_GPU_SIEVE -save-temps gpusieve.cl -fno-modules -Xclang -std=cl1.2
Apple metal version 32023.335 (metalfe-32023.335)
Target: air64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/current/bin
“/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/bin/metal” -cc1 -triple air64-apple-macosx14.0.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -E -save-temps=cwd -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name gpusieve.cl -mrelocation-model static -mframe-pointer=all -fno-strict-return -ffp-contract=on -fno-rounding-math -fno-verbose-asm -no-integrated-as -faligned-alloc-unavailable -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -target-abi vb -debugger-tuning=lldb -fno-dwarf-directory-asm -target-linker-version 1115.7.3 -v -fcoverage-compilation-dir=[redact] -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335 -D RDNA -I . -I /Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/ -D VECTOR_SIZE=4 -D MORE_CLASSES -D CL_GPU_SIEVE -internal-isystem /usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include -internal-externc-isystem /usr/include -Wno-reorder-init-list -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-extra-semi-stmt -Wno-misleading-indentation -Wno-quoted-include-in-framework-header -Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion -Wno-elaborated-enum-base -Wno-reserved-identifier -Wno-gnu-folding-constant -Wno-objc-load-method -fno-autolink -fdebug-compilation-dir=[redact] -ferror-limit 19 -fstack-check -finclude-default-header -fdeclare-opencl-builtins -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-threadsafe-statics -no-opaque-pointers -fcommon -fcolor-diagnostics -std=cl1.2 -clang-vendor-feature=+disableNonDependentMemberExprInCurrentInstantiation -fno-odr-hash-protocols -clang-vendor-feature=+enableAggressiveVLAFolding -clang-vendor-feature=+revert09abecef7bbf -clang-vendor-feature=+thisNoAlignAttr -clang-vendor-feature=+thisNoNullAttr -mllvm -disable-aligned-alloc-awareness=1 -o gpusieve.i -x cl gpusieve.cl
clang -cc1 version 32023.335 (metalfe-32023.335) default target x86_64-apple-darwin23.6.0
ignoring nonexistent directory “/usr/include”
ignoring duplicate directory “/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include”
as it is a non-system directory that duplicates a system directory
#include “…” search starts here:
#include search starts here:
.
/usr/local/include
/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
End of search list.
“/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/bin/metal” -cc1 [snip] -disable-llvm-passes -o gpusieve.bc -x cpp-output gpusieve.i
error: invalid argument ‘-std=cl1.2’ not allowed with ‘C’
“`
So it’s doing it in two steps. Perhaps if I remove “-save-temps” it would work?
I run `/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/metal -DNUM_CLASSES=420 -v –driver-mode=openclc -x cl -DRDNA -I. -I/Applications/Xcode.app//Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/metal/32023/lib/clang/32023.335/include/ -DVECTOR_SIZE=4 -DMORE_CLASSES -DCL_GPU_SIEVE gpusieve.cl -fno-modules -Xclang -std=cl1.2` and… Boom! it does produce a default.metallib. Welp.
(Using gpusieve.cl because barrett15.cl produces a number of missing-typedef errors even when compiled with plain `clang`: it appear that the file is not quite intended to be used alone. But yes I checked, without `-save-temps` the error is the same as regular `clang`, no more complaining about not knowing what `half` is.)