Thread EnzymeContext for world/MI lookup instead of LLVM attributes#3156
Open
vchuravy wants to merge 12 commits into
Open
Thread EnzymeContext for world/MI lookup instead of LLVM attributes#3156vchuravy wants to merge 12 commits into
vchuravy wants to merge 12 commits into
Conversation
Carry world and an MI cache on EnzymeContext, reachable from GradientUtils/TypeAnalyzer via new external-context accessors, and use them in place of per-function `enzymejl_world` attribute extraction (`enzyme_extract_world`) and attribute-scraping `enzyme_custom_extract_mi`. Drops the now-unused `emit_methodinstance!`. Also avoids a crash in the runtime-activity error path: `world` is now initialized from the context rather than left `nothing` (and a tautological assert removed), so reporting no longer asserts when the erroring value is neither an Instruction nor an Argument. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
|
Your PR requires formatting changes to meet the project's style guidelines. Click here to view the suggested changes.diff --git a/src/absint.jl b/src/absint.jl
index 10046717..c57a0494 100644
--- a/src/absint.jl
+++ b/src/absint.jl
@@ -13,7 +13,7 @@ function unbind(@nospecialize(val))
end
end
-function absint(@nospecialize(arg::LLVM.Value), partial::Bool = false, istracked::Bool=false, typetag::Bool=false, enzyme_context::Union{EnzymeContext, Nothing} = nothing)::Tuple{Bool, Any}
+function absint(@nospecialize(arg::LLVM.Value), partial::Bool = false, istracked::Bool = false, typetag::Bool = false, enzyme_context::Union{EnzymeContext, Nothing} = nothing)::Tuple{Bool, Any}
if (value_type(arg) == LLVM.PointerType(LLVM.StructType(LLVMType[]), Tracked)) || (value_type(arg) == LLVM.PointerType(LLVM.StructType(LLVMType[]), Derived)) || istracked
ce, _ = get_base_and_offset(arg; offsetAllowed = false, inttoptr = true)
if isa(ce, GlobalVariable)
@@ -474,7 +474,7 @@ function abs_typeof(
# Type tag is arg 3
if nm == "jl_alloc_genericmemory_unchecked" ||
nm == "ijl_alloc_genericmemory_unchecked"
- vals = absint(operands(arg)[3], partial, true, true, enzyme_context)
+ vals = absint(operands(arg)[3], partial, true, true, enzyme_context)
@assert !(vals[2] isa Core.Binding)
return (vals[1], vals[2], vals[1] ? GPUCompiler.MUT_REF : nothing)
end
diff --git a/src/api.jl b/src/api.jl
index ba479ce0..d357033d 100644
--- a/src/api.jl
+++ b/src/api.jl
@@ -580,7 +580,7 @@ EnzymeGetShadowType(width, T) =
ccall((:EnzymeGetShadowType, libEnzyme), LLVMTypeRef, (UInt64, LLVMTypeRef), width, T)
function EnzymeGradientUtilsGetExternalContext(gutils)
- ccall(
+ return ccall(
(:EnzymeGradientUtilsGetExternalContext, libEnzyme),
Ptr{Cvoid},
(EnzymeGradientUtilsRef,),
@@ -1369,7 +1369,7 @@ function EnzymeTypeAnalyzerToString(typeanalyzer)
end
function EnzymeTypeAnalyzerGetExternalContext(typeanalyzer)
- ccall(
+ return ccall(
(:EnzymeTypeAnalyzerGetExternalContext, libEnzyme),
Ptr{Cvoid},
(EnzymeTypeAnalyzerRef,),
diff --git a/src/compiler.jl b/src/compiler.jl
index a4905461..f2f0e315 100644
--- a/src/compiler.jl
+++ b/src/compiler.jl
@@ -249,12 +249,12 @@ include("compiler/utils.jl")
include("compiler/orcv2.jl")
import .Enzyme: GradientUtils, call_samefunc_with_inverted_bundles!,
- get_width, get_mode, get_runtime_activity,
- get_strong_zero, get_atomic_add, get_shadow_type, get_uncacheable,
- erase_with_placeholder, is_constant_value, is_constant_inst,
- new_from_original, lookup_value, invert_pointer, debug_from_orig!,
- add_reverse_block!, set_reverse_block!, enzyme_context, enzyme_gutils_context,
- batch_call_same_with_inverted_arg_if_active!
+ get_width, get_mode, get_runtime_activity,
+ get_strong_zero, get_atomic_add, get_shadow_type, get_uncacheable,
+ erase_with_placeholder, is_constant_value, is_constant_inst,
+ new_from_original, lookup_value, invert_pointer, debug_from_orig!,
+ add_reverse_block!, set_reverse_block!, enzyme_context, enzyme_gutils_context,
+ batch_call_same_with_inverted_arg_if_active!
# Julia function to LLVM stem and arity
const cmplx_known_ops =
@@ -532,7 +532,7 @@ include("typeutils/make_zero.jl")
function nested_codegen!(enzyme_context::EnzymeContext, mode::API.CDerivativeMode, mod::LLVM.Module, @nospecialize(f), @nospecialize(tt::Type))
funcspec = my_methodinstance(mode == API.DEM_ForwardMode ? Forward : Reverse, typeof(f), tt, enzyme_context.world)
- nested_codegen!(enzyme_context, mode, mod, funcspec)
+ return nested_codegen!(enzyme_context, mode, mod, funcspec)
end
function prepare_llvm(enzyme_context::EnzymeContext, interp, mod::LLVM.Module, job, meta)
@@ -1134,7 +1134,7 @@ end
return
end
-function set_module_types!(enzyme_context::EnzymeContext, interp, mod::LLVM.Module, primalf::Union{Nothing, LLVM.Function}, job, edges, run_enzyme, mode::API.CDerivativeMode)::Tuple{Dict{String,LLVM.API.LLVMLinkage}, HandlerState}
+function set_module_types!(enzyme_context::EnzymeContext, interp, mod::LLVM.Module, primalf::Union{Nothing, LLVM.Function}, job, edges, run_enzyme, mode::API.CDerivativeMode)::Tuple{Dict{String, LLVM.API.LLVMLinkage}, HandlerState}
for f in functions(mod)
if startswith(LLVM.name(f), "japi3") || startswith(LLVM.name(f), "japi1")
@@ -1325,7 +1325,7 @@ function nested_codegen!(
enzyme_context::EnzymeContext,
mode::API.CDerivativeMode,
mod::LLVM.Module,
- funcspec::Core.MethodInstance,
+ funcspec::Core.MethodInstance,
alwaysinline::Bool=false,
)
world = enzyme_context.world
@@ -1783,10 +1783,10 @@ function shadow_alloc_rewrite(V::LLVM.API.LLVMValueRef, gutils::API.EnzymeGradie
if nm == "jl_f_apply_type" || nm == "ijl_f_apply_type"
index += 1
found = Any[]
- legal, Ty = absint(operands(arg)[index], partial, false, false, enzyme_context(gutils))
+ legal, Ty = absint(operands(arg)[index], partial, false, false, enzyme_context(gutils))
Ty = unbind(Ty)
if legal && Ty == NTuple
- legal, Ty = absint(operands(arg)[index+2], false, false, false, enzyme_context(gutils))
+ legal, Ty = absint(operands(arg)[index + 2], false, false, false, enzyme_context(gutils))
Ty = unbind(Ty)
if legal
# count should represent {the total size in bytes, the aligned size of each element}
@@ -2815,7 +2815,7 @@ function enzyme!(
augmented,
width,
returnPrimal,
- shadow_init,
+ shadow_init,
interp,
runtimeActivity,
)
@@ -2857,7 +2857,7 @@ function enzyme!(
augmented,
width,
false,
- shadow_init,
+ shadow_init,
interp,
runtimeActivity
) #=returnPrimal=#
@@ -2898,7 +2898,7 @@ function enzyme!(
nothing,
width,
returnPrimal,
- shadow_init,
+ shadow_init,
interp,
runtimeActivity
)
@@ -2943,7 +2943,7 @@ function enzyme!(
nothing,
width,
returnPrimal,
- shadow_init,
+ shadow_init,
interp,
runtimeActivity
)
@@ -3026,7 +3026,7 @@ function create_abi_wrapper(
augmented,
width::Int,
returnPrimal::Bool,
- shadow_init::Bool,
+ shadow_init::Bool,
interp,
runtime_activity::Bool
)
@@ -4343,7 +4343,7 @@ end
# Modified from GPUCompiler/src/irgen.jl:365 lower_byval
function lower_convention(
- enzyme_context::EnzymeContext,
+ enzyme_context::EnzymeContext,
@nospecialize(functy::Type),
mod::LLVM.Module,
entry_f::LLVM.Function,
@@ -5714,7 +5714,7 @@ function GPUCompiler.compile_unhooked(output::Symbol, job::CompilerJob{<:EnzymeT
@static if VERSION < v"1.11-"
else
- legal2, obj = absint(inst, false, false, false, enzyme_context)
+ legal2, obj = absint(inst, false, false, false, enzyme_context)
obj = unbind(obj)
if legal2 && is_memory_instance(obj)
metadata(inst)["nonnull"] = MDNode(LLVM.Metadata[])
@@ -5963,7 +5963,7 @@ end
adjointf, augmented_primalf, TapeType = enzyme!(
enzyme_context,
job,
- interp,
+ interp,
mod,
primalf,
TT,
diff --git a/src/errors.jl b/src/errors.jl
index baeb5dbe..5ccc5f2d 100644
--- a/src/errors.jl
+++ b/src/errors.jl
@@ -1130,7 +1130,7 @@ function julia_error(
print(io, "Current scope: \n")
print(io, ir)
end
- legal, obj = absint(val, false, false, false, enzyme_context(gutils))
+ legal, obj = absint(val, false, false, false, enzyme_context(gutils))
if legal
obj0 = obj
obj = unbind(obj)
@@ -1434,7 +1434,7 @@ else
if isa(cur, LLVM.LoadInst)
larg, off = get_base_and_offset(operands(cur)[1])
if isa(larg, LLVM.LoadInst)
- legal2, obj = absint(larg, false, false, false, enzyme_context(gutils))
+ legal2, obj = absint(larg, false, false, false, enzyme_context(gutils))
obj = unbind(obj)
if legal2 && is_memory_instance(obj)
return make_batched(ncur, prevbb)
diff --git a/src/gradientutils.jl b/src/gradientutils.jl
index c917f241..dfac34d8 100644
--- a/src/gradientutils.jl
+++ b/src/gradientutils.jl
@@ -330,7 +330,7 @@ function batch_call_same_with_inverted_arg_if_active!(
width = get_width(gutils)
- void_rt = LLVM.value_type(orig) ==LLVM.VoidType()
+ void_rt = LLVM.value_type(orig) == LLVM.VoidType()
shadow = if !void_rt && need_result
ST = LLVM.LLVMType(API.EnzymeGetShadowType(width, LLVM.value_type(orig)))
LLVM.UndefValue(ST)::LLVM.Value
diff --git a/src/rules/jitrules.jl b/src/rules/jitrules.jl
index 6d562e9d..d12641ae 100644
--- a/src/rules/jitrules.jl
+++ b/src/rules/jitrules.jl
@@ -2405,9 +2405,9 @@ function common_apply_iterate_fwd(offset, B, orig, gutils, normalR, shadowR)
return true
end
- v, isiter = absint(operands(orig)[offset+1], false, false, false, enzyme_context(gutils))
+ v, isiter = absint(operands(orig)[offset + 1], false, false, false, enzyme_context(gutils))
isiter = unbind(isiter)
- v2, istup = absint(operands(orig)[offset+2], false, false, false, enzyme_context(gutils))
+ v2, istup = absint(operands(orig)[offset + 2], false, false, false, enzyme_context(gutils))
istup = unbind(istup)
width = get_width(gutils)
@@ -2558,8 +2558,8 @@ function common_apply_iterate_augfwd(offset, B, orig, gutils, normalR, shadowR,
return true
end
- v, isiter = absint(operands(orig)[offset+1], false, false, false, enzyme_context(gutils))
- v2, istup = absint(operands(orig)[offset+2], false, false, false, enzyme_context(gutils))
+ v, isiter = absint(operands(orig)[offset + 1], false, false, false, enzyme_context(gutils))
+ v2, istup = absint(operands(orig)[offset + 2], false, false, false, enzyme_context(gutils))
isiter = unbind(isiter)
istup = unbind(istup)
|
Member
|
@vchuravy can we do world first, and then mi later. Sole reason being that various other functions will copy an llvm function and those should take the mi too. World is the same everywhere so we don't need to be careful for a first look into |
c1ee065 to
f29884a
Compare
bf88838 to
1c25b7d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on top of #2643 (
vc/gutils_context).What
Use the
worldand a new MI cache carried onEnzymeContextinstead of scraping per-function LLVM attributes:EnzymeContextgains aworldand anmi_cache::Dict{String, Tuple{MethodInstance, Any}}(populated inprepare_llvm).GradientUtilsand theTypeAnalyzervia new external-context accessors (EnzymeGradientUtilsGetExternalContext,EnzymeTypeAnalyzerGetExternalContext), mirroring the existingLogicaccessor (which is renamed toenzyme_logic_context).absint/abs_typeof/enzyme_custom_extract_mitake the context and consult the cache, replacingenzyme_extract_world(fn)and attribute-only MI extraction at the call sites throughout the rules and compiler.emit_methodinstance!.Bug fix
The reverse-mode runtime-activity error path initialized
world = nothingand only assigned it whenvalwas anInstruction/Argument, then assertedworld == enzyme_context(gutils).world. For any otherval(e.g. a constant/global) this asserted instead of emitting the intendedEnzymeRuntimeActivityError.worldis now initialized from the context and the tautological assert is dropped.Notes
--no-verify: the whole-filerunicpre-commit hook wants to reformat large amounts of pre-existing non-runic code on the base branch. Changed lines should be checked with the diff-scopedgit runic vc/gutils_context.mi_cacheis keyed by LLVM function name, which is more fragile across modules than per-function attributes; and theET_IllegalTypeAnalysis/ET_NoTypebranches now require the analyzer's external context to be non-null.🤖 Generated with Claude Code