Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metal: Remove ability to generate binary libraries. #548

Merged
merged 4 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 61 additions & 61 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,36 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ['1.8', '1.9', '1.10.0-beta3', 'nightly']
version: ['1.8', '1.9', '1.10'] # 'nightly'
os: [ubuntu-latest, macOS-latest, windows-latest]
arch: [x64]
llvm_args: ['']
include:
# starting with Julia 1.10, we can enable opaque pointers
- version: '1.10.0-beta3'
- version: '1.10'
os: 'ubuntu-latest'
arch: 'x64'
llvm_args: '--opaque-pointers'
- version: '1.10.0-beta3'
- version: '1.10'
os: 'macOS-latest'
arch: 'x64'
llvm_args: '--opaque-pointers'
- version: '1.10.0-beta3'
os: 'windows-latest'
arch: 'x64'
llvm_args: '--opaque-pointers'
- version: 'nightly'
os: 'ubuntu-latest'
arch: 'x64'
llvm_args: '--opaque-pointers'
- version: 'nightly'
os: 'macOS-latest'
arch: 'x64'
llvm_args: '--opaque-pointers'
- version: 'nightly'
- version: '1.10'
os: 'windows-latest'
arch: 'x64'
llvm_args: '--opaque-pointers'
#- version: 'nightly'
# os: 'ubuntu-latest'
# arch: 'x64'
# llvm_args: '--opaque-pointers'
#- version: 'nightly'
# os: 'macOS-latest'
# arch: 'x64'
# llvm_args: '--opaque-pointers'
#- version: 'nightly'
# os: 'windows-latest'
# arch: 'x64'
# llvm_args: '--opaque-pointers'
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -82,48 +82,48 @@ jobs:
file: lcov.info

