diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0d900e0..3fbca13e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -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 diff --git a/Project.toml b/Project.toml index cfeb47e7..3ae5983e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "GPUCompiler" uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" authors = ["Tim Besard "] -version = "0.25.0" +version = "0.26.0" [deps] ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04" diff --git a/src/metal.jl b/src/metal.jl index e82c9ade..b3e18159 100644 --- a/src/metal.jl +++ b/src/metal.jl @@ -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 @@ -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) @@ -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) @@ -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 @@ -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 diff --git a/test/Project.toml b/test/Project.toml index c8fa51a6..ebfd27d3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -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" diff --git a/test/metal_tests.jl b/test/metal_tests.jl index 81978486..6a6efd5b 100644 --- a/test/metal_tests.jl +++ b/test/metal_tests.jl @@ -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 @@ -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