Skip to content
Closed
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
7 changes: 6 additions & 1 deletion base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ mutable struct InferenceState
# `max_valid`, to be used in inlining
matching_methods_cache::IdDict{Any, Tuple{Any, UInt, UInt}}

# The interpreter that created this inference state. Not looked at by
# NativeInterpreter. But other interpreters may use this to detect cycles
interp::AbstractInterpreter

Comment on lines +49 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a slight design smell that this other state is getting mixed into into our state here. Why isn't this part of the InferenceParams configuration values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's not really for configuration, it's so that the interpreter can detect that this InferenceState was not created by it and handle it appropriately.

# src is assumed to be a newly-allocated CodeInfo, that can be modified in-place to contain intermediate results
function InferenceState(result::InferenceResult, src::CodeInfo,
cached::Bool, interp::AbstractInterpreter)
Expand Down Expand Up @@ -107,7 +111,8 @@ mutable struct InferenceState
Vector{InferenceState}(), # callers_in_cycle
#=parent=#nothing,
cached, false, false, false,
IdDict{Any, Tuple{Any, UInt, UInt}}())
IdDict{Any, Tuple{Any, UInt, UInt}}(),
interp)
result.result = frame
cached && push!(get_inference_cache(interp), result)
return frame
Expand Down
12 changes: 8 additions & 4 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -439,21 +439,25 @@ function merge_call_chain!(parent::InferenceState, ancestor::InferenceState, chi
end
end

function is_same_frame(interp::AbstractInterpreter, linfo::MethodInstance, frame::InferenceState)
return linfo === frame.linfo
end

# Walk through `linfo`'s upstream call chain, starting at `parent`. If a parent
# frame matching `linfo` is encountered, then there is a cycle in the call graph
# (i.e. `linfo` is a descendant callee of itself). Upon encountering this cycle,
# we "resolve" it by merging the call chain, which entails unioning each intermediary
# frame's `callers_in_cycle` field and adding the appropriate backedges. Finally,
# we return `linfo`'s pre-existing frame. If no cycles are found, `nothing` is
# returned instead.
function resolve_call_cycle!(linfo::MethodInstance, parent::InferenceState)
function resolve_call_cycle!(interp::AbstractInterpreter, linfo::MethodInstance, parent::InferenceState)
frame = parent
uncached = false
limited = false
while isa(frame, InferenceState)
uncached |= !frame.cached # ensure we never add an uncached frame to a cycle
limited |= frame.limited
if frame.linfo === linfo
if is_same_frame(interp, linfo, frame)
if uncached
# our attempt to speculate into a constant call lead to an undesired self-cycle
# that cannot be converged: poison our call-stack (up to the discovered duplicate frame)
Expand All @@ -465,7 +469,7 @@ function resolve_call_cycle!(linfo::MethodInstance, parent::InferenceState)
return frame
end
for caller in frame.callers_in_cycle
if caller.linfo === linfo
if is_same_frame(interp, linfo, caller)
if uncached
poison_callstack(parent, frame, false)
return true
Expand Down Expand Up @@ -496,7 +500,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
# (if we asked resolve_call_cyle, it might instead detect that there is a cycle that it can't merge)
frame = false
else
frame = resolve_call_cycle!(mi, caller)
frame = resolve_call_cycle!(interp, mi, caller)
end
if frame === false
# completely new
Expand Down
8 changes: 6 additions & 2 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -983,18 +983,22 @@ struct CodegenParams

lookup::Ptr{Cvoid}

generic_context::Any

function CodegenParams(; track_allocations::Bool=true, code_coverage::Bool=true,
static_alloc::Bool=true, prefer_specsig::Bool=false,
gnu_pubnames=true, debug_info_kind::Cint = default_debug_info_kind(),
module_setup=nothing, module_activation=nothing, raise_exception=nothing,
emit_function=nothing, emitted_function=nothing,
lookup::Ptr{Cvoid}=cglobal(:jl_rettype_inferred))
lookup::Ptr{Cvoid}=cglobal(:jl_rettype_inferred),
generic_context = nothing)
return new(
Cint(track_allocations), Cint(code_coverage),
Cint(static_alloc), Cint(prefer_specsig),
Cint(gnu_pubnames), debug_info_kind,
module_setup, module_activation, raise_exception,
emit_function, emitted_function, lookup)
emit_function, emitted_function, lookup,
generic_context)
end
end