# fetching builds from Buildkite with assertions enabled
assert_test:
name: Julia-master ${{ matrix.build }} ${{ matrix.llvm_args }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
build: ['x86_64-linux-gnuassert']
os: ['ubuntu-latest']
arch: ['x64']
llvm_args: ['', '--opaque-pointers']
steps:
- uses: actions/checkout@v4

- name: Download Julia
env:
BUILDKITE_TOKEN: ${{ secrets.BUILDKITE_TOKEN }}
run: |
./.github/download_build.sh build_${{ matrix.build }} julia.tar.gz
tar -xf julia.tar.gz -C ../
rm julia.tar.gz
echo $PWD/../julia-*/bin >> $GITHUB_PATH

# set-up packages
- uses: actions/cache@v4
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/julia-buildpkg@v1

- name: Run tests
uses: julia-actions/julia-runtest@v1
env:
JULIA_LLVM_ARGS: ${{ matrix.llvm_args }}

# post-process
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
with:
file: lcov.info
#assert_test:
# name: Julia-master ${{ matrix.build }} ${{ matrix.llvm_args }}
# runs-on: ${{ matrix.os }}
# strategy:
# fail-fast: false
# matrix:
# build: ['x86_64-linux-gnuassert']
# os: ['ubuntu-latest']
# arch: ['x64']
# llvm_args: ['', '--opaque-pointers']
# steps:
# - uses: actions/checkout@v4
#
# - name: Download Julia
# env:
# BUILDKITE_TOKEN: ${{ secrets.BUILDKITE_TOKEN }}
# run: |
# ./.github/download_build.sh build_${{ matrix.build }} julia.tar.gz
# tar -xf julia.tar.gz -C ../
# rm julia.tar.gz
# echo $PWD/../julia-*/bin >> $GITHUB_PATH
#
# # set-up packages
# - uses: actions/cache@v4
# env:
# cache-name: cache-artifacts
# with:
# path: ~/.julia/artifacts
# key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
# restore-keys: |
# ${{ runner.os }}-test-${{ env.cache-name }}-
# ${{ runner.os }}-test-
# ${{ runner.os }}-
# - uses: julia-actions/julia-buildpkg@v1
#
# - name: Run tests
# uses: julia-actions/julia-runtest@v1
# env:
# JULIA_LLVM_ARGS: ${{ matrix.llvm_args }}
#
# # post-process
# - uses: julia-actions/julia-processcoverage@v1
# - uses: codecov/codecov-action@v4
# with:
# file: lcov.info
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "GPUCompiler"
uuid = "61eb1bfa-7361-4325-ad38-22787b887f55"
authors = ["Tim Besard <[email protected]>"]
version = "0.25.0"
version = "0.26.0"

[deps]
ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
Expand Down
126 changes: 47 additions & 79 deletions src/metal.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# implementation of the GPUCompiler interfaces for generating Metal code

const Metal_LLVM_Tools_jll = LazyModule("Metal_LLVM_Tools_jll", UUID("0418c028-ff8c-56b8-a53e-0f9676ed36fc"))

## target

export MetalCompilerTarget
Expand Down Expand Up @@ -47,21 +45,11 @@ runtime_slug(job::CompilerJob{MetalCompilerTarget}) = "metal-macos$(job.config.t
isintrinsic(@nospecialize(job::CompilerJob{MetalCompilerTarget}), fn::String) =
return startswith(fn, "air.")

const LLVMMETALFUNCCallConv = LLVM.API.LLVMCallConv(102)
const LLVMMETALKERNELCallConv = LLVM.API.LLVMCallConv(103)

function finish_module!(@nospecialize(job::CompilerJob{MetalCompilerTarget}), mod::LLVM.Module, entry::LLVM.Function)
entry_fn = LLVM.name(entry)

# update calling conventions
for f in functions(mod)
#callconv!(f, LLVMMETALFUNCCallConv)
# XXX: this makes InstCombine erase kernel->func calls.
# do we even need this? if we do, do so in metallib-instead.
end
if job.config.kernel
callconv!(entry, LLVMMETALKERNELCallConv)

entry = pass_by_reference!(job, mod, entry)

add_input_arguments!(job, mod, entry)
Expand Down Expand Up @@ -110,6 +98,49 @@ function validate_ir(job::CompilerJob{MetalCompilerTarget}, mod::LLVM.Module)
check_ir_values(mod, LLVM.DoubleType())
end

# hide `noreturn` function attributes, which cause issues with the back-end compiler,
# probably because of thread-divergent control flow as we've encountered with CUDA.
# note that it isn't enough to remove the function attribute, because the Metal LLVM
# compiler re-optimizes and will rediscover the property. to avoid this, we inline
# all functions that are marked noreturn, i.e., until LLVM cannot rediscover it.
function hide_noreturn!(mod::LLVM.Module)
noreturn_attr = EnumAttribute("noreturn", 0)
noinline_attr = EnumAttribute("noinline", 0)
alwaysinline_attr = EnumAttribute("alwaysinline", 0)

any_noreturn = false
for f in functions(mod)
attrs = function_attributes(f)
if noreturn_attr in collect(attrs)
delete!(attrs, noreturn_attr)
delete!(attrs, noinline_attr)
push!(attrs, alwaysinline_attr)
any_noreturn = true
end
end
any_noreturn || return false

if use_newpm
@dispose pb=PassBuilder() mpm=NewPMModulePassManager(pb) begin
add!(mpm, AlwaysInlinerPass())
add!(mpm, NewPMFunctionPassManager) do fpm
add!(fpm, SimplifyCFGPass())
add!(fpm, InstCombinePass())
end
run!(mpm, mod)
end
else
@dispose pm=ModulePassManager() begin
always_inliner!(pm)
cfgsimplification!(pm)
instruction_combining!(pm)
run!(pm, mod)
end
end

return true
end

function finish_ir!(@nospecialize(job::CompilerJob{MetalCompilerTarget}), mod::LLVM.Module,
entry::LLVM.Function)
entry_fn = LLVM.name(entry)
Expand All @@ -121,6 +152,8 @@ function finish_ir!(@nospecialize(job::CompilerJob{MetalCompilerTarget}), mod::L
add_argument_metadata!(job, mod, entry)

add_module_metadata!(job, mod)

hide_noreturn!(mod)
end

# lower LLVM intrinsics that AIR doesn't support
Expand Down Expand Up @@ -154,73 +187,8 @@ end

@unlocked function mcgen(job::CompilerJob{MetalCompilerTarget}, mod::LLVM.Module,
format=LLVM.API.LLVMObjectFile)
strip_debuginfo!(mod) # XXX: is this needed?

# hide `noreturn` function attributes, which cause issues with the back-end compiler,
# probably because of thread-divergent control flow as we've encountered with CUDA.
# note that it isn't enough to remove the function attribute, because the Metal LLVM
# compiler re-optimizes and will rediscover the property. to avoid this, we inline
# all functions that are marked noreturn, i.e., until LLVM cannot rediscover it.
let
noreturn_attr = EnumAttribute("noreturn", 0)
noinline_attr = EnumAttribute("noinline", 0)
alwaysinline_attr = EnumAttribute("alwaysinline", 0)

any_noreturn = false
for f in functions(mod)
attrs = function_attributes(f)
if noreturn_attr in collect(attrs)
delete!(attrs, noreturn_attr)
delete!(attrs, noinline_attr)
push!(attrs, alwaysinline_attr)
any_noreturn = true
end
end

if any_noreturn
if use_newpm
@dispose pb=PassBuilder() mpm=NewPMModulePassManager(pb) begin
add!(mpm, AlwaysInlinerPass())
add!(mpm, NewPMFunctionPassManager) do fpm
add!(fpm, SimplifyCFGPass())
add!(fpm, InstCombinePass())
end
run!(mpm, mod)
end
else
@dispose pm=ModulePassManager() begin
always_inliner!(pm)
cfgsimplification!(pm)
instruction_combining!(pm)
run!(pm, mod)
end
end
end
end

# translate to metallib
input = tempname(cleanup=false) * ".bc"
translated = tempname(cleanup=false) * ".metallib"
write(input, mod)
let cmd = `$(Metal_LLVM_Tools_jll.metallib_as()) -o $translated $input`
proc = run(ignorestatus(cmd))
if !success(proc)
error("""Failed to translate LLVM code to MetalLib.
If you think this is a bug, please file an issue and attach $(input).""")
end
end

output = if format == LLVM.API.LLVMObjectFile
read(translated)
else
# disassemble
read(`$(Metal_LLVM_Tools_jll.metallib_dis()) -o - $translated`, String)
end

rm(input)
rm(translated)

return output
# our LLVM version does not support emitting Metal libraries
return nothing
end


Expand Down
1 change: 0 additions & 1 deletion test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
Metal_LLVM_Tools_jll = "0418c028-ff8c-56b8-a53e-0f9676ed36fc"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
ReTestItems = "817f1d60-ba6b-4fd5-9520-3cf149f6a823"
SPIRV_LLVM_Translator_unified_jll = "85f0d8ed-5b39-5caa-b1ae-7472de402361"
Expand Down
30 changes: 1 addition & 29 deletions test/metal_tests.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
@testitem "Metal" setup=[Metal, Helpers] begin

using Metal_LLVM_Tools_jll, LLVM
using LLVM

############################################################################################

@testset "IR" begin

@testset "kernel functions" begin
@testset "calling convention" begin
kernel() = return

ir = sprint(io->Metal.code_llvm(io, kernel, Tuple{}; dump_module=true))
@test !occursin("cc103", ir)

ir = sprint(io->Metal.code_llvm(io, kernel, Tuple{};
dump_module=true, kernel=true))
@test occursin("cc103", ir)
end

@testset "byref aggregates" begin
kernel(x) = return

Expand Down Expand Up @@ -94,21 +83,4 @@ end

end

############################################################################################

Sys.isapple() && @testset "asm" begin

@testset "smoke test" begin
kernel() = return

asm = sprint(io->Metal.code_native(io, kernel, Tuple{};
dump_module=true, kernel=true))
@test occursin("[header]", asm)
@test occursin("[program]", asm)
@test occursin(r"name: \w*kernel\w*", asm)
@test occursin(r"define void @\w*kernel\w*", asm)
end

end

end
Loading