Skip to content
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
1 change: 1 addition & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ steps:
julia:
- "1.11"
- "1.12"
- "1.13-nightly"
1 change: 1 addition & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
version:
- '1.11'
- '1.12'
- '1.13-nightly'
os:
- Linux
- Windows
Expand Down
34 changes: 15 additions & 19 deletions src/compiler/codegen/expressions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ In Tile IR codegen, only ghost types (zero-size immutables like `Val{V}`,
function emit_new!(ctx::CGCtx, expr::Expr, @nospecialize(result_type))
T = CC.widenconst(result_type)
is_ghost_type(T) && return ghost_value(T)
# On older Julia versions, method errors are emitted as
# %new(MethodError, func, args_tuple, world) instead of Core.throw_methoderror
if T === MethodError
# expr.args: (MethodError, func, args_tuple, world)
_throw_method_error(ctx, expr.args[2:end-1])
end
throw(IRError("Struct construction not supported in Tile IR: $T"))
end

Expand Down Expand Up @@ -103,7 +109,7 @@ function emit_call!(ctx::CGCtx, expr::Expr, @nospecialize(result_type))
end

result = emit_intrinsic!(ctx, func, call_args)
result === missing && _unsupported_call(ctx, func, call_args)
result === missing && _throw_method_error(ctx, [func; call_args])
validate_result_type(result, result_type, func)
return result
end
Expand All @@ -125,7 +131,7 @@ function emit_invoke!(ctx::CGCtx, expr::Expr, @nospecialize(result_type))
end

result = emit_intrinsic!(ctx, func, call_args)
result === missing && _unsupported_call(ctx, func, call_args)
result === missing && _throw_method_error(ctx, [func; call_args])
validate_result_type(result, result_type, func)
return result
end
Expand All @@ -138,7 +144,6 @@ Assert that the intrinsic returned a type compatible with what the IR expects.
function validate_result_type(@nospecialize(result), @nospecialize(expected_type), @nospecialize(func))
result === nothing && return # void return
result isa CGVal || return

actual = CC.widenconst(result.jltype)
expected = CC.widenconst(expected_type)

Expand All @@ -151,13 +156,15 @@ end
"""
_throw_method_error(ctx, call_args)

Provide a clear error message when Julia inserts a `throw_methoderror` call,
indicating that type inference found no matching method for a function call.
Provide a clear error message when an unsupported function is encountered during
Tile IR compilation, either from an explicit `throw_methoderror` / `MethodError`
construction, or from a function call with no Tile IR intrinsic mapping.

`call_args` contains `(function, arg1, arg2, ...)`.
"""
function _throw_method_error(ctx::CGCtx, call_args)
# call_args typically contains: (function, arg1, arg2, ...)
if isempty(call_args)
throw(IRError("MethodError during Tile IR compilation"))
throw(IRError("Unsupported function call during Tile IR compilation"))
end

func_val = try
Expand All @@ -168,18 +175,7 @@ function _throw_method_error(ctx::CGCtx, call_args)

argtypes = argextype.(Ref(ctx), call_args[2:end])
typestr = isempty(argtypes) ? "" : " with argument types ($(join(argtypes, ", ")))"
throw(IRError("MethodError during Tile IR compilation: no matching method for $func_val$typestr"))
end

"""
_unsupported_call(ctx, func, call_args)

Provide a clear error message when a function has no Tile IR intrinsic mapping.
"""
function _unsupported_call(ctx::CGCtx, @nospecialize(func), call_args)
argtypes = argextype.(Ref(ctx), call_args)
typestr = isempty(argtypes) ? "" : " with argument types ($(join(argtypes, ", ")))"
throw(IRError("Unsupported function call during Tile IR compilation: $func$typestr has no Tile IR equivalent"))
throw(IRError("Unsupported function call during Tile IR compilation: $func_val$typestr has no Tile IR equivalent"))
end

"""
Expand Down
30 changes: 28 additions & 2 deletions src/compiler/interpreter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ This is necessary because NativeInterpreter has a fixed method_table type parame
struct cuTileInterpreter <: CC.AbstractInterpreter
cache::CacheView
method_table::CC.CachedMethodTable{CC.OverlayMethodTable}
inf_cache::Vector{CC.InferenceResult}
inf_cache::@static isdefined(CC, :InferenceCache) ? CC.InferenceCache : Vector{CC.InferenceResult}
inf_params::CC.InferenceParams
opt_params::CC.OptimizationParams
end

function cuTileInterpreter(cache::CacheView; always_inline::Bool=true)
method_table = get_method_table_view(cache.world)
inf_cache = Vector{CC.InferenceResult}()
@static if isdefined(CC, :InferenceCache)
inf_cache = CC.InferenceCache()
else
inf_cache = Vector{CC.InferenceResult}()
end
inf_params = CC.InferenceParams()
opt_params = if always_inline
CC.OptimizationParams(; inline_cost_threshold=typemax(Int))
Expand Down Expand Up @@ -237,6 +241,28 @@ else # 1.11: synchronous, edges auto-tracked via stmt_edges
end
end

# Force inlining of all functions with source code.
#
# Julia 1.13+ changed inlining cost storage to encode costs into a UInt8 via
# jl_encode_inlining_cost. This lossy encoding saturates costs above ~5000 to
# MAX_INLINE_COST, making functions permanently non-inlineable regardless of the
# caller's inline_cost_threshold. Each cuTile intrinsic call is penalized at
# inline_nonleaf_penalty (1000), so functions with ≥5 intrinsic calls hit the
# ceiling.
#
# This override tells the inliner to always consider functions with available
# source code as inlineable, matching the behavior that our typemax(Int)
# inline_cost_threshold intends.
@static if VERSION >= v"1.13-"
function CC.src_inlining_policy(interp::cuTileInterpreter,
@nospecialize(src), @nospecialize(info::CC.CallInfo), stmt_flag::UInt32)
isa(src, CC.OptimizationState) && (src = src.src)
isa(src, CC.MaybeCompressed) && return true
isa(src, CC.IRCode) && return true
return false
end
end

# Disable semi-concrete interpretation (broken with overlays per JuliaLang/julia#47349)
function CC.concrete_eval_eligible(interp::cuTileInterpreter,
@nospecialize(f), result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.InferenceState)
Expand Down
9 changes: 7 additions & 2 deletions src/compiler/intrinsics/julia.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
# built-in: tuple (ghost — no runtime representation)
emit_intrinsic!(ctx::CGCtx, ::typeof(Core.tuple), args) = nothing

# built-in: isa (compile-time type narrowing)
emit_intrinsic!(ctx::CGCtx, ::typeof(isa), args) = nothing
# built-in: isa (compile-time type check, emitted as a tile constant)
function emit_intrinsic!(ctx::CGCtx, ::typeof(isa), args)
length(args) >= 2 || return nothing
T = @something get_constant(ctx, args[2]) return nothing
val_type = CC.widenconst(argextype(ctx, args[1]))
emit_constant!(ctx, val_type <: T, Tile{Bool, Tuple{}})
end

# built-in: donotdelete (keep-alive barrier — no Tile IR emission)
emit_intrinsic!(ctx::CGCtx, ::typeof(donotdelete), args) = nothing
7 changes: 6 additions & 1 deletion src/compiler/passes/canonicalize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@ function scalar_elim_block!(block::Block)
for inst in instructions(block)
call = resolve_call(block, stmt(inst))
call === nothing && continue
_, ops = call
func, ops = call

# isa is a scalar type check, not a tile operation — its result shape
# should not inherit from operands. (On Julia nightly, InferenceCache
# interactions can leave isa unresolved in the IR; see _combine_masks.)
func === isa && continue

current_type = value_type(inst)
current_type === nothing && continue
Expand Down
1 change: 1 addition & 0 deletions src/language/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

# mul_hi (high bits of integer multiply — no Core.Intrinsic equivalent)
@static if VERSION >= v"1.13-"
using Base: mul_hi
@overlay Base.mul_hi(x::T, y::T) where {T <: Signed} = Intrinsics.mulhii(x, y, Signedness.Signed)
@overlay Base.mul_hi(x::T, y::T) where {T <: Unsigned} = Intrinsics.mulhii(x, y, Signedness.Unsigned)
else
Expand Down
4 changes: 2 additions & 2 deletions test/codegen/integration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ end
isdefined(Core, :throw_methoderror) &&
@testset "mismatched tile shapes with + produces MethodError" begin
spec2d = ct.ArraySpec{2}(16, true)
@test_throws "MethodError during Tile IR compilation" begin
@test_throws "Unsupported function call during Tile IR compilation" begin
code_tiled(Tuple{ct.TileArray{Float32,2,spec2d}}) do a
pid = ct.bid(1)
tile_a = ct.load(a, pid, (4, 8))
Expand All @@ -574,7 +574,7 @@ end
isdefined(Core, :throw_methoderror) &&
@testset "no matching method produces MethodError" begin
only_ints(x::Int) = x
@test_throws "MethodError during Tile IR compilation" begin
@test_throws "Unsupported function call during Tile IR compilation" begin
code_tiled(Tuple{ct.TileArray{Float32,1,spec}}) do a
tile = ct.load(a, ct.bid(1), (16,))
only_ints(tile)
Expand Down
Loading