Expand Down
1 change: 1 addition & 0 deletions src/builtin_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ DECLARE_BUILTIN(typeof); DECLARE_BUILTIN(sizeof);
DECLARE_BUILTIN(issubtype); DECLARE_BUILTIN(isa);
DECLARE_BUILTIN(_apply); DECLARE_BUILTIN(_apply_pure);
DECLARE_BUILTIN(_apply_latest); DECLARE_BUILTIN(_apply_iterate);
DECLARE_BUILTIN(_invoke_codeinst);
DECLARE_BUILTIN(isdefined); DECLARE_BUILTIN(nfields);
DECLARE_BUILTIN(tuple); DECLARE_BUILTIN(svec);
DECLARE_BUILTIN(getfield); DECLARE_BUILTIN(setfield);
Expand Down
9 changes: 9 additions & 0 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,14 @@ JL_CALLABLE(jl_f__apply)
return do_apply(F, args, nargs, NULL);
}

JL_CALLABLE(jl_f__invoke_codeinst)
{
JL_NARGSV(_invoke_codeinst, 2);
JL_TYPECHK(_invoke_codeinst, code_instance, args[0]);
jl_code_instance_t *inst = (jl_code_instance_t*)args[0];
return inst->invoke(args[1], &args[2], nargs-2, inst);
}

// this is like `_apply`, but with quasi-exact checks to make sure it is pure
JL_CALLABLE(jl_f__apply_pure)
{
Expand Down Expand Up @@ -1523,6 +1531,7 @@ void jl_init_primitives(void) JL_GC_DISABLED
jl_builtin_apply_type = add_builtin_func("apply_type", jl_f_apply_type);
jl_builtin__apply = add_builtin_func("_apply", jl_f__apply);
jl_builtin__apply_iterate = add_builtin_func("_apply_iterate", jl_f__apply_iterate);
jl_builtin__invoke_codeinst = add_builtin_func("_invoke_codeinst", jl_f__invoke_codeinst);
jl_builtin__expr = add_builtin_func("_expr", jl_f__expr);
jl_builtin_svec = add_builtin_func("svec", jl_f_svec);
add_builtin_func("_apply_pure", jl_f__apply_pure);
Expand Down
16 changes: 13 additions & 3 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ static const std::map<jl_fptr_args_t, JuliaFunction*> builtin_func_map = {
{ &jl_f__apply_iterate, new JuliaFunction{"jl_f__apply_iterate", get_func_sig, get_func_attrs} },
{ &jl_f__apply_pure, new JuliaFunction{"jl_f__apply_pure", get_func_sig, get_func_attrs} },
{ &jl_f__apply_latest, new JuliaFunction{"jl_f__apply_latest", get_func_sig, get_func_attrs} },
{ &jl_f__invoke_codeinst, new JuliaFunction{"jl_f__invoke_codeinst", get_func_sig, get_func_attrs} },
{ &jl_f_throw, new JuliaFunction{"jl_f_throw", get_func_sig, get_func_attrs} },
{ &jl_f_tuple, jltuple_func },
{ &jl_f_svec, new JuliaFunction{"jl_f_svec", get_func_sig, get_func_attrs} },
Expand Down Expand Up @@ -821,7 +822,7 @@ extern "C" {
1,
#endif
jl_default_debug_info_kind, NULL, NULL, NULL, NULL, NULL,
jl_rettype_inferred };
jl_rettype_inferred, NULL };
}

template<typename T>
Expand Down Expand Up @@ -3379,7 +3380,15 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt)
return emit_intrinsic(ctx, fi, args, nargs - 1);
}

jl_cgval_t *argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs);
jl_value_t *context = ctx.params->generic_context == jl_nothing ? nullptr : ctx.params->generic_context;
size_t n_generic_args = nargs + (context ? 1 : 0);

jl_cgval_t *generic_argv = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * n_generic_args);
jl_cgval_t *argv = generic_argv;
if (context) {
generic_argv[0] = mark_julia_const(context);
argv = &generic_argv[1];
}
argv[0] = f;
for (size_t i = 1; i < nargs; ++i) {
argv[i] = emit_expr(ctx, args[i]);
Expand All @@ -3405,7 +3414,7 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt)
}

// emit function and arguments
Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, nargs, JLCALL_F_CC);
Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, generic_argv, n_generic_args, JLCALL_F_CC);
return mark_julia_type(ctx, callval, true, rt);
}

