Skip to content

Commit

Permalink
Switch LLVM reflection utilities to be CodeInstance based
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Keno committed Dec 20, 2024
1 parent 9bc27ad commit 4d0eed3
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 108 deletions.
24 changes: 11 additions & 13 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -403,31 +403,28 @@ function code_typed_by_type(@nospecialize(tt::Type);
return asts
end

function get_oc_code_rt(passed_interp, oc::Core.OpaqueClosure, types, optimize::Bool)
function get_oc_ci(passed_interp, oc::Core.OpaqueClosure, types, optimize::Bool)
@nospecialize oc types
ccall(:jl_is_in_pure_context, Bool, ()) &&
error("code reflection cannot be used from generated functions")
m = oc.source
if isa(m, Method)
if isdefined(m, :source)
tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...}
mi = specialize_method(m, tt, Core.svec())
if optimize
tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...}
mi = specialize_method(m, tt, Core.svec())
interp = invoke_interp_compiler(passed_interp, :_default_interp, m.primary_world)
code = invoke_interp_compiler(passed_interp, :typeinf_code, interp, mi, optimize)
if code isa CodeInfo
return Pair{CodeInfo, Any}(code, code.rettype)
end
error("inference not successful")
return invoke_interp_compiler(passed_interp, :typeinf_ext, interp, mi, Base.Compiler.SOURCE_MODE_FORCE_SOURCE)
else
code = _uncompressed_ir(m)
return Pair{CodeInfo, Any}(code, typeof(oc).parameters[2])
return CodeInstance(mi, nothing, Any, Any, nothing, code,
UInt32(0), typmin(UInt), typemax(UInt), UInt32(0), nothing,
UInt8(0), nothing, Core.svec())
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)
return codeinst
end
else
error("encountered invalid Core.OpaqueClosure object")
Expand All @@ -440,9 +437,10 @@ function code_typed_opaque_closure(oc::Core.OpaqueClosure, types;
interp=nothing,
_...)
@nospecialize oc types
(code, rt) = get_oc_code_rt(interp, oc, types, optimize)
ci = get_oc_ci(interp, oc, types, optimize)
code = ci.inferred::CodeInfo
debuginfo === :none && remove_linenums!(code)
return Any[Pair{CodeInfo,Any}(code, rt)]
return Any[Pair{CodeInfo,Any}(code, ci.rettype)]
end

