Skip to content

Commit 4d0eed3

Browse files
committed
Switch LLVM reflection utilities to be CodeInstance based
This is a natural continuation of the stream of work begun in #53219. In particular, with #56555, it is important to be able to pass a CodeInstance, since the ABI may be overwritten and without knowing that, codegen will generated incorrect code for the CodeInstance.
1 parent 9bc27ad commit 4d0eed3

File tree

5 files changed

+113
-108
lines changed

5 files changed

+113
-108
lines changed

base/reflection.jl

+11-13
Original file line numberDiff line numberDiff line change
@@ -403,31 +403,28 @@ function code_typed_by_type(@nospecialize(tt::Type);
403403
return asts
404404
end
405405

406-
function get_oc_code_rt(passed_interp, oc::Core.OpaqueClosure, types, optimize::Bool)
406+
function get_oc_ci(passed_interp, oc::Core.OpaqueClosure, types, optimize::Bool)
407407
@nospecialize oc types
408408
ccall(:jl_is_in_pure_context, Bool, ()) &&
409409
error("code reflection cannot be used from generated functions")
410410
m = oc.source
411411
if isa(m, Method)
412412
if isdefined(m, :source)
413+
tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...}
414+
mi = specialize_method(m, tt, Core.svec())
413415
if optimize
414-
tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...}
415-
mi = specialize_method(m, tt, Core.svec())
416416
interp = invoke_interp_compiler(passed_interp, :_default_interp, m.primary_world)
417-
code = invoke_interp_compiler(passed_interp, :typeinf_code, interp, mi, optimize)
418-
if code isa CodeInfo
419-
return Pair{CodeInfo, Any}(code, code.rettype)
420-
end
421-
error("inference not successful")
417+
return invoke_interp_compiler(passed_interp, :typeinf_ext, interp, mi, Base.Compiler.SOURCE_MODE_FORCE_SOURCE)
422418
else
423419
code = _uncompressed_ir(m)
424-
return Pair{CodeInfo, Any}(code, typeof(oc).parameters[2])
420+
return CodeInstance(mi, nothing, Any, Any, nothing, code,
421+
UInt32(0), typmin(UInt), typemax(UInt), UInt32(0), nothing,
422+
UInt8(0), nothing, Core.svec())
425423
end
426424
else
427425
# OC constructed from optimized IR
428426
codeinst = m.specializations.cache
429-
# XXX: the inferred field is not normally a CodeInfo, but this assumes it is guaranteed to be always
430-
return Pair{CodeInfo, Any}(codeinst.inferred, codeinst.rettype)
427+
return codeinst
431428
end
432429
else
433430
error("encountered invalid Core.OpaqueClosure object")
@@ -440,9 +437,10 @@ function code_typed_opaque_closure(oc::Core.OpaqueClosure, types;
440437
interp=nothing,
441438
_...)
442439
@nospecialize oc types
443-
(code, rt) = get_oc_code_rt(interp, oc, types, optimize)
440+
ci = get_oc_ci(interp, oc, types, optimize)
441+
code = ci.inferred::CodeInfo
444442
debuginfo === :none && remove_linenums!(code)
445-
return Any[Pair{CodeInfo,Any}(code, rt)]
443+
return Any[Pair{CodeInfo,Any}(code, ci.rettype)]
446444
end
447445