Expand Down Expand Up @@ -7425,6 +7434,7 @@ extern "C" void jl_init_llvm(void)
jl_default_cgparams.raise_exception = jl_nothing;
jl_default_cgparams.emit_function = jl_nothing;
jl_default_cgparams.emitted_function = jl_nothing;
jl_default_cgparams.generic_context = jl_nothing;
jl_init_debuginfo();

InitializeNativeTarget();
Expand Down
17 changes: 14 additions & 3 deletions src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,16 @@ void jl_jit_strings(jl_codegen_params_t::SymMapGV &stringConstants)
}
}


// this generates llvm code for the lambda info
// and adds the result to the jitlayers
// (and the shadow module),
// and generates code for it
static jl_callptr_t _jl_compile_codeinst(
extern "C" {
JL_DLLEXPORT jl_callptr_t jl_compile_codeinst(
jl_code_instance_t *codeinst,
jl_code_info_t *src,
size_t world)
size_t world,
const jl_cgparams_t *cg_params)
{
// TODO: Merge with jl_dump_compiles?
static ios_t f_precompile;
Expand All @@ -111,6 +112,7 @@ static jl_callptr_t _jl_compile_codeinst(
jl_codegen_params_t params;
params.cache = true;
params.world = world;
params.params = cg_params;
std::map<jl_code_instance_t*, jl_compile_result_t> emitted;
{
JL_TIMING(CODEGEN);
Expand Down Expand Up @@ -218,6 +220,15 @@ static jl_callptr_t _jl_compile_codeinst(
}
return fptr;
}
}

static jl_callptr_t _jl_compile_codeinst(
jl_code_instance_t *codeinst,
jl_code_info_t *src,
size_t world)
{
return jl_compile_codeinst(codeinst, src, world, &jl_default_cgparams);
}

void jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t &params);

Expand Down
4 changes: 4 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -2100,6 +2100,10 @@ typedef struct {

// Cache access. Default: jl_rettype_inferred.
jl_codeinstance_lookup_t lookup;

// If not `nothing`, rewrite all generic calls to call
// generic_context(f, args...) instead of f(args...).
jl_value_t *generic_context;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a default value in jl_default_cgparams?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Also I realize the comment is outdated, since I settled on nothing rather than null to indicate absence.

} jl_cgparams_t;
extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams;
extern JL_DLLEXPORT int jl_default_debug_info_kind;
Expand Down
6 changes: 4 additions & 2 deletions src/staticdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static void *const _tags[] = {
// some Core.Builtin Functions that we want to be able to reference:
&jl_builtin_throw, &jl_builtin_is, &jl_builtin_typeof, &jl_builtin_sizeof,
&jl_builtin_issubtype, &jl_builtin_isa, &jl_builtin_typeassert, &jl_builtin__apply,
&jl_builtin__apply_iterate,
&jl_builtin__apply_iterate, &jl_builtin__invoke_codeinst,
&jl_builtin_isdefined, &jl_builtin_nfields, &jl_builtin_tuple, &jl_builtin_svec,
&jl_builtin_getfield, &jl_builtin_setfield, &jl_builtin_fieldtype, &jl_builtin_arrayref,
&jl_builtin_const_arrayref, &jl_builtin_arrayset, &jl_builtin_arraysize,
Expand Down Expand Up @@ -117,7 +117,9 @@ void *native_functions;
// This is a manually constructed dual of the fvars array, which would be produced by codegen for Julia code, for C.
static const jl_fptr_args_t id_to_fptrs[] = {
&jl_f_throw, &jl_f_is, &jl_f_typeof, &jl_f_issubtype, &jl_f_isa,
&jl_f_typeassert, &jl_f__apply, &jl_f__apply_iterate, &jl_f__apply_pure, &jl_f__apply_latest, &jl_f_isdefined,
&jl_f_typeassert, &jl_f__apply, &jl_f__apply_iterate, &jl_f__apply_pure, &jl_f__apply_latest,
&jl_f__invoke_codeinst,
&jl_f_isdefined,
&jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, &jl_f_invoke_kwsorter,
&jl_f_getfield, &jl_f_setfield, &jl_f_fieldtype, &jl_f_nfields,
&jl_f_arrayref, &jl_f_const_arrayref, &jl_f_arrayset, &jl_f_arraysize, &jl_f_apply_type,
Expand Down