From db25494f62b78372c745053dc2a0644847bfab33 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 2 Jun 2024 20:47:51 +0000 Subject: [PATCH 1/3] codegen: start to remove the ability to call back into inference This is preparation for making codegen purely a transform of CodeInfo -> Module* without involving any of the caches (at the C++ level), so that this can instead be driven by caches managed entirely at the Julia inference level. Removes the test for code_llvm lookup behavior, since codegen no longer does lookup, so that is no longer relevant to test. --- base/compiler/typeinfer.jl | 14 ++-- base/opaque_closure.jl | 1 + base/reflection.jl | 15 ++-- doc/src/devdocs/ast.md | 34 ++++------ doc/src/devdocs/compiler.md | 14 ++-- src/aotcompile.cpp | 83 ++++++++--------------- src/codegen-stubs.c | 2 +- src/codegen.cpp | 21 +++--- src/disasm.cpp | 4 +- src/ircode.c | 5 ++ src/jitlayers.cpp | 47 +------------ src/jitlayers.h | 4 +- src/jltypes.c | 12 ++-- src/julia.h | 13 ++-- src/julia_internal.h | 2 +- src/method.c | 3 +- stdlib/InteractiveUtils/src/codeview.jl | 43 ++++++++---- stdlib/InteractiveUtils/test/runtests.jl | 9 +-- stdlib/Serialization/src/Serialization.jl | 27 +++----- test/compiler/AbstractInterpreter.jl | 69 ------------------- 20 files changed, 155 insertions(+), 267 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 5abc92ee19eb7..d1a1fa5ff8e3f 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -556,6 +556,9 @@ function finish(me::InferenceState, interp::AbstractInterpreter) me.result.result = bestguess ipo_effects = me.result.ipo_effects = me.ipo_effects = adjust_effects(me) me.result.exc_result = me.exc_bestguess = refine_exception_type(me.exc_bestguess, ipo_effects) + me.src.rettype = widenconst(ignorelimited(bestguess)) + me.src.min_world = first(me.valid_worlds) + me.src.max_world = last(me.valid_worlds) if limited_ret # a parent may be cached still, but not this intermediate work: @@ -933,6 +936,7 @@ function codeinfo_for_const(interp::AbstractInterpreter, mi::MethodInstance, @no tree.ssavaluetypes = 1 tree.debuginfo = DebugInfo(mi) tree.ssaflags = UInt32[0] + tree.rettype = Core.Typeof(val) set_inlineable!(tree, true) tree.parent = mi return tree @@ -965,15 +969,13 @@ typeinf_code(interp::AbstractInterpreter, method::Method, @nospecialize(atype), typeinf_code(interp, specialize_method(method, atype, sparams), run_optimizer) function typeinf_code(interp::AbstractInterpreter, mi::MethodInstance, run_optimizer::Bool) frame = typeinf_frame(interp, mi, run_optimizer) - frame === nothing && return nothing, Any - is_inferred(frame) || return nothing, Any + frame === nothing && return nothing + is_inferred(frame) || return nothing if result_is_constabi(interp, frame.result, run_optimizer) rt = frame.result.result::Const - return codeinfo_for_const(interp, frame.linfo, rt.val), widenconst(rt) + return codeinfo_for_const(interp, frame.linfo, rt.val) end - code = frame.src - rt = widenconst(ignorelimited(frame.result.result)) - return code, rt + return frame.src end """ diff --git a/base/opaque_closure.jl b/base/opaque_closure.jl index bd1cf4d5ae3fd..ef920c29b8d43 100644 --- a/base/opaque_closure.jl +++ b/base/opaque_closure.jl @@ -80,6 +80,7 @@ function Core.OpaqueClosure(ir::IRCode, @nospecialize env...; src.isva = isva src.nargs = nargtypes src = Core.Compiler.ir_to_codeinf!(src, ir) + src.rettype = rt return generate_opaque_closure(sig, Union{}, rt, src, nargs, isva, env...; kwargs...) end diff --git a/base/reflection.jl b/base/reflection.jl index 617ea76679cb6..3fead12f2eb8e 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1660,12 +1660,12 @@ function code_typed_by_type(@nospecialize(tt::Type); asts = [] for match in matches.matches match = match::Core.MethodMatch - (code, ty) = Core.Compiler.typeinf_code(interp, match, optimize) + code = Core.Compiler.typeinf_code(interp, match, optimize) if code === nothing push!(asts, match.method => Any) else debuginfo === :none && remove_linenums!(code) - push!(asts, code => ty) + push!(asts, code => code.rettype) end end return asts @@ -1682,14 +1682,19 @@ function get_oc_code_rt(oc::Core.OpaqueClosure, types, optimize::Bool) tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...} mi = Core.Compiler.specialize_method(m, tt, Core.svec()) interp = Core.Compiler.NativeInterpreter(m.primary_world) - return Core.Compiler.typeinf_code(interp, mi, optimize) + code = Core.Compiler.typeinf_code(interp, mi, optimize) + if code isa CodeInfo + return Pair{CodeInfo, Any}(code, code.rettype) + end + error("inference not successful") else code = _uncompressed_ir(m) - return Pair{CodeInfo,Any}(code, typeof(oc).parameters[2]) + return Pair{CodeInfo, Any}(code, typeof(oc).parameters[2]) end else # OC constructed from optimized IR codeinst = m.specializations.cache + # XXX: the inferred field is not normally a CodeInfo, but this assumes it is guaranteed to be always return Pair{CodeInfo, Any}(codeinst.inferred, codeinst.rettype) end else @@ -2209,7 +2214,7 @@ function print_statement_costs(io::IO, @nospecialize(tt::Type); for match in matches.matches match = match::Core.MethodMatch println(io, match.method) - (code, ty) = Core.Compiler.typeinf_code(interp, match, true) + code = Core.Compiler.typeinf_code(interp, match, true) if code === nothing println(io, " inference not successful") else diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index cbf3299a555af..56c09cfa57b72 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -660,7 +660,7 @@ for important details on how to modify these fields safely. ### CodeInfo -A (usually temporary) container for holding lowered source code. +A (usually temporary) container for holding lowered (and possibly inferred) source code. * `code` @@ -691,25 +691,18 @@ A (usually temporary) container for holding lowered source code. Statement-level 32 bits flags for each expression in the function. See the definition of `jl_code_info_t` in julia.h for more details. +These are only populated after inference (or by generated functions in some cases): + * `debuginfo` An object to retrieve source information for each statements, see [How to interpret line numbers in a `CodeInfo` object](@ref). -Optional Fields: - - * `slottypes` - - An array of types for the slots. - * `rettype` - The inferred return type of the lowered form (IR). Default value is `Any`. - - * `method_for_inference_limit_heuristics` - - The `method_for_inference_heuristics` will expand the given method's generator if - necessary during inference. + The inferred return type of the lowered form (IR). Default value is `Any`. This is + mostly present for convenience, as (due to the way OpaqueClosures work) it is not + necessarily the rettype used by codegen. * `parent` @@ -723,16 +716,19 @@ Optional Fields: The range of world ages for which this code was valid at the time when it had been inferred. +Optional Fields: -Boolean properties: + * `slottypes` - * `inferred` + An array of types for the slots. - Whether this has been produced by type inference. + * `method_for_inference_limit_heuristics` - * `inlineable` + The `method_for_inference_heuristics` will expand the given method's generator if + necessary during inference. - Whether this should be eligible for inlining. + +Boolean properties: * `propagate_inbounds` @@ -742,7 +738,7 @@ Boolean properties: `UInt8` settings: - * `constprop` + * `constprop`, `inlineable` * 0 = use heuristic * 1 = aggressive diff --git a/doc/src/devdocs/compiler.md b/doc/src/devdocs/compiler.md index 0749eafd81bd3..8f5f2bb1aa17c 100644 --- a/doc/src/devdocs/compiler.md +++ b/doc/src/devdocs/compiler.md @@ -94,11 +94,16 @@ Use appropriate care when copying. ## Specialized Calling Convention Signature Representation -A `jl_returninfo_t` object describes the calling convention details of any callable. +A `jl_returninfo_t` object describes the specialized calling convention details of any +callable. It can be generated from any (specTypes, rettype) pair, such as a CodeInstance, or +other place they are declared. This is the expected calling convention for specptr, but +other data may be stored there. Only if the function pointer stored there has the +expected specialized calling convention will the corresponding flag be set in specsigflags +to indicate it is useable. -If any of the arguments or return type of a method can be represented unboxed, -and the method is not varargs, it'll be given an optimized calling convention -signature based on its `specTypes` and `rettype` fields. +If any of the arguments or return type of a method can be represented unboxed, and none are +unable to be represented unboxed (such as an unbounded vararg), it will be given an +optimized calling convention signature based on the `specTypes` and `rettype` values. The general principles are that: @@ -112,4 +117,5 @@ The total logic for this is implemented by `get_specsig_function` and `deserves_ Additionally, if the return type is a union, it may be returned as a pair of values (a pointer and a tag). If the union values can be stack-allocated, then sufficient space to store them will also be passed as a hidden first argument. +If the struct to return needs gc roots, space for those will be passed as a hidden second argument. It is up to the callee whether the returned pointer will point to this space, a boxed object, or even other constant memory. diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 4156d33577f37..4567416b92b14 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1929,19 +1929,35 @@ void addTargetPasses(legacy::PassManagerBase *PM, const Triple &triple, TargetIR PM->add(createTargetTransformInfoWrapperPass(std::move(analysis))); } -// sometimes in GDB you want to find out what code was created from a mi +// sometimes in GDB you want to find out what code would be created from a mi extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instance_t *mi) { jl_llvmf_dump_t llvmf_dump; size_t world = jl_current_task->world_age; JL_STREAM *stream = (JL_STREAM*)STDERR_FILENO; + jl_code_info_t *src = NULL; + jl_value_t *ci = jl_default_cgparams.lookup(mi, world, world); + if (ci == jl_nothing) { + ci = (jl_value_t*)jl_type_infer(mi, world, 0, SOURCE_MODE_FORCE_SOURCE_UNCACHED); + } else { + ci = NULL; + } + JL_GC_PUSH2(&ci, &src); + if (ci) { + jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; + src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); + if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) { + src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); + } + } + jl_printf(stream, "---- dumping IR for ----\n"); jl_static_show(stream, (jl_value_t*)mi); jl_printf(stream, "\n----\n"); jl_printf(stream, "\n---- unoptimized IR ----"); - jl_get_llvmf_defn(&llvmf_dump, mi, world, 0, false, jl_default_cgparams); + jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, false, jl_default_cgparams); if (llvmf_dump.F) { jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source"); jl_static_show(stream, ir); @@ -1949,7 +1965,7 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc jl_printf(stream, "----\n"); jl_printf(stream, "\n---- optimized IR ----"); - jl_get_llvmf_defn(&llvmf_dump, mi, world, 0, true, jl_default_cgparams); + jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, true, jl_default_cgparams); if (llvmf_dump.F) { jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source"); jl_static_show(stream, ir); @@ -1957,69 +1973,28 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc jl_printf(stream, "----\n"); jl_printf(stream, "\n---- assembly ----"); - jl_get_llvmf_defn(&llvmf_dump, mi, world, 0, true, jl_default_cgparams); + jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, true, jl_default_cgparams); if (llvmf_dump.F) { jl_value_t *ir = jl_dump_function_asm(&llvmf_dump, 0, "", "source", 0, true); jl_static_show(stream, ir); } jl_printf(stream, "----\n"); + JL_GC_POP(); - jl_code_info_t *src = NULL; - jl_value_t *ci = jl_default_cgparams.lookup(mi, world, world); - if (ci == jl_nothing) { - ci = (jl_value_t*)jl_type_infer(mi, world, 0, SOURCE_MODE_FORCE_SOURCE_UNCACHED); - } else { - ci = NULL; - } - if (ci) { - jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; - src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); - if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) { - JL_GC_PUSH2(&codeinst, &src); - src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); - JL_GC_POP(); - } - } return src; } // --- native code info, and dump function to IR and ASM --- // Get pointer to llvm::Function instance, compiling if necessary // for use in reflection from Julia. -// This is paired with jl_dump_function_ir, jl_dump_function_asm, jl_dump_method_asm in particular ways: -// misuse will leak memory or cause read-after-free +// This is paired with jl_dump_function_ir and jl_dump_function_asm, either of which will free all memory allocated here extern "C" JL_DLLEXPORT_CODEGEN -void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, size_t world, char getwrapper, char optimize, const jl_cgparams_t params) +void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params) { - if (jl_is_method(mi->def.method) && mi->def.method->source == NULL && - mi->def.method->generator == NULL && !mi->def.method->is_for_opaque_closure) { - // not a generic function - dump->F = NULL; - return; - } - - // get the source code for this function - jl_code_info_t *src = NULL; - jl_code_instance_t *codeinst = NULL; - JL_GC_PUSH2(&src, &codeinst); - jl_value_t *ci = params.lookup(mi, world, world); - if (ci && ci != jl_nothing) { - codeinst = (jl_code_instance_t*)ci; - src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); - } - if (!src || (jl_value_t*)src == jl_nothing) { - codeinst = jl_type_infer(mi, world, 0, SOURCE_MODE_FORCE_SOURCE_UNCACHED); - if (codeinst) { - src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); - } - } - if (src) { - if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) - src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); - } - // emit this function into a new llvm module - if (codeinst && src && jl_is_code_info(src)) { + dump->F = nullptr; + dump->TSM = nullptr; + if (src && jl_is_code_info(src)) { auto ctx = jl_ExecutionEngine->getContext(); orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(mi), *ctx); uint64_t compiler_start_time = 0; @@ -2040,7 +2015,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz // This would also be nice, but it seems to cause OOMs on the windows32 builder // To get correct names in the IR this needs to be at least 2 output.debug_level = params.debug_info_level; - auto decls = jl_emit_code(m, mi, src, codeinst->rettype, output, jl_atomic_load_relaxed(&codeinst->min_world), jl_atomic_load_relaxed(&codeinst->max_world)); + auto decls = jl_emit_code(m, mi, src, output); JL_UNLOCK(&jl_codegen_lock); // Might GC Function *F = NULL; @@ -2091,7 +2066,6 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz fname = &decls.functionObject; F = cast(m.getModuleUnlocked()->getNamedValue(*fname)); } - JL_GC_POP(); if (measure_compile_time_enabled) { auto end = jl_hrtime(); jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); @@ -2102,7 +2076,4 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz return; } } - - const char *mname = name_from_method_instance(mi); - jl_errorf("unable to compile source for function %s", mname); } diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index e67ae3b75ea76..3e97c149bffe3 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -20,7 +20,7 @@ JL_DLLEXPORT void jl_extern_c_fallback(jl_function_t *f, jl_value_t *rt, jl_valu JL_DLLEXPORT jl_value_t *jl_dump_method_asm_fallback(jl_method_instance_t *linfo, size_t world, char emit_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary) UNAVAILABLE JL_DLLEXPORT jl_value_t *jl_dump_function_ir_fallback(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo) UNAVAILABLE -JL_DLLEXPORT void jl_get_llvmf_defn_fallback(jl_llvmf_dump_t *dump, jl_method_instance_t *linfo, size_t world, char getwrapper, char optimize, const jl_cgparams_t params) UNAVAILABLE +JL_DLLEXPORT void jl_get_llvmf_defn_fallback(jl_llvmf_dump_t *dump, jl_method_instance_t *linfo, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params) UNAVAILABLE JL_DLLEXPORT void *jl_LLVMCreateDisasm_fallback(const char *TripleName, void *DisInfo, int TagType, void *GetOpInfo, void *SymbolLookUp) UNAVAILABLE JL_DLLEXPORT size_t jl_LLVMDisasmInstruction_fallback(void *DC, uint8_t *Bytes, uint64_t BytesSize, uint64_t PC, char *OutString, size_t OutStringSize) UNAVAILABLE diff --git a/src/codegen.cpp b/src/codegen.cpp index 86532021fbb40..9094e0ba80b1a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4076,9 +4076,8 @@ static jl_llvm_functions_t orc::ThreadSafeModule &TSM, jl_method_instance_t *lam, jl_code_info_t *src, - jl_value_t *jlrettype, - jl_codegen_params_t ¶ms, - size_t min_world, size_t max_world); + jl_value_t *rettype, + jl_codegen_params_t ¶ms); static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_sym_t *type, jl_cgval_t name); @@ -6216,7 +6215,7 @@ static std::pair get_oc_function(jl_codectx_t &ctx, jl_met orc::ThreadSafeModule closure_m = jl_create_ts_module( name_from_method_instance(mi), ctx.emission_context.tsctx, jl_Module->getDataLayout(), Triple(jl_Module->getTargetTriple())); - jl_llvm_functions_t closure_decls = emit_function(closure_m, mi, ir, rettype, ctx.emission_context, ctx.min_world, ctx.max_world); + jl_llvm_functions_t closure_decls = emit_function(closure_m, mi, ir, rettype, ctx.emission_context); JL_GC_POP(); it = ctx.emission_context.compiled_functions.insert(std::make_pair(ci, std::make_pair(std::move(closure_m), std::move(closure_decls)))).first; } @@ -8088,11 +8087,12 @@ static jl_llvm_functions_t jl_method_instance_t *lam, jl_code_info_t *src, jl_value_t *jlrettype, - jl_codegen_params_t ¶ms, - size_t min_world, size_t max_world) + jl_codegen_params_t ¶ms) { ++EmittedFunctions; // step 1. unpack AST and allocate codegen context for this function + size_t min_world = src->min_world; + size_t max_world = src->max_world; jl_llvm_functions_t declarations; jl_codectx_t ctx(*params.tsctx.getContext(), params, min_world, max_world); jl_datatype_t *vatyp = NULL; @@ -9733,9 +9733,7 @@ jl_llvm_functions_t jl_emit_code( orc::ThreadSafeModule &m, jl_method_instance_t *li, jl_code_info_t *src, - jl_value_t *jlrettype, - jl_codegen_params_t ¶ms, - size_t min_world, size_t max_world) + jl_codegen_params_t ¶ms) { JL_TIMING(CODEGEN, CODEGEN_LLVM); jl_timing_show_func_sig((jl_value_t *)li->specTypes, JL_TIMING_DEFAULT_BLOCK); @@ -9745,7 +9743,7 @@ jl_llvm_functions_t jl_emit_code( compare_cgparams(params.params, &jl_default_cgparams)) && "functions compiled with custom codegen params must not be cached"); JL_TRY { - decls = emit_function(m, li, src, jlrettype, params, min_world, max_world); + decls = emit_function(m, li, src, src->rettype, params); auto stream = *jl_ExecutionEngine->get_dump_emitted_mi_name_stream(); if (stream) { jl_printf(stream, "%s\t", decls.specFunctionObject.c_str()); @@ -9829,8 +9827,7 @@ jl_llvm_functions_t jl_emit_codeinst( } } assert(jl_egal((jl_value_t*)jl_atomic_load_relaxed(&codeinst->debuginfo), (jl_value_t*)src->debuginfo) && "trying to generate code for a codeinst for an incompatible src"); - jl_llvm_functions_t decls = jl_emit_code(m, codeinst->def, src, codeinst->rettype, params, - jl_atomic_load_relaxed(&codeinst->min_world), jl_atomic_load_relaxed(&codeinst->max_world)); + jl_llvm_functions_t decls = jl_emit_code(m, codeinst->def, src, params); const std::string &specf = decls.specFunctionObject; const std::string &f = decls.functionObject; diff --git a/src/disasm.cpp b/src/disasm.cpp index 8df09fde02ae1..b24c374607113 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -499,7 +499,7 @@ jl_value_t *jl_dump_function_ir_impl(jl_llvmf_dump_t *dump, char strip_ir_metada std::string code; raw_string_ostream stream(code); - { + if (dump->F) { //RAII will release the module auto TSM = std::unique_ptr(unwrap(dump->TSM)); //If TSM is not passed in, then the context MUST be locked externally. @@ -1204,7 +1204,7 @@ jl_value_t *jl_dump_function_asm_impl(jl_llvmf_dump_t* dump, char emit_mc, const { // precise printing via IR assembler SmallVector ObjBufferSV; - { // scope block + if (dump->F) { // scope block also auto TSM = std::unique_ptr(unwrap(dump->TSM)); llvm::raw_svector_ostream asmfile(ObjBufferSV); TSM->withModuleDo([&](Module &m) { diff --git a/src/ircode.c b/src/ircode.c index 50ab603ebe94b..873d33d2d7523 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -1010,6 +1010,11 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t JL_GC_POP(); if (metadata) { code->parent = metadata->def; + jl_gc_wb(code, code->parent); + code->rettype = metadata->rettype; + jl_gc_wb(code, code->rettype); + code->min_world = jl_atomic_load_relaxed(&metadata->min_world); + code->max_world = jl_atomic_load_relaxed(&metadata->max_world); } return code; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index a5792de5eb501..b32f211c1ad8c 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -557,54 +557,13 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world, jl_code_instance_t *codeinst = jl_compile_method_internal(mi, world); if (codeinst) { uintptr_t fptr = (uintptr_t)jl_atomic_load_acquire(&codeinst->invoke); - if (getwrapper) - return jl_dump_fptr_asm(fptr, emit_mc, asm_variant, debuginfo, binary); uintptr_t specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); - if (fptr == (uintptr_t)jl_fptr_const_return_addr && specfptr == 0) { - // normally we prevent native code from being generated for these functions, - // (using sentinel value `1` instead) - // so create an exception here so we can print pretty our lies - auto ct = jl_current_task; - bool timed = (ct->reentrant_timing & 1) == 0; - if (timed) - ct->reentrant_timing |= 1; - uint64_t compiler_start_time = 0; - uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled); - if (measure_compile_time_enabled) - compiler_start_time = jl_hrtime(); - JL_LOCK(&jl_codegen_lock); // also disables finalizers, to prevent any unexpected recursion - specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr); - if (specfptr == 0) { - // Doesn't need SOURCE_MODE_FORCE_SOURCE_UNCACHED, because the codegen lock is held, - // so there's no concern that the ->inferred field will be deleted. - jl_code_instance_t *forced_ci = jl_type_infer(mi, world, 0, SOURCE_MODE_FORCE_SOURCE); - JL_GC_PUSH1(&forced_ci); - if (forced_ci) { - // Force compile of this codeinst even though it already has an ->invoke - _jl_compile_codeinst(forced_ci, NULL, *jl_ExecutionEngine->getContext()); - specfptr = (uintptr_t)jl_atomic_load_relaxed(&forced_ci->specptr.fptr); - } - JL_GC_POP(); - } - JL_UNLOCK(&jl_codegen_lock); - if (timed) { - if (measure_compile_time_enabled) { - auto end = jl_hrtime(); - jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time); - } - ct->reentrant_timing &= ~1ull; - } - } + if (getwrapper || specfptr == 0) + specfptr = fptr; if (specfptr != 0) return jl_dump_fptr_asm(specfptr, emit_mc, asm_variant, debuginfo, binary); } - - // whatever, that didn't work - use the assembler output instead - jl_llvmf_dump_t llvmf_dump; - jl_get_llvmf_defn(&llvmf_dump, mi, world, getwrapper, true, jl_default_cgparams); - if (!llvmf_dump.F) - return jl_an_empty_string; - return jl_dump_function_asm(&llvmf_dump, emit_mc, asm_variant, debuginfo, binary, false); + return jl_an_empty_string; } CodeGenOpt::Level CodeGenOptLevelFor(int optlevel) diff --git a/src/jitlayers.h b/src/jitlayers.h index a849148653f07..4f05db50f1388 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -261,9 +261,7 @@ jl_llvm_functions_t jl_emit_code( orc::ThreadSafeModule &M, jl_method_instance_t *mi, jl_code_info_t *src, - jl_value_t *jlrettype, - jl_codegen_params_t ¶ms, - size_t min_world, size_t max_world); + jl_codegen_params_t ¶ms); jl_llvm_functions_t jl_emit_codeinst( orc::ThreadSafeModule &M, diff --git a/src/jltypes.c b/src/jltypes.c index 79cb787ab042f..59807226fb4a9 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3313,7 +3313,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_code_info_type = jl_new_datatype(jl_symbol("CodeInfo"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(21, + jl_perm_symsvec(22, "code", "debuginfo", "ssavaluetypes", @@ -3321,11 +3321,12 @@ void jl_init_types(void) JL_GC_DISABLED "slotnames", "slotflags", "slottypes", + "rettype", "parent", - "method_for_inference_limit_heuristics", "edges", "min_world", "max_world", + "method_for_inference_limit_heuristics", "nargs", "propagate_inbounds", "has_fcall", @@ -3335,7 +3336,7 @@ void jl_init_types(void) JL_GC_DISABLED "constprop", "purity", "inlining_cost"), - jl_svec(21, + jl_svec(22, jl_array_any_type, jl_debuginfo_type, jl_any_type, @@ -3345,9 +3346,10 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_any_type, jl_any_type, - jl_any_type, + jl_any_type, // prefers svec, but tolerates Vector{Any} jl_ulong_type, jl_ulong_type, + jl_any_type, jl_ulong_type, jl_bool_type, jl_bool_type, @@ -3358,7 +3360,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_uint16_type, jl_uint16_type), jl_emptysvec, - 0, 1, 21); + 0, 1, 22); jl_method_type = jl_new_datatype(jl_symbol("Method"), core, diff --git a/src/julia.h b/src/julia.h index 4e6f1fde882d9..4534d00caa888 100644 --- a/src/julia.h +++ b/src/julia.h @@ -309,17 +309,18 @@ typedef struct _jl_code_info_t { // miscellaneous data: jl_array_t *slotnames; // names of local variables jl_array_t *slotflags; // local var bit flags - // the following are optional transient properties (not preserved by compression--as they typically get stored elsewhere): + // the following is a deprecated property (not preserved by compression) jl_value_t *slottypes; // inferred types of slots + // more inferred data: + jl_value_t *rettype; // return type relevant for fptr jl_method_instance_t *parent; // context (after inference, otherwise nothing) + // the following are required to cache the method correctly + jl_value_t *edges; // forward edge info (svec preferred, but tolerates Array{Any} and nothing token) + size_t min_world; + size_t max_world; // These may be used by generated functions to further constrain the resulting inputs. - // They are not used by any other part of the system and may be moved elsewhere in the - // future. jl_value_t *method_for_inference_limit_heuristics; // optional method used during inference - jl_value_t *edges; // forward edges to method instances that must be invalidated (for copying to debuginfo) - size_t min_world; - size_t max_world; size_t nargs; // various boolean properties: diff --git a/src/julia_internal.h b/src/julia_internal.h index 62506cb6c43ae..fdd085863d80e 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1753,7 +1753,7 @@ typedef struct { JL_DLLIMPORT jl_value_t *jl_dump_method_asm(jl_method_instance_t *linfo, size_t world, char emit_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary); -JL_DLLIMPORT void jl_get_llvmf_defn(jl_llvmf_dump_t* dump, jl_method_instance_t *linfo, size_t world, char getwrapper, char optimize, const jl_cgparams_t params); +JL_DLLIMPORT void jl_get_llvmf_defn(jl_llvmf_dump_t* dump, jl_method_instance_t *linfo, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params); JL_DLLIMPORT jl_value_t *jl_dump_fptr_asm(uint64_t fptr, char emit_mc, const char* asm_variant, const char *debuginfo, char binary); JL_DLLIMPORT jl_value_t *jl_dump_function_ir(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo); JL_DLLIMPORT jl_value_t *jl_dump_function_asm(jl_llvmf_dump_t *dump, char emit_mc, const char* asm_variant, const char *debuginfo, char binary, char raw); diff --git a/src/method.c b/src/method.c index 59c24671f46f3..cb426514a6d54 100644 --- a/src/method.c +++ b/src/method.c @@ -627,7 +627,7 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void) jl_task_t *ct = jl_current_task; jl_code_info_t *src = (jl_code_info_t*)jl_gc_alloc(ct->ptls, sizeof(jl_code_info_t), - jl_code_info_type); + jl_code_info_type); src->code = NULL; src->debuginfo = NULL; src->ssavaluetypes = NULL; @@ -636,6 +636,7 @@ JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void) src->slotflags = NULL; src->slotnames = NULL; src->slottypes = jl_nothing; + src->rettype = (jl_value_t*)jl_any_type; src->parent = (jl_method_instance_t*)jl_nothing; src->min_world = 1; src->max_world = ~(size_t)0; diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index 82b3b9e936113..9f1538cd4a7fe 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -162,11 +162,15 @@ function code_warntype(io::IO, @nospecialize(f), @nospecialize(tt=Base.default_t matches === nothing && Base.raise_match_failure(:code_warntype, tt) for match in matches.matches match = match::Core.MethodMatch - (src, rettype) = Core.Compiler.typeinf_code(interp, match, optimize) + src = Core.Compiler.typeinf_code(interp, match, optimize) mi = Core.Compiler.specialize_method(match) mi.def isa Method && (nargs = (mi.def::Method).nargs) print_warntype_mi(io, mi) - print_warntype_codeinfo(io, src, rettype, nargs; lineprinter) + if src isa Core.CodeInfo + print_warntype_codeinfo(io, src, src.rettype, nargs; lineprinter) + else + println(io, " inference not successful") + end end nothing end @@ -210,7 +214,6 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe Core.Compiler.hasintersect(typeof(f).parameters[1], tt) || (warning = OC_MISMATCH_WARNING) else mi = Core.Compiler.specialize_method(f.source, Tuple{typeof(f.captures), tt.parameters...}, Core.svec()) - actual = isdispatchtuple(mi.specTypes) isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING) end end @@ -224,15 +227,29 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe if syntax !== :att && syntax !== :intel throw(ArgumentError("'syntax' must be either :intel or :att")) end - if dump_module - # we want module metadata, so use LLVM to generate assembly output - str = _dump_function_native_assembly(mi, world, wrapper, syntax, debuginfo, binary, raw, params) - else - # if we don't want the module metadata, just disassemble what our JIT has + str = "" + if !dump_module + # if we don't want the module metadata, attempt to disassemble what our JIT has str = _dump_function_native_disassembly(mi, world, wrapper, syntax, debuginfo, binary) end + if isempty(str) + # if that failed (or we want metadata), use LLVM to generate more accurate assembly output + if !isa(f, Core.OpaqueClosure) + src = Core.Compiler.typeinf_code(Core.Compiler.NativeInterpreter(world), mi, true) + else + src, rt = Base.get_oc_code_rt(f, tt, true) + end + src isa Core.CodeInfo || error("failed to infer source for $mi") + str = _dump_function_native_assembly(mi, src, wrapper, syntax, debuginfo, binary, raw, params) + end else - str = _dump_function_llvm(mi, world, wrapper, !raw, dump_module, optimize, debuginfo, params) + if !isa(f, Core.OpaqueClosure) + src = Core.Compiler.typeinf_code(Core.Compiler.NativeInterpreter(world), mi, true) + else + src, rt = Base.get_oc_code_rt(f, tt, true) + end + src isa Core.CodeInfo || error("failed to infer source for $mi") + str = _dump_function_llvm(mi, src, wrapper, !raw, dump_module, optimize, debuginfo, params) end str = warning * str return str @@ -252,11 +269,11 @@ struct LLVMFDump f::Ptr{Cvoid} # opaque end -function _dump_function_native_assembly(mi::Core.MethodInstance, world::UInt, +function _dump_function_native_assembly(mi::Core.MethodInstance, src::Core.CodeInfo, wrapper::Bool, syntax::Symbol, debuginfo::Symbol, binary::Bool, raw::Bool, params::CodegenParams) llvmf_dump = Ref{LLVMFDump}() - @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump},mi::Any, world::UInt, wrapper::Bool, + @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any, wrapper::Bool, true::Bool, params::CodegenParams)::Cvoid llvmf_dump[].f == C_NULL && error("could not compile the specified method") str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool, @@ -266,12 +283,12 @@ function _dump_function_native_assembly(mi::Core.MethodInstance, world::UInt, end function _dump_function_llvm( - mi::Core.MethodInstance, world::UInt, wrapper::Bool, + mi::Core.MethodInstance, src::Core.CodeInfo, wrapper::Bool, strip_ir_metadata::Bool, dump_module::Bool, optimize::Bool, debuginfo::Symbol, params::CodegenParams) llvmf_dump = Ref{LLVMFDump}() - @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, world::UInt, + @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, src::Any, wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid llvmf_dump[].f == C_NULL && error("could not compile the specified method") str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool, diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index c8d2fd6db348a..12e4c201daaae 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -393,23 +393,24 @@ let errf = tempname(), new_stderr = open(errf, "w") try redirect_stderr(new_stderr) + @test occursin("f_broken_code", sprint(code_native, h_broken_code, ())) println(new_stderr, "start") flush(new_stderr) - @test occursin("h_broken_code", sprint(code_native, h_broken_code, ())) + @test_throws "could not compile the specified method" sprint(code_native, f_broken_code, ()) Libc.flush_cstdio() println(new_stderr, "end") flush(new_stderr) - @eval @test g_broken_code() == 0 + @test invokelatest(g_broken_code) == 0 finally + Libc.flush_cstdio() redirect_stderr(old_stderr) close(new_stderr) let errstr = read(errf, String) @test startswith(errstr, """start - end Internal error: encountered unexpected error during compilation of f_broken_code: ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """) || errstr - @test !endswith(errstr, "\nend\n") || errstr + @test endswith(errstr, "\nend\n") || errstr end rm(errf) end diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index c8b5314fe719d..7600457812f66 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1231,26 +1231,21 @@ function deserialize(s::AbstractSerializer, ::Type{CodeInfo}) if !pre_12 ci.slotflags = deserialize(s) ci.slottypes = deserialize(s) - if format_version(s) <= 26 - deserialize(s) # rettype - ci.parent = deserialize(s) - world_or_edges = deserialize(s) - pre_13 = isa(world_or_edges, Union{UInt, Int}) - if pre_13 - ci.min_world = reinterpret(UInt, world_or_edges) - ci.max_world = reinterpret(UInt, deserialize(s)) - else - ci.edges = world_or_edges - ci.min_world = deserialize(s)::UInt - ci.max_world = deserialize(s)::UInt - end + ci.rettype = deserialize(s) + ci.parent = deserialize(s) + world_or_edges = deserialize(s) + pre_13 = isa(world_or_edges, Union{UInt, Int}) + if pre_13 + ci.min_world = reinterpret(UInt, world_or_edges) + ci.max_world = reinterpret(UInt, deserialize(s)) else - ci.parent = deserialize(s) - ci.method_for_inference_limit_heuristics = deserialize(s) - ci.edges = deserialize(s) + ci.edges = world_or_edges ci.min_world = deserialize(s)::UInt ci.max_world = deserialize(s)::UInt end + if format_version(s) >= 26 + ci.method_for_inference_limit_heuristics = deserialize(s) + end end if format_version(s) <= 26 deserialize(s)::Bool # inferred diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 7ffb7c61abcc1..4c468d1504bef 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -427,75 +427,6 @@ end Core.eval(Core.Compiler, quote f(;a=1) = a end) @test_throws MethodError Core.Compiler.f(;b=2) -# Custom lookup function -# ====================== - -# In the following test with `ConstInvokeInterp`, we use a custom lookup function that -# uses const-prop'ed source if available, and check if LLVM emits code using it. - -using Core: MethodInstance, CodeInstance -using Base: CodegenParams -using InteractiveUtils - -@newinterp ConstInvokeInterp -function CC.concrete_eval_eligible(interp::ConstInvokeInterp, - @nospecialize(f), result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.AbsIntState) - ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, - f::Any, result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.AbsIntState) - if ret === :semi_concrete_eval - return :none # disable semi-concrete interpretation - end - return ret -end -Base.@constprop :aggressive @noinline function custom_lookup_target(c::Bool, x::Int) - if c - y = sin(x) - z = nothing - else - y = cos(x) - z = missing - end - return y, z -end -custom_lookup_context(x::Int) = custom_lookup_target(true, x) - -const CONST_INVOKE_INTERP_WORLD = Base.get_world_counter() -const CONST_INVOKE_INTERP = ConstInvokeInterp(; world=CONST_INVOKE_INTERP_WORLD) -function custom_lookup(mi::MethodInstance, min_world::UInt, max_world::UInt) - for inf_result in CONST_INVOKE_INTERP.inf_cache - if inf_result.linfo === mi - if CC.any(inf_result.overridden_by_const) - return CodeInstance(CONST_INVOKE_INTERP, inf_result) - end - end - end - # XXX: This seems buggy, custom_lookup should probably construct the absint on demand. - return CC.getindex(CC.code_cache(CONST_INVOKE_INTERP), mi) -end - -let # generate cache - code_typed(custom_lookup_context; world=CONST_INVOKE_INTERP_WORLD, interp=CONST_INVOKE_INTERP) - - # check if the lookup function works as expected - target_mi = CC.specialize_method(only(methods(custom_lookup_target)), Tuple{typeof(custom_lookup_target),Bool,Int}, Core.svec()) - target_ci = custom_lookup(target_mi, CONST_INVOKE_INTERP_WORLD, CONST_INVOKE_INTERP_WORLD) - @test target_ci.rettype == Tuple{Float64,Nothing} # constprop'ed source - - raw = false - lookup = @cfunction(custom_lookup, Any, (Any,Csize_t,Csize_t)) - params = CodegenParams(; - debug_info_kind=Cint(0), - debug_info_level=Cint(2), - safepoint_on_entry=raw, - gcstack_arg=raw, - lookup) - io = IOBuffer() - code_llvm(io, custom_lookup_target, (Bool,Int,); params) - s = String(take!(io)) - @test occursin("j_sin_", s) - @test !occursin("j_cos_", s) -end - # custom inferred data # ==================== From 12fb821dacdbd9a93a8bd83f2836b004dcc73a75 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 3 Jun 2024 00:23:08 +0000 Subject: [PATCH 2/3] gdb: rewrite jl_gdbdumpcode reflection helper using reflection instead of jl_type_infer Removes last remaining use of SOURCE_MODE_FORCE_SOURCE_UNCACHED, allowing it to be eliminated. --- src/aotcompile.cpp | 38 ++++++++++++++------------------------ src/gf.c | 38 ++++++++++++++++++++++++++++++++++++++ src/julia_internal.h | 1 + 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 4567416b92b14..1d7bbd710f26b 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1936,49 +1936,39 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc size_t world = jl_current_task->world_age; JL_STREAM *stream = (JL_STREAM*)STDERR_FILENO; - jl_code_info_t *src = NULL; - jl_value_t *ci = jl_default_cgparams.lookup(mi, world, world); - if (ci == jl_nothing) { - ci = (jl_value_t*)jl_type_infer(mi, world, 0, SOURCE_MODE_FORCE_SOURCE_UNCACHED); - } else { - ci = NULL; - } - JL_GC_PUSH2(&ci, &src); - if (ci) { - jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; - src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred); - if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method)) { - src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src); - } - } + jl_code_info_t *src = jl_gdbcodetyped1(mi, world); + JL_GC_PUSH1(&src); jl_printf(stream, "---- dumping IR for ----\n"); jl_static_show(stream, (jl_value_t*)mi); jl_printf(stream, "\n----\n"); - jl_printf(stream, "\n---- unoptimized IR ----"); + jl_printf(stream, "\n---- unoptimized IR ----\n"); jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, false, jl_default_cgparams); if (llvmf_dump.F) { jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source"); - jl_static_show(stream, ir); + if (ir != NULL && jl_is_string(ir)) + jl_printf(stream, "%s", jl_string_data(ir)); } - jl_printf(stream, "----\n"); + jl_printf(stream, "\n----\n"); - jl_printf(stream, "\n---- optimized IR ----"); + jl_printf(stream, "\n---- optimized IR ----\n"); jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, true, jl_default_cgparams); if (llvmf_dump.F) { jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source"); - jl_static_show(stream, ir); + if (ir != NULL && jl_is_string(ir)) + jl_printf(stream, "%s", jl_string_data(ir)); } - jl_printf(stream, "----\n"); + jl_printf(stream, "\n----\n"); - jl_printf(stream, "\n---- assembly ----"); + jl_printf(stream, "\n---- assembly ----\n"); jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, true, jl_default_cgparams); if (llvmf_dump.F) { jl_value_t *ir = jl_dump_function_asm(&llvmf_dump, 0, "", "source", 0, true); - jl_static_show(stream, ir); + if (ir != NULL && jl_is_string(ir)) + jl_printf(stream, "%s", jl_string_data(ir)); } - jl_printf(stream, "----\n"); + jl_printf(stream, "\n----\n"); JL_GC_POP(); return src; diff --git a/src/gf.c b/src/gf.c index 423a6bf928929..e5a33ecf68c5d 100644 --- a/src/gf.c +++ b/src/gf.c @@ -430,6 +430,44 @@ jl_code_instance_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int fo return ci; } +// Attempt to run `Core.Compiler.code_typed` on the lambda "mi" +JL_DLLEXPORT jl_code_info_t *jl_gdbcodetyped1(jl_method_instance_t *mi, size_t world) +{ + jl_task_t *ct = jl_current_task; + jl_code_info_t *ci = NULL; + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif + int last_pure = ct->ptls->in_pure_callback; + ct->ptls->in_pure_callback = 0; + size_t last_age = ct->world_age; + ct->world_age = jl_typeinf_world; + jl_value_t **fargs; + JL_GC_PUSHARGS(fargs, 4); + jl_module_t *CC = (jl_module_t*)jl_get_global(jl_core_module, jl_symbol("Compiler")); + if (CC != NULL && jl_is_module(CC)) { + fargs[0] = jl_get_global(CC, jl_symbol("NativeInterpreter"));; + fargs[1] = jl_box_ulong(world); + fargs[1] = jl_apply(fargs, 2); + fargs[0] = jl_get_global(CC, jl_symbol("typeinf_code")); + fargs[2] = (jl_value_t*)mi; + fargs[3] = jl_true; + ci = (jl_code_info_t*)jl_apply(fargs, 4); + } + ct->world_age = last_age; + ct->ptls->in_pure_callback = last_pure; +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; + if (ci && !jl_is_code_info(ci)) { + ci = NULL; + } + JL_GC_POP(); + return ci; +} + JL_DLLEXPORT jl_value_t *jl_call_in_typeinf_world(jl_value_t **args, int nargs) { jl_task_t *ct = jl_current_task; diff --git a/src/julia_internal.h b/src/julia_internal.h index fdd085863d80e..2f7a971045251 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -654,6 +654,7 @@ typedef union { #define SOURCE_MODE_FORCE_SOURCE_UNCACHED 0x3 JL_DLLEXPORT jl_code_instance_t *jl_type_infer(jl_method_instance_t *li, size_t world, int force, uint8_t source_mode); +JL_DLLEXPORT jl_code_info_t *jl_gdbcodetyped1(jl_method_instance_t *mi, size_t world); JL_DLLEXPORT jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *meth JL_PROPAGATES_ROOT, size_t world); JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, From a4cc6c81bac2b2fee3c7b8d6cd97bf093e5ab8b2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 3 Jun 2024 00:28:10 +0000 Subject: [PATCH 3/3] delete unused code for SOURCE_MODE_FORCE_SOURCE_UNCACHED --- base/compiler/typeinfer.jl | 17 +++-------------- src/julia_internal.h | 1 - 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index d1a1fa5ff8e3f..7233cf62f6b93 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -1065,15 +1065,6 @@ N.B.: The same caching considerations as SOURCE_MODE_ABI apply. """ const SOURCE_MODE_FORCE_SOURCE = 0x2 -""" - SOURCE_MODE_FORCE_SOURCE_UNCACHED - -Like `SOURCE_MODE_FORCE_SOURCE`, but ensures that the resulting code instance is -not part of the cache hierarchy, so the `->inferred` field may be safely used -without the possibility of deletion by the compiler. -""" -const SOURCE_MODE_FORCE_SOURCE_UNCACHED = 0x3 - function ci_has_source(code::CodeInstance) inf = @atomic :monotonic code.inferred return isa(inf, CodeInfo) || isa(inf, String) @@ -1095,7 +1086,6 @@ function ci_meets_requirement(code::CodeInstance, source_mode::UInt8, ci_is_cach source_mode == SOURCE_MODE_NOT_REQUIRED && return true source_mode == SOURCE_MODE_ABI && return ci_has_abi(code) source_mode == SOURCE_MODE_FORCE_SOURCE && return ci_has_source(code) - source_mode == SOURCE_MODE_FORCE_SOURCE_UNCACHED && return (!ci_is_cached && ci_has_source(code)) return false end @@ -1108,7 +1098,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod code = get(code_cache(interp), mi, nothing) if code isa CodeInstance # see if this code already exists in the cache - if source_mode in (SOURCE_MODE_FORCE_SOURCE, SOURCE_MODE_FORCE_SOURCE_UNCACHED) && use_const_api(code) + if source_mode == SOURCE_MODE_FORCE_SOURCE && use_const_api(code) code = codeinstance_for_const_with_code(interp, code) ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) return code @@ -1130,7 +1120,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod end lock_mi_inference(interp, mi) result = InferenceResult(mi, typeinf_lattice(interp)) - frame = InferenceState(result, #=cache_mode=#source_mode == SOURCE_MODE_FORCE_SOURCE_UNCACHED ? :volatile : :global, interp) + frame = InferenceState(result, #=cache_mode=#:global, interp) frame === nothing && return nothing typeinf(interp, frame) ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time) @@ -1149,14 +1139,13 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod # store the source in the cache, but the caller wanted it anyway (e.g. for reflection). # We construct a new CodeInstance for it that is not part of the cache hierarchy. can_discard_trees = source_mode ≠ SOURCE_MODE_FORCE_SOURCE && - source_mode ≠ SOURCE_MODE_FORCE_SOURCE_UNCACHED && is_result_constabi_eligible(result) code = CodeInstance(interp, result; can_discard_trees) # If the caller cares about the code and this is constabi, still use our synthesis function # anyway, because we will have not finished inferring the code inside the CodeInstance once # we realized it was constabi, but we want reflection to pretend that we did. - if use_const_api(code) && source_mode in (SOURCE_MODE_FORCE_SOURCE, SOURCE_MODE_FORCE_SOURCE_UNCACHED) + if use_const_api(code) && source_mode == SOURCE_MODE_FORCE_SOURCE return codeinstance_for_const_with_code(interp, code) end @assert ci_meets_requirement(code, source_mode, false) diff --git a/src/julia_internal.h b/src/julia_internal.h index 2f7a971045251..6f71b6018606f 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -651,7 +651,6 @@ typedef union { #define SOURCE_MODE_NOT_REQUIRED 0x0 #define SOURCE_MODE_ABI 0x1 #define SOURCE_MODE_FORCE_SOURCE 0x2 -#define SOURCE_MODE_FORCE_SOURCE_UNCACHED 0x3 JL_DLLEXPORT jl_code_instance_t *jl_type_infer(jl_method_instance_t *li, size_t world, int force, uint8_t source_mode); JL_DLLEXPORT jl_code_info_t *jl_gdbcodetyped1(jl_method_instance_t *mi, size_t world);