Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch LLVM reflection utilities to be CodeInstance based #56876

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading