From dc04c1647d6cfff4c848676fc04a3f8fa8608bce Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 14 Nov 2024 10:47:38 +0000 Subject: [PATCH] WIP: Add ABIOverwrite type for owner field Together with #54899, this PR is intending to replicate the functionality of #54373, which allowed particular specializations to have a different ABI signature than what would be suggested by the MethodInstance's `specTypes` field. This PR handles that by adding a special `ABIOverwrite` type, which, when placed in the `owner` field of a `CodeInstance` instructs the system to use the given signature instead. --- base/boot.jl | 6 +++++ src/aotcompile.cpp | 2 +- src/codegen.cpp | 58 ++++++++++++++++++++++++---------------- src/jitlayers.h | 1 + src/jl_exported_data.inc | 1 + src/julia.h | 7 +++++ src/staticdata.c | 3 ++- 7 files changed, 53 insertions(+), 25 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 0df0cde64f8c0..6ebfde3ead19c 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -446,6 +446,12 @@ struct InitError <: WrappedException error end +struct ABIOverwrite + abi::Type + owner + ABIOverwrite(@nospecialize(abi::Type), @nospecialize(owner)) = new(abi, owner) +end + struct PrecompilableError <: Exception end String(s::String) = s # no constructor yet diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index ff14901c2e47f..1267d1fc7af53 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -2158,7 +2158,7 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, jl_ // 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, output); + auto decls = jl_emit_code(m, mi, src, NULL, output); Function *F = NULL; if (m) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 8662016fd069f..a1d79b3e0862e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3046,9 +3046,8 @@ static bool uses_specsig(jl_value_t *sig, bool needsparams, jl_value_t *rettype, return false; // jlcall sig won't require any box allocations } -static std::pair uses_specsig(jl_method_instance_t *lam, jl_value_t *rettype, bool prefer_specsig) +static std::pair uses_specsig(jl_value_t *abi, jl_method_instance_t *lam, jl_value_t *rettype, bool prefer_specsig) { - jl_value_t *sig = lam->specTypes; bool needsparams = false; if (jl_is_method(lam->def.method)) { if ((size_t)jl_subtype_env_size(lam->def.method->sig) != jl_svec_len(lam->sparam_vals)) @@ -3058,7 +3057,7 @@ static std::pair uses_specsig(jl_method_instance_t *lam, jl_value_t needsparams = true; } } - return std::make_pair(uses_specsig(sig, needsparams, rettype, prefer_specsig), needsparams); + return std::make_pair(uses_specsig(abi, needsparams, rettype, prefer_specsig), needsparams); } @@ -4373,6 +4372,7 @@ static jl_llvm_functions_t orc::ThreadSafeModule &TSM, jl_method_instance_t *lam, jl_code_info_t *src, + jl_value_t *abi, jl_value_t *rettype, jl_codegen_params_t ¶ms); @@ -5490,6 +5490,14 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt) return emit_invoke(ctx, lival, argv, nargs, rt); } +static jl_value_t *get_ci_abi(jl_code_instance_t *ci) +{ + if (ci->owner && jl_typeof(ci->owner) == (jl_value_t*)jl_abioverwrite_type) + return ((jl_abi_overwrite_t*)ci->owner)->abi; + return ci->def->specTypes; +} + + static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayRef argv, size_t nargs, jl_value_t *rt) { ++EmittedInvokes; @@ -5525,7 +5533,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR } else if (invoke != jl_fptr_sparam_addr) { bool specsig, needsparams; - std::tie(specsig, needsparams) = uses_specsig(mi, codeinst->rettype, ctx.params->prefer_specsig); + std::tie(specsig, needsparams) = uses_specsig(get_ci_abi(codeinst), mi, codeinst->rettype, ctx.params->prefer_specsig); std::string name; StringRef protoname; bool need_to_emit = true; @@ -8327,13 +8335,13 @@ get_specsig_di(jl_codectx_t &ctx, jl_debugcache_t &debuginfo, jl_value_t *rt, jl } /* aka Core.Compiler.tuple_tfunc */ -static jl_datatype_t *compute_va_type(jl_method_instance_t *lam, size_t nreq) +static jl_datatype_t *compute_va_type(jl_value_t *sig, size_t nreq) { - size_t nvargs = jl_nparams(lam->specTypes)-nreq; + size_t nvargs = jl_nparams(sig)-nreq; jl_svec_t *tupargs = jl_alloc_svec(nvargs); JL_GC_PUSH1(&tupargs); - for (size_t i = nreq; i < jl_nparams(lam->specTypes); ++i) { - jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + for (size_t i = nreq; i < jl_nparams(sig); ++i) { + jl_value_t *argType = jl_nth_slot_type(sig, i); // n.b. specTypes is required to be a datatype by construction for specsig if (is_uniquerep_Type(argType)) argType = jl_typeof(jl_tparam0(argType)); @@ -8373,6 +8381,7 @@ static jl_llvm_functions_t orc::ThreadSafeModule &TSM, jl_method_instance_t *lam, jl_code_info_t *src, + jl_value_t *abi, jl_value_t *jlrettype, jl_codegen_params_t ¶ms) { @@ -8453,7 +8462,7 @@ static jl_llvm_functions_t int n_ssavalues = jl_is_long(src->ssavaluetypes) ? jl_unbox_long(src->ssavaluetypes) : jl_array_nrows(src->ssavaluetypes); size_t vinfoslen = jl_array_dim0(src->slotflags); ctx.slots.resize(vinfoslen, jl_varinfo_t(ctx.builder.getContext())); - assert(lam->specTypes); // the specTypes field should always be assigned + assert(abi); // the specTypes field should always be assigned // create SAvalue locations for SSAValue objects @@ -8462,7 +8471,7 @@ static jl_llvm_functions_t ctx.ssavalue_usecount.assign(n_ssavalues, 0); bool specsig, needsparams; - std::tie(specsig, needsparams) = uses_specsig(lam, jlrettype, params.params->prefer_specsig); + std::tie(specsig, needsparams) = uses_specsig(abi, lam, jlrettype, params.params->prefer_specsig); // step 3. some variable analysis size_t i; @@ -8472,7 +8481,7 @@ static jl_llvm_functions_t jl_sym_t *argname = slot_symbol(ctx, i); if (argname == jl_unused_sym) continue; - jl_value_t *ty = jl_nth_slot_type(lam->specTypes, i); + jl_value_t *ty = jl_nth_slot_type(abi, i); // TODO: jl_nth_slot_type should call jl_rewrap_unionall // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise // OpaqueClosure implicitly loads the env @@ -8490,7 +8499,7 @@ static jl_llvm_functions_t if (va && ctx.vaSlot != -1) { jl_varinfo_t &varinfo = ctx.slots[ctx.vaSlot]; varinfo.isArgument = true; - vatyp = specsig ? compute_va_type(lam, nreq) : (jl_tuple_type); + vatyp = specsig ? compute_va_type(abi, nreq) : (jl_tuple_type); varinfo.value = mark_julia_type(ctx, (Value*)NULL, false, vatyp); } @@ -8542,7 +8551,7 @@ static jl_llvm_functions_t ArgNames[i] = name; } } - returninfo = get_specsig_function(ctx, M, NULL, declarations.specFunctionObject, lam->specTypes, + returninfo = get_specsig_function(ctx, M, NULL, declarations.specFunctionObject, abi, jlrettype, ctx.is_opaque_closure, JL_FEAT_TEST(ctx,gcstack_arg), ArgNames, nreq); f = cast(returninfo.decl.getCallee()); @@ -8576,7 +8585,7 @@ static jl_llvm_functions_t std::string wrapName; raw_string_ostream(wrapName) << "jfptr_" << ctx.name << "_" << jl_atomic_fetch_add_relaxed(&globalUniqueGeneratedNames, 1); declarations.functionObject = wrapName; - size_t nparams = jl_nparams(lam->specTypes); + size_t nparams = jl_nparams(abi); gen_invoke_wrapper(lam, jlrettype, returninfo, nparams, retarg, ctx.is_opaque_closure, declarations.functionObject, M, ctx.emission_context); // TODO: add attributes: maybe_mark_argument_dereferenceable(Arg, argType) // TODO: add attributes: dereferenceable @@ -8596,10 +8605,10 @@ static jl_llvm_functions_t declarations.functionObject = needsparams ? "jl_fptr_sparam" : "jl_fptr_args"; } - if (ctx.emission_context.debug_level >= 2 && lam->def.method && jl_is_method(lam->def.method) && lam->specTypes != (jl_value_t*)jl_emptytuple_type) { + if (ctx.emission_context.debug_level >= 2 && lam->def.method && jl_is_method(lam->def.method) && abi != (jl_value_t*)jl_emptytuple_type) { ios_t sigbuf; ios_mem(&sigbuf, 0); - jl_static_show_func_sig((JL_STREAM*) &sigbuf, (jl_value_t*)lam->specTypes); + jl_static_show_func_sig((JL_STREAM*) &sigbuf, (jl_value_t*)abi); f->addFnAttr("julia.fsig", StringRef(sigbuf.buf, sigbuf.size)); ios_close(&sigbuf); } @@ -8656,7 +8665,7 @@ static jl_llvm_functions_t else if (!specsig) subrty = debugcache.jl_di_func_sig; else - subrty = get_specsig_di(ctx, debugcache, jlrettype, lam->specTypes, dbuilder); + subrty = get_specsig_di(ctx, debugcache, jlrettype, abi, dbuilder); SP = dbuilder.createFunction(nullptr ,dbgFuncName // Name ,f->getName() // LinkageName @@ -8987,7 +8996,7 @@ static jl_llvm_functions_t nullptr, nullptr, /*isboxed*/true, AtomicOrdering::NotAtomic, false, sizeof(void*)); } else { - jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + jl_value_t *argType = jl_nth_slot_type(abi, i); // TODO: jl_nth_slot_type should call jl_rewrap_unionall? // specTypes is required to be a datatype by construction for specsig, but maybe not otherwise bool isboxed = deserves_argbox(argType); @@ -9061,10 +9070,10 @@ static jl_llvm_functions_t assert(vi.boxroot == NULL); } else if (specsig) { - ctx.nvargs = jl_nparams(lam->specTypes) - nreq; + ctx.nvargs = jl_nparams(abi) - nreq; SmallVector vargs(ctx.nvargs); - for (size_t i = nreq; i < jl_nparams(lam->specTypes); ++i) { - jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); + for (size_t i = nreq; i < jl_nparams(abi); ++i) { + jl_value_t *argType = jl_nth_slot_type(abi, i); // n.b. specTypes is required to be a datatype by construction for specsig bool isboxed = deserves_argbox(argType); Type *llvmArgType = isboxed ? ctx.types().T_prjlvalue : julia_type_to_llvm(ctx, argType); @@ -10011,6 +10020,7 @@ jl_llvm_functions_t jl_emit_code( orc::ThreadSafeModule &m, jl_method_instance_t *li, jl_code_info_t *src, + jl_value_t *abi, jl_codegen_params_t ¶ms) { JL_TIMING(CODEGEN, CODEGEN_LLVM); @@ -10019,8 +10029,10 @@ jl_llvm_functions_t jl_emit_code( assert((params.params == &jl_default_cgparams /* fast path */ || !params.cache || compare_cgparams(params.params, &jl_default_cgparams)) && "functions compiled with custom codegen params must not be cached"); + if (!abi) + abi = li->specTypes; JL_TRY { - decls = emit_function(m, li, src, src->rettype, params); + decls = emit_function(m, li, src, abi, src->rettype, params); auto stream = *jl_ExecutionEngine->get_dump_emitted_mi_name_stream(); if (stream) { jl_printf(stream, "%s\t", decls.specFunctionObject.c_str()); @@ -10111,7 +10123,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, params); + jl_llvm_functions_t decls = jl_emit_code(m, codeinst->def, src, get_ci_abi(codeinst), params); const std::string &specf = decls.specFunctionObject; const std::string &f = decls.functionObject; diff --git a/src/jitlayers.h b/src/jitlayers.h index ba4ac3081795e..59c61aff3c9bf 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -277,6 +277,7 @@ jl_llvm_functions_t jl_emit_code( orc::ThreadSafeModule &M, jl_method_instance_t *mi, jl_code_info_t *src, + jl_value_t *abi, jl_codegen_params_t ¶ms); jl_llvm_functions_t jl_emit_codeinst( diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index 8711c14514145..41e5a84773ca4 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -116,6 +116,7 @@ XX(jl_simplevector_type) \ XX(jl_slotnumber_type) \ XX(jl_ssavalue_type) \ + XX(jl_abioverwrite_type) \ XX(jl_stackovf_exception) \ XX(jl_string_type) \ XX(jl_symbol_type) \ diff --git a/src/julia.h b/src/julia.h index 81e6cf42da567..8b3424a133df8 100644 --- a/src/julia.h +++ b/src/julia.h @@ -486,6 +486,12 @@ typedef struct _jl_code_instance_t { } specptr; // private data for `jlcall entry point } jl_code_instance_t; +typedef struct _jl_abi_overwrite_t { + JL_DATA_TYPE + jl_value_t *abi; + jl_value_t *owner; +} jl_abi_overwrite_t; + // all values are callable as Functions typedef jl_value_t jl_function_t; @@ -942,6 +948,7 @@ extern JL_DLLIMPORT jl_datatype_t *jl_fielderror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_atomicerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_missingcodeerror_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_datatype_t *jl_lineinfonode_type JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_datatype_t *jl_abioverwrite_type JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_stackovf_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_memory_exception JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_readonlymemory_exception JL_GLOBALLY_ROOTED; diff --git a/src/staticdata.c b/src/staticdata.c index c2e8d7e3956ea..d41e8b002bd52 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -100,7 +100,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 193 +#define NUM_TAGS 194 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -215,6 +215,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_addrspace_typename); INSERT_TAG(jl_addrspacecore_type); INSERT_TAG(jl_debuginfo_type); + INSERT_TAG(jl_abioverwrite_type); // special typenames INSERT_TAG(jl_tuple_typename);