448446
"""

src/aotcompile.cpp

+75-78
Original file line numberDiff line numberDiff line change
@@ -2190,23 +2190,22 @@ void jl_dump_native_impl(void *native_code,
21902190
}
21912191
}
21922192

2193-
21942193
// sometimes in GDB you want to find out what code would be created from a mi
2195-
extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instance_t *mi)
2194+
extern "C" JL_DLLEXPORT_CODEGEN jl_code_instance_t *jl_gdbdumpcode(jl_method_instance_t *mi)
21962195
{
21972196
jl_llvmf_dump_t llvmf_dump;
21982197
size_t world = jl_current_task->world_age;
21992198
JL_STREAM *stream = (JL_STREAM*)STDERR_FILENO;
22002199

2201-
jl_code_info_t *src = jl_gdbcodetyped1(mi, world);
2202-
JL_GC_PUSH1(&src);
2200+
jl_code_instance_t *ci = jl_type_infer(mi, world, SOURCE_MODE_FORCE_SOURCE);
2201+
JL_GC_PUSH1(&ci);
22032202

22042203
jl_printf(stream, "---- dumping IR for ----\n");
22052204
jl_static_show(stream, (jl_value_t*)mi);
22062205
jl_printf(stream, "\n----\n");
22072206

22082207
jl_printf(stream, "\n---- unoptimized IR ----\n");
2209-
jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, false, jl_default_cgparams);
2208+
jl_get_llvmf_defn(&llvmf_dump, ci, NULL, 0, false, jl_default_cgparams);
22102209
if (llvmf_dump.F) {
22112210
jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source");
22122211
if (ir != NULL && jl_is_string(ir))
@@ -2215,7 +2214,7 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc
22152214
jl_printf(stream, "\n----\n");
22162215

22172216
jl_printf(stream, "\n---- optimized IR ----\n");
2218-
jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, true, jl_default_cgparams);
2217+
jl_get_llvmf_defn(&llvmf_dump, ci, NULL, 0, true, jl_default_cgparams);
22192218
if (llvmf_dump.F) {
22202219
jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source");
22212220
if (ir != NULL && jl_is_string(ir))
@@ -2224,7 +2223,7 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc
22242223
jl_printf(stream, "\n----\n");
22252224

22262225
jl_printf(stream, "\n---- assembly ----\n");
2227-
jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, true, jl_default_cgparams);
2226+
jl_get_llvmf_defn(&llvmf_dump, ci, NULL, 0, true, jl_default_cgparams);
22282227
if (llvmf_dump.F) {
22292228
jl_value_t *ir = jl_dump_function_asm(&llvmf_dump, 0, "", "source", 0, true);
22302229
if (ir != NULL && jl_is_string(ir))
@@ -2233,92 +2232,90 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc
22332232
jl_printf(stream, "\n----\n");
22342233
JL_GC_POP();
22352234

2236-
return src;
2235+
return ci;
22372236
}
22382237

22392238
// --- native code info, and dump function to IR and ASM ---
22402239
// Get pointer to llvm::Function instance, compiling if necessary
22412240
// for use in reflection from Julia.
22422241
// This is paired with jl_dump_function_ir and jl_dump_function_asm, either of which will free all memory allocated here
22432242
extern "C" JL_DLLEXPORT_CODEGEN
2244-
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)
2243+
void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_code_instance_t *ci, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params)
22452244
{
22462245
// emit this function into a new llvm module
22472246
dump->F = nullptr;
22482247
dump->TSM = nullptr;
2249-
if (src && jl_is_code_info(src)) {
2250-
auto ctx = jl_ExecutionEngine->makeContext();
2251-
orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(mi), ctx);
2252-
uint64_t compiler_start_time = 0;
2253-
uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled);
2254-
if (measure_compile_time_enabled)
2255-
compiler_start_time = jl_hrtime();
2256-
auto target_info = m.withModuleDo([&](Module &M) {
2257-
return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple()));
2258-
});
2259-
jl_codegen_params_t output(ctx, std::move(target_info.first), std::move(target_info.second));
2260-
output.params = &params;
2261-
output.imaging_mode = imaging_default();
2262-
// This would be nice, but currently it causes some assembly regressions that make printed output
2263-
// differ very significantly from the actual non-imaging mode code.
2264-
// // Force imaging mode for names of pointers
2265-
// output.imaging = true;
2266-
// This would also be nice, but it seems to cause OOMs on the windows32 builder
2267-
// To get correct names in the IR this needs to be at least 2
2268-
output.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
2269-
JL_GC_PUSH1(&output.temporary_roots);
2270-
auto decls = jl_emit_code(m, mi, src, NULL, output);
2271-
output.temporary_roots = nullptr;
2272-
JL_GC_POP(); // GC the global_targets array contents now since reflection doesn't need it
2273-
2274-
Function *F = NULL;
2275-
if (m) {
2276-
// if compilation succeeded, prepare to return the result
2277-
// Similar to jl_link_global from jitlayers.cpp,
2278-
// so that code_llvm shows similar codegen to the jit
2279-
for (auto &global : output.global_targets) {
2280-
if (jl_options.image_codegen) {
2281-
global.second->setLinkage(GlobalValue::ExternalLinkage);
2282-
}
2283-
else {
2284-
auto p = literal_static_pointer_val(global.first, global.second->getValueType());
2285-
Type *elty = PointerType::get(output.getContext(), 0);
2286-
// For pretty printing, when LLVM inlines the global initializer into its loads
2287-
auto alias = GlobalAlias::create(elty, 0, GlobalValue::PrivateLinkage, global.second->getName() + ".jit", p, global.second->getParent());
2288-
global.second->setInitializer(ConstantExpr::getBitCast(alias, global.second->getValueType()));
2289-
global.second->setConstant(true);
2290-
global.second->setLinkage(GlobalValue::PrivateLinkage);
2291-
global.second->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
2292-
global.second->setVisibility(GlobalValue::DefaultVisibility);
2293-
}
2294-
}
2295-
if (!jl_options.image_codegen) {
2296-
optimizeDLSyms(*m.getModuleUnlocked());
2248+
auto ctx = jl_ExecutionEngine->makeContext();
2249+
orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(jl_get_ci_mi(ci)), ctx);
2250+
uint64_t compiler_start_time = 0;
2251+
uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled);
2252+
if (measure_compile_time_enabled)
2253+
compiler_start_time = jl_hrtime();
2254+
auto target_info = m.withModuleDo([&](Module &M) {
2255+
return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple()));
2256+
});
2257+
jl_codegen_params_t output(ctx, std::move(target_info.first), std::move(target_info.second));
2258+
output.params = &params;
2259+
output.imaging_mode = imaging_default();
2260+
// This would be nice, but currently it causes some assembly regressions that make printed output
2261+
// differ very significantly from the actual non-imaging mode code.
2262+
// // Force imaging mode for names of pointers
2263+
// output.imaging = true;
2264+
// This would also be nice, but it seems to cause OOMs on the windows32 builder
2265+
// To get correct names in the IR this needs to be at least 2
2266+
output.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
2267+
JL_GC_PUSH1(&output.temporary_roots);
2268+
auto decls = jl_emit_codeinst(m, ci, src, output);
2269+
output.temporary_roots = nullptr;
2270+
JL_GC_POP(); // GC the global_targets array contents now since reflection doesn't need it
2271+
2272+
Function *F = NULL;
2273+
if (m) {
2274+
// if compilation succeeded, prepare to return the result
2275+
// Similar to jl_link_global from jitlayers.cpp,
2276+
// so that code_llvm shows similar codegen to the jit
2277+
for (auto &global : output.global_targets) {
2278+
if (jl_options.image_codegen) {
2279+
global.second->setLinkage(GlobalValue::ExternalLinkage);
22972280
}
2298-
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
2299-
if (optimize) {
2300-
NewPM PM{jl_ExecutionEngine->cloneTargetMachine(), getOptLevel(jl_options.opt_level)};
2301-
//Safe b/c context lock is held by output
2302-
PM.run(*m.getModuleUnlocked());
2303-
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
2281+
else {
2282+
auto p = literal_static_pointer_val(global.first, global.second->getValueType());
2283+
Type *elty = PointerType::get(output.getContext(), 0);
2284+
// For pretty printing, when LLVM inlines the global initializer into its loads
2285+
auto alias = GlobalAlias::create(elty, 0, GlobalValue::PrivateLinkage, global.second->getName() + ".jit", p, global.second->getParent());
2286+
global.second->setInitializer(ConstantExpr::getBitCast(alias, global.second->getValueType()));
2287+
global.second->setConstant(true);
2288+
global.second->setLinkage(GlobalValue::PrivateLinkage);
2289+
global.second->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
2290+
global.second->setVisibility(GlobalValue::DefaultVisibility);
23042291
}
2305-
const std::string *fname;
2306-
if (decls.functionObject == "jl_fptr_args" || decls.functionObject == "jl_fptr_sparam")
2307-
getwrapper = false;
2308-
if (!getwrapper)
2309-
fname = &decls.specFunctionObject;
2310-
else
2311-
fname = &decls.functionObject;
2312-
F = cast<Function>(m.getModuleUnlocked()->getNamedValue(*fname));
23132292
}
2314-
if (measure_compile_time_enabled) {
2315-
auto end = jl_hrtime();
2316-
jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time);
2293+
if (!jl_options.image_codegen) {
2294+
optimizeDLSyms(*m.getModuleUnlocked());
23172295
}
2318-
if (F) {
2319-
dump->TSM = wrap(new orc::ThreadSafeModule(std::move(m)));
2320-
dump->F = wrap(F);
2321-
return;
2296+
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
2297+
if (optimize) {
2298+
NewPM PM{jl_ExecutionEngine->cloneTargetMachine(), getOptLevel(jl_options.opt_level)};
2299+
//Safe b/c context lock is held by output
2300+
PM.run(*m.getModuleUnlocked());
2301+
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
23222302
}
2303+
const std::string *fname;
2304+
if (decls.functionObject == "jl_fptr_args" || decls.functionObject == "jl_fptr_sparam")
2305+
getwrapper = false;
2306+
if (!getwrapper)
2307+
fname = &decls.specFunctionObject;
2308+
else
2309+
fname = &decls.functionObject;
2310+
F = cast<Function>(m.getModuleUnlocked()->getNamedValue(*fname));
2311+
}
2312+
if (measure_compile_time_enabled) {
2313+
auto end = jl_hrtime();
2314+
jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time);
2315+
}
2316+
if (F) {
2317+
dump->TSM = wrap(new orc::ThreadSafeModule(std::move(m)));
2318+
dump->F = wrap(F);
2319+
return;
23232320
}
23242321
}

src/codegen-stubs.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ JL_DLLEXPORT void jl_extern_c_fallback(jl_function_t *f, jl_value_t *rt, jl_valu
2121
JL_DLLEXPORT jl_value_t *jl_dump_method_asm_fallback(jl_method_instance_t *linfo, size_t world,
2222
char emit_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary) UNAVAILABLE
2323
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
24-
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
24+
JL_DLLEXPORT void jl_get_llvmf_defn_fallback(jl_llvmf_dump_t *dump, jl_code_instance_t *ci, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params) UNAVAILABLE
2525

2626
JL_DLLEXPORT void *jl_LLVMCreateDisasm_fallback(const char *TripleName, void *DisInfo, int TagType, void *GetOpInfo, void *SymbolLookUp) UNAVAILABLE
2727
JL_DLLEXPORT size_t jl_LLVMDisasmInstruction_fallback(void *DC, uint8_t *Bytes, uint64_t BytesSize, uint64_t PC, char *OutString, size_t OutStringSize) UNAVAILABLE

src/julia_internal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1952,7 +1952,7 @@ typedef struct {
19521952

19531953
JL_DLLIMPORT jl_value_t *jl_dump_method_asm(jl_method_instance_t *linfo, size_t world,
19541954
char emit_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary);
1955-
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);
1955+
JL_DLLIMPORT void jl_get_llvmf_defn(jl_llvmf_dump_t* dump, jl_code_instance_t *ci, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params);
19561956
JL_DLLIMPORT jl_value_t *jl_dump_fptr_asm(uint64_t fptr, char emit_mc, const char* asm_variant, const char *debuginfo, char binary);
19571957
JL_DLLIMPORT jl_value_t *jl_dump_function_ir(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo);
19581958
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);

0 commit comments

Comments
 (0)