"""
Expand Down
153 changes: 75 additions & 78 deletions src/aotcompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2190,23 +2190,22 @@ void jl_dump_native_impl(void *native_code,
}
}


// 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)
extern "C" JL_DLLEXPORT_CODEGEN jl_code_instance_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 = jl_gdbcodetyped1(mi, world);
JL_GC_PUSH1(&src);
jl_code_instance_t *ci = jl_type_infer(mi, world, SOURCE_MODE_FORCE_SOURCE);
JL_GC_PUSH1(&ci);

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 ----\n");
jl_get_llvmf_defn(&llvmf_dump, mi, src, 0, false, jl_default_cgparams);
jl_get_llvmf_defn(&llvmf_dump, ci, NULL, 0, false, jl_default_cgparams);
if (llvmf_dump.F) {
jl_value_t *ir = jl_dump_function_ir(&llvmf_dump, 0, 1, "source");
if (ir != NULL && jl_is_string(ir))
Expand All @@ -2215,7 +2214,7 @@ extern "C" JL_DLLEXPORT_CODEGEN jl_code_info_t *jl_gdbdumpcode(jl_method_instanc
jl_printf(stream, "\n----\n");

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

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

return src;
return ci;
}

// --- 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 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, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params)
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)
{
// emit this function into a new llvm module
dump->F = nullptr;
dump->TSM = nullptr;
if (src && jl_is_code_info(src)) {
auto ctx = jl_ExecutionEngine->makeContext();
orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(mi), ctx);
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();
auto target_info = m.withModuleDo([&](Module &M) {
return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple()));
});
jl_codegen_params_t output(ctx, std::move(target_info.first), std::move(target_info.second));
output.params = &params;
output.imaging_mode = imaging_default();
// This would be nice, but currently it causes some assembly regressions that make printed output
// differ very significantly from the actual non-imaging mode code.
// // Force imaging mode for names of pointers
// output.imaging = true;
// 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.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
JL_GC_PUSH1(&output.temporary_roots);
auto decls = jl_emit_code(m, mi, src, NULL, output);
output.temporary_roots = nullptr;
JL_GC_POP(); // GC the global_targets array contents now since reflection doesn't need it

Function *F = NULL;
if (m) {
// if compilation succeeded, prepare to return the result
// Similar to jl_link_global from jitlayers.cpp,
// so that code_llvm shows similar codegen to the jit
for (auto &global : output.global_targets) {
if (jl_options.image_codegen) {
global.second->setLinkage(GlobalValue::ExternalLinkage);
}
else {
auto p = literal_static_pointer_val(global.first, global.second->getValueType());
Type *elty = PointerType::get(output.getContext(), 0);
// For pretty printing, when LLVM inlines the global initializer into its loads
auto alias = GlobalAlias::create(elty, 0, GlobalValue::PrivateLinkage, global.second->getName() + ".jit", p, global.second->getParent());
global.second->setInitializer(ConstantExpr::getBitCast(alias, global.second->getValueType()));
global.second->setConstant(true);
global.second->setLinkage(GlobalValue::PrivateLinkage);
global.second->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
global.second->setVisibility(GlobalValue::DefaultVisibility);
}
}
if (!jl_options.image_codegen) {
optimizeDLSyms(*m.getModuleUnlocked());
auto ctx = jl_ExecutionEngine->makeContext();
orc::ThreadSafeModule m = jl_create_ts_module(name_from_method_instance(jl_get_ci_mi(ci)), ctx);
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();
auto target_info = m.withModuleDo([&](Module &M) {
return std::make_pair(M.getDataLayout(), Triple(M.getTargetTriple()));
});
jl_codegen_params_t output(ctx, std::move(target_info.first), std::move(target_info.second));
output.params = &params;
output.imaging_mode = imaging_default();
// This would be nice, but currently it causes some assembly regressions that make printed output
// differ very significantly from the actual non-imaging mode code.
// // Force imaging mode for names of pointers
// output.imaging = true;
// 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.temporary_roots = jl_alloc_array_1d(jl_array_any_type, 0);
JL_GC_PUSH1(&output.temporary_roots);
auto decls = jl_emit_codeinst(m, ci, src, output);
output.temporary_roots = nullptr;
JL_GC_POP(); // GC the global_targets array contents now since reflection doesn't need it

Function *F = NULL;
if (m) {
// if compilation succeeded, prepare to return the result
// Similar to jl_link_global from jitlayers.cpp,
// so that code_llvm shows similar codegen to the jit
for (auto &global : output.global_targets) {
if (jl_options.image_codegen) {
global.second->setLinkage(GlobalValue::ExternalLinkage);
}
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
if (optimize) {
NewPM PM{jl_ExecutionEngine->cloneTargetMachine(), getOptLevel(jl_options.opt_level)};
//Safe b/c context lock is held by output
PM.run(*m.getModuleUnlocked());
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
else {
auto p = literal_static_pointer_val(global.first, global.second->getValueType());
Type *elty = PointerType::get(output.getContext(), 0);
// For pretty printing, when LLVM inlines the global initializer into its loads
auto alias = GlobalAlias::create(elty, 0, GlobalValue::PrivateLinkage, global.second->getName() + ".jit", p, global.second->getParent());
global.second->setInitializer(ConstantExpr::getBitCast(alias, global.second->getValueType()));
global.second->setConstant(true);
global.second->setLinkage(GlobalValue::PrivateLinkage);
global.second->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
global.second->setVisibility(GlobalValue::DefaultVisibility);
}
const std::string *fname;
if (decls.functionObject == "jl_fptr_args" || decls.functionObject == "jl_fptr_sparam")
getwrapper = false;
if (!getwrapper)
fname = &decls.specFunctionObject;
else
fname = &decls.functionObject;
F = cast<Function>(m.getModuleUnlocked()->getNamedValue(*fname));
}
if (measure_compile_time_enabled) {
auto end = jl_hrtime();
jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time);
if (!jl_options.image_codegen) {
optimizeDLSyms(*m.getModuleUnlocked());
}
if (F) {
dump->TSM = wrap(new orc::ThreadSafeModule(std::move(m)));
dump->F = wrap(F);
return;
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
if (optimize) {
NewPM PM{jl_ExecutionEngine->cloneTargetMachine(), getOptLevel(jl_options.opt_level)};
//Safe b/c context lock is held by output
PM.run(*m.getModuleUnlocked());
assert(!verifyLLVMIR(*m.getModuleUnlocked()));
}
const std::string *fname;
if (decls.functionObject == "jl_fptr_args" || decls.functionObject == "jl_fptr_sparam")
getwrapper = false;
if (!getwrapper)
fname = &decls.specFunctionObject;
else
fname = &decls.functionObject;
F = cast<Function>(m.getModuleUnlocked()->getNamedValue(*fname));
}
if (measure_compile_time_enabled) {
auto end = jl_hrtime();
jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, end - compiler_start_time);
}
if (F) {
dump->TSM = wrap(new orc::ThreadSafeModule(std::move(m)));
dump->F = wrap(F);
return;
}
}
2 changes: 1 addition & 1 deletion src/codegen-stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,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, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params) UNAVAILABLE
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

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
Expand Down
2 changes: 1 addition & 1 deletion src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1952,7 +1952,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, jl_code_info_t *src, char getwrapper, char optimize, const jl_cgparams_t params);
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);
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);
Expand Down
Loading

0 comments on commit 4d0eed3

Please sign in to comment.