Skip to content

Commit 8bd3aa7

Browse files
maleadtclaude
andauthored
Metal: drop integer call-site attributes when narrowing pointer args (#826)
The typed-pointer shim retargeted a Ptr argument's integer image to a pointer but left the call site's integer attributes (e.g. zeroext) in place, which LLVM rejects on a pointer. Crashed throwing-kernel codegen on Julia 1.10/1.11. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7b270bd commit 8bd3aa7

3 files changed

Lines changed: 55 additions & 3 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "GPUCompiler"
22
uuid = "61eb1bfa-7361-4325-ad38-22787b887f55"
3-
version = "1.16.0"
3+
version = "1.16.1"
44
authors = ["Tim Besard <tim.besard@gmail.com>"]
55

66
[workspace]

src/metal.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,14 +725,20 @@ end
725725

726726
# copy the call-site attributes (function/return/per-argument) from `src` onto `dst`. the
727727
# narrowing keeps argument positions unchanged, so they map across one-to-one.
728-
function copy_callsite_attributes!(dst::LLVM.CallInst, src::LLVM.CallInst)
728+
function copy_callsite_attributes!(dst::LLVM.CallInst, src::LLVM.CallInst,
729+
int_ptr_types::Vector{Any})
729730
for attr in collect(function_attributes(src))
730731
push!(function_attributes(dst), attr)
731732
end
732733
for attr in collect(return_attributes(src))
733734
push!(return_attributes(dst), attr)
734735
end
735736
for i in 1:length(arguments(src))
737+
# Skip Case B (typed-pointer shim) arguments: `src` passed an integer (the `ptrtoint`
738+
# image of a specific-space pointer) whose attributes (e.g. `zeroext`) are invalid on
739+
# the retargeted pointer argument. Mirrors the parameter-side handling in
740+
# `narrow_pointer_parameters!`, which likewise drops Case B attributes.
741+
int_ptr_types[i] !== nothing && continue
736742
for attr in collect(argument_attributes(src, i))
737743
push!(argument_attributes(dst, i), attr)
738744
end
@@ -763,7 +769,7 @@ function rewrite_narrowed_call!(builder::IRBuilder, cs::LLVM.CallInst,
763769
end
764770
new_call = call!(builder, new_ft, new_f, new_args, operand_bundles(cs))
765771
callconv!(new_call, callconv(cs))
766-
copy_callsite_attributes!(new_call, cs)
772+
copy_callsite_attributes!(new_call, cs, int_ptr_types)
767773
replace_uses!(cs, new_call)
768774
erase!(cs)
769775
return new_call

test/metal.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,52 @@ end
380380
@test (verify(mod); true)
381381
end
382382

383+
# typed-pointer shim with an integer-ABI attribute on the boundary: Julia tags the
384+
# `ptrtoint` image with `zeroext`, which is invalid once the parameter/argument is
385+
# retargeted to a pointer. the pass must drop it on the parameter *and* the call site, or
386+
# the module fails verification. (regression: the parameter side was cleaned but the call
387+
# site kept its `zeroext`, leaving `i8 addrspace(2)* zeroext` and crashing LLVM
388+
# verification of throwing kernels on Julia 1.10/1.11 -- JuliaGPU/Metal.jl device exceptions.)
389+
Context() do ctx
390+
i8 = LLVM.Int8Type()
391+
i64 = LLVM.Int64Type()
392+
mod = LLVM.Module("test")
393+
callee_ft = LLVM.FunctionType(i8, LLVM.LLVMType[i64])
394+
callee = LLVM.Function(mod, "callee", callee_ft)
395+
linkage!(callee, LLVM.API.LLVMInternalLinkage)
396+
push!(parameter_attributes(callee, 1), EnumAttribute("zeroext", 0))
397+
@dispose builder=IRBuilder() begin
398+
position!(builder, BasicBlock(callee, "entry"))
399+
p = inttoptr!(builder, parameters(callee)[1], asptr(0))
400+
ret!(builder, load!(builder, i8, p))
401+
end
402+
g = GlobalVariable(mod, i8, "g", 2)
403+
initializer!(g, ConstantInt(i8, 1)); constant!(g, true)
404+
caller = LLVM.Function(mod, "caller", LLVM.FunctionType(i8, LLVM.LLVMType[]))
405+
linkage!(caller, LLVM.API.LLVMInternalLinkage)
406+
@dispose builder=IRBuilder() begin
407+
position!(builder, BasicBlock(caller, "entry"))
408+
arg = const_ptrtoint(const_addrspacecast(g, asptr(0)), i64)
409+
cs = call!(builder, callee_ft, callee, [arg])
410+
push!(argument_attributes(cs, 1), EnumAttribute("zeroext", 0))
411+
ret!(builder, cs)
412+
end
413+
@test (verify(mod); true) # `zeroext` on the i64 boundary is valid pre-narrowing
414+
415+
if supports_typed_pointers()
416+
@test GPUCompiler.propagate_argument_address_spaces!(mod)
417+
# the retargeted pointer must not keep the integer's `zeroext`, on either side
418+
callee = functions(mod)["callee"]
419+
@test !(kind(EnumAttribute("zeroext", 0)) in
420+
kind.(collect(parameter_attributes(callee, 1))))
421+
@test all(c -> !(kind(EnumAttribute("zeroext", 0)) in
422+
kind.(collect(argument_attributes(c, 1)))), calls_to(mod, "callee"))
423+
else
424+
@test !GPUCompiler.propagate_argument_address_spaces!(mod)
425+
end
426+
@test (verify(mod); true)
427+
end
428+
383429
# an integer parameter used as more than a pointer image (here also in arithmetic) is
384430
# left alone: narrowing it would lose the integer's other uses (and under opaque pointers
385431
# the typed-pointer shim is off, so it is left alone regardless)

0 commit comments

Comments
 (0)