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

use interpreter when possible in --compile=no mode #24473

Merged
merged 4 commits into from
Nov 9, 2017
Merged
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
4 changes: 2 additions & 2 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ function _depwarn(msg, opts, bt, caller)
end
end

firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol) = firstcaller(bt, (funcsym,))
function firstcaller(bt::Array{Ptr{Void},1}, funcsyms)
firstcaller(bt::Vector, funcsym::Symbol) = firstcaller(bt, (funcsym,))
function firstcaller(bt::Vector, funcsyms)
# Identify the calling line
found = false
lkup = StackTraces.UNKNOWN
Expand Down
42 changes: 21 additions & 21 deletions base/error.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,22 @@ the current exception (if called within a `catch` block).
rethrow() = ccall(:jl_rethrow, Bottom, ())
rethrow(e) = ccall(:jl_rethrow_other, Bottom, (Any,), e)

"""
backtrace()

Get a backtrace object for the current program point.
"""
backtrace() = ccall(:jl_backtrace_from_here, Array{Ptr{Void},1}, (Int32,), false)

struct InterpreterIP
code::CodeInfo
code::Union{CodeInfo,Core.MethodInstance,Void}
stmt::Csize_t
end

"""
catch_backtrace()

Get the backtrace of the current exception, for use within `catch` blocks.
"""
function catch_backtrace()
bt = Ref{Any}(nothing)
bt2 = Ref{Any}(nothing)
ccall(:jl_get_backtrace, Void, (Ref{Any}, Ref{Any}), bt, bt2)
# convert dual arrays (ips, interpreter_frames) to a single array of locations
function _reformat_bt(bt, bt2)
ret = Array{Union{InterpreterIP,Ptr{Void}},1}()
i, j = 1, 1
while i <= length(bt[])
ip = bt[][i]::Ptr{Void}
while i <= length(bt)
ip = bt[i]::Ptr{Void}
if ip == Ptr{Void}(-1%UInt)
# The next one is really a CodeInfo
push!(ret, InterpreterIP(
bt2[][j],
bt[][i+2]))
bt2[j],
bt[i+2]))
j += 1
i += 3
else
Expand All @@ -93,6 +79,20 @@ function catch_backtrace()
ret
end

function backtrace end

"""
catch_backtrace()

Get the backtrace of the current exception, for use within `catch` blocks.
"""
function catch_backtrace()
bt = Ref{Any}(nothing)
bt2 = Ref{Any}(nothing)
ccall(:jl_get_backtrace, Void, (Ref{Any}, Ref{Any}), bt, bt2)
return _reformat_bt(bt[], bt2[])
end

## keyword arg lowering generates calls to this ##
function kwerr(kw, args::Vararg{Any,N}) where {N}
@_noinline_meta
Expand Down
3 changes: 2 additions & 1 deletion base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3692,7 +3692,8 @@ function finish(me::InferenceState)
end
end

if !already_inferred
# don't store inferred code if we've decided to interpret this function
if !already_inferred && me.linfo.jlcall_api != 4
const_flags = (me.const_ret) << 1 | me.const_api
if me.const_ret
if isa(me.bestguess, Const)
Expand Down
23 changes: 16 additions & 7 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -635,17 +635,26 @@ end
global LAST_SHOWN_LINE_INFOS = Tuple{String, Int}[]

function show_backtrace(io::IO, t::Vector)
n_frames = 0
frame_counter = 0
resize!(LAST_SHOWN_LINE_INFOS, 0)
process_backtrace((a,b) -> n_frames += 1, t)
n_frames != 0 && print(io, "\nStacktrace:")
process_entry = (last_frame, n) -> begin
filtered = Any[]
process_backtrace((fr, count) -> push!(filtered, (fr, count)), t)
isempty(filtered) && return

if length(filtered) == 1 && StackTraces.is_top_level_frame(filtered[1][1])
f = filtered[1][1]
if f.line == 0 && f.file == Symbol("")
# don't show a single top-level frame with no location info
return
end
end

print(io, "\nStacktrace:")
frame_counter = 0
for (last_frame, n) in filtered
frame_counter += 1
show_trace_entry(IOContext(io, :backtrace => true), last_frame, n, prefix = string(" [", frame_counter, "] "))
push!(LAST_SHOWN_LINE_INFOS, (string(last_frame.file), last_frame.line))
end
process_backtrace(process_entry, t)
end

function show_backtrace(io::IO, t::Vector{Any})
Expand All @@ -671,7 +680,7 @@ function process_backtrace(process_func::Function, t::Vector, limit::Int=typemax
count += 1
if count > limit; break; end

if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func
if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func || lkup.linfo !== lkup.linfo
if n > 0
process_func(last_frame, n)
end
Expand Down
59 changes: 53 additions & 6 deletions base/stacktraces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,30 @@ end

lookup(pointer::UInt) = lookup(convert(Ptr{Void}, pointer))

const top_level_scope_sym = Symbol("top-level scope")

using Base.Meta
is_loc_meta(expr, kind) = isexpr(expr, :meta) && length(expr.args) >= 1 && expr.args[1] === kind
function lookup(ip::Base.InterpreterIP)
i = ip.stmt
foundline = false
func = empty_sym
file = empty_sym
line = 0
if ip.code isa Core.MethodInstance
codeinfo = ip.code.inferred
func = ip.code.def.name
file = ip.code.def.file
line = ip.code.def.line
elseif ip.code === nothing
# interpreted top-level expression with no CodeInfo
return [StackFrame(top_level_scope_sym, empty_sym, 0, nothing, false, false, 0)]
else
assert(ip.code isa CodeInfo)
codeinfo = ip.code
func = top_level_scope_sym
file = empty_sym
line = 0
end
while i >= 1
expr = ip.code.code[i]
expr = codeinfo.code[i]
if isa(expr, LineNumberNode)
if line == 0
line = expr.line
Expand All @@ -174,7 +188,7 @@ function lookup(ip::Base.InterpreterIP)
npops = 1
while npops >= 1
i -= 1
expr = ip.code.code[i]
expr = codeinfo.code[i]
is_loc_meta(expr, :pop_loc) && (npops += 1)
is_loc_meta(expr, :push_loc) && (npops -= 1)
end
Expand All @@ -188,6 +202,35 @@ end
lookup(s::StackFrame) = StackFrame[s]
lookup(s::Tuple{StackFrame,Int}) = StackFrame[s[1]]

"""
backtrace()

Get a backtrace object for the current program point.
"""
function Base.backtrace()
bt, bt2 = ccall(:jl_backtrace_from_here, Any, (Int32,), false)
if length(bt) > 2
# remove frames for jl_backtrace_from_here and backtrace()
if bt[2] == Ptr{Void}(-1%UInt)
# backtrace() is interpreted
# Note: win32 is missing the top frame (see https://bugs.chromium.org/p/crashpad/issues/detail?id=53)
@static if Base.Sys.iswindows() && Int === Int32
deleteat!(bt, 1:2)
else
deleteat!(bt, 1:3)
end
unshift!(bt2)
else
@static if Base.Sys.iswindows() && Int === Int32
deleteat!(bt, 1)
else
deleteat!(bt, 1:2)
end
end
end
return Base._reformat_bt(bt, bt2)
end

"""
stacktrace([trace::Vector{Ptr{Void}},] [c_funcs::Bool=false]) -> StackTrace

Expand Down Expand Up @@ -252,10 +295,14 @@ function remove_frames!(stack::StackTrace, m::Module)
return stack
end

is_top_level_frame(f::StackFrame) = f.linfo isa CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym)

function show_spec_linfo(io::IO, frame::StackFrame)
if frame.linfo == nothing
if frame.func === empty_sym
@printf(io, "ip:%#x", frame.pointer)
elseif frame.func === top_level_scope_sym
print(io, "top-level scope")
else
print_with_color(Base.have_color && get(io, :backtrace, false) ? Base.stackframe_function_color() : :nothing, io, string(frame.func))
end
Expand All @@ -266,7 +313,7 @@ function show_spec_linfo(io::IO, frame::StackFrame)
Base.show(io, frame.linfo)
end
elseif frame.linfo isa CodeInfo
print(io, "In toplevel scope")
print(io, "top-level scope")
end
end

Expand Down
2 changes: 2 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ PUBLIC_HEADERS += $(LIBUV_INC)/uv*
endif
PUBLIC_HEADER_TARGETS := $(addprefix $(build_includedir)/julia/,$(notdir $(PUBLIC_HEADERS)))

ifeq ($(JULIACODEGEN),LLVM)
# In LLVM < 3.4, --ldflags includes both options and libraries, so use it both before and after --libs
# In LLVM >= 3.4, --ldflags has only options, and --system-libs has the libraries.
ifneq ($(USE_LLVM_SHLIB),1)
Expand All @@ -95,6 +96,7 @@ else
LLVMLINK += $(shell $(LLVM_CONFIG_HOST) --ldflags) -lLLVM
FLAGS += -DLLVM_SHLIB
endif # USE_LLVM_SHLIB == 1
endif

COMMON_LIBS := -L$(build_shlibdir) -L$(build_libdir) $(LIBUV) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LLVMLINK) $(OSLIBS)
DEBUG_LIBS := $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a $(COMMON_LIBS)
Expand Down
31 changes: 24 additions & 7 deletions src/anticodegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ int globalUnique = 0;

#define UNAVAILABLE { jl_errorf("%s: not available in this build of Julia", __func__); }

void jl_dump_native(const char *bc_fname, const char *obj_fname, const char *sysimg_data, size_t sysimg_len) UNAVAILABLE
void jl_dump_objfile(char *fname, int jit_model, const char *sysimg_data, size_t sysimg_len) UNAVAILABLE
void jl_dump_native(const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *sysimg_data, size_t sysimg_len) UNAVAILABLE
int32_t jl_get_llvm_gv(jl_value_t *p) UNAVAILABLE
void jl_write_malloc_log(void) UNAVAILABLE
void jl_write_coverage_data(void) UNAVAILABLE

JL_DLLEXPORT void jl_clear_malloc_data(void) UNAVAILABLE
JL_DLLEXPORT void jl_extern_c(jl_function_t *f, jl_value_t *rt, jl_value_t *argt, char *name) UNAVAILABLE
JL_DLLEXPORT void *jl_function_ptr(jl_function_t *f, jl_value_t *rt, jl_value_t *argt) UNAVAILABLE
JL_DLLEXPORT const jl_value_t *jl_dump_function_asm(void *f, int raw_mc) UNAVAILABLE
JL_DLLEXPORT const jl_value_t *jl_dump_function_asm(void *f, int raw_mc, const char* asm_variant) UNAVAILABLE
JL_DLLEXPORT const jl_value_t *jl_dump_function_ir(void *f, uint8_t strip_ir_metadata, uint8_t dump_module) UNAVAILABLE

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

int32_t jl_assign_functionID(const char *fname) UNAVAILABLE

void jl_init_codegen(void) { }
void jl_fptr_to_llvm(jl_fptr_t fptr, jl_method_instance_t *lam, int specsig)
{
Expand All @@ -42,13 +43,29 @@ void jl_register_fptrs(uint64_t sysimage_base, const struct _jl_sysimg_fptrs_t *
(void)sysimage_base; (void)fptrs; (void)linfos; (void)n;
}

void jl_compile_linfo(jl_method_instance_t *li) { }
jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t *src, size_t world, const jl_cgparams_t *params)
{
jl_method_instance_t *li = *pli;
jl_llvm_functions_t decls = {};

if (jl_is_method(li->def.method)) {
jl_printf(JL_STDERR, "code missing for ");
jl_static_show(JL_STDERR, (jl_value_t*)li);
jl_printf(JL_STDERR, " : sysimg may not have been built with --compile=all\n");
}
else {
jl_printf(JL_STDERR, "top level expression cannot be compiled in this build of Julia");
}
return decls;
}

jl_value_t *jl_interpret_call(jl_method_instance_t *lam, jl_value_t **args, uint32_t nargs);
void jl_generate_fptr(jl_method_instance_t *li)
jl_generic_fptr_t jl_generate_fptr(jl_method_instance_t *li, const char *F, size_t world)
{
li->fptr = (jl_fptr_t)&jl_interpret_call;
li->jlcall_api = JL_API_INTERPRETED;
jl_generic_fptr_t fptr;
fptr.fptr = (jl_fptr_t)&jl_interpret_call;
fptr.jlcall_api = JL_API_INTERPRETED;
return fptr;
}

JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION(void)
Expand Down
3 changes: 1 addition & 2 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jl_sym_t *enter_sym; jl_sym_t *leave_sym;
jl_sym_t *exc_sym; jl_sym_t *error_sym;
jl_sym_t *new_sym; jl_sym_t *using_sym;
jl_sym_t *const_sym; jl_sym_t *thunk_sym;
jl_sym_t *anonymous_sym; jl_sym_t *underscore_sym;
jl_sym_t *underscore_sym;
jl_sym_t *abstracttype_sym; jl_sym_t *primtype_sym;
jl_sym_t *structtype_sym; jl_sym_t *foreigncall_sym;
jl_sym_t *global_sym; jl_sym_t *list_sym;
Expand Down Expand Up @@ -312,7 +312,6 @@ void jl_init_frontend(void)
const_sym = jl_symbol("const");
global_sym = jl_symbol("global");
thunk_sym = jl_symbol("thunk");
anonymous_sym = jl_symbol("anonymous");
underscore_sym = jl_symbol("_");
amp_sym = jl_symbol("&");
abstracttype_sym = jl_symbol("abstract_type");
Expand Down
31 changes: 11 additions & 20 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,22 +247,6 @@ static DIDerivedType *jl_ppvalue_dillvmt;
static DISubroutineType *jl_di_func_sig;
static DISubroutineType *jl_di_func_null_sig;

extern "C"
int32_t jl_jlcall_api(const char *fname)
{
// give the function an index in the constant lookup table
if (fname == NULL)
return 0;
StringRef Name(fname);
if (Name.startswith("japi3_")) // jlcall abi 3 from JIT
return JL_API_WITH_PARAMETERS;
assert(Name.startswith("japi1_") || // jlcall abi 1 from JIT
Name.startswith("jsys1_") || // jlcall abi 1 from sysimg
Name.startswith("jlcall_") || // jlcall abi 1 from JIT wrapping a specsig method
Name.startswith("jlsysw_")); // jlcall abi 1 from sysimg wrapping a specsig method
return JL_API_GENERIC;
}


// constants
static Constant *V_null;
Expand Down Expand Up @@ -1060,6 +1044,11 @@ static std::unique_ptr<Module> emit_function(
const jl_cgparams_t *params);
void jl_add_linfo_in_flight(StringRef name, jl_method_instance_t *linfo, const DataLayout &DL);

const char *name_from_method_instance(jl_method_instance_t *li)
{
return jl_is_method(li->def.method) ? jl_symbol_name(li->def.method->name) : "top-level scope";
}

// this generates llvm code for the lambda info
// and adds the result to the jitlayers
// (and the shadow module), but doesn't yet compile
Expand Down Expand Up @@ -1170,7 +1159,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t
li->functionObjectsDecls.specFunctionObject = NULL;
nested_compile = last_n_c;
JL_UNLOCK(&codegen_lock); // Might GC
const char *mname = jl_symbol_name(jl_is_method(li->def.method) ? li->def.method->name : anonymous_sym);
const char *mname = name_from_method_instance(li);
jl_rethrow_with_add("error compiling %s", mname);
}
const char *f = decls.functionObject;
Expand Down Expand Up @@ -1512,7 +1501,7 @@ void *jl_get_llvmf_defn(jl_method_instance_t *linfo, size_t world, bool getwrapp
// something failed!
nested_compile = last_n_c;
JL_UNLOCK(&codegen_lock); // Might GC
const char *mname = jl_symbol_name(jl_is_method(linfo->def.method) ? linfo->def.method->name : anonymous_sym);
const char *mname = name_from_method_instance(linfo);
jl_rethrow_with_add("error compiling %s", mname);
}
// Restore the previous compile context
Expand Down Expand Up @@ -1563,7 +1552,9 @@ void *jl_get_llvmf_decl(jl_method_instance_t *linfo, size_t world, bool getwrapp
}

// compile this normally
jl_llvm_functions_t decls = jl_compile_for_dispatch(&linfo, world);
if (linfo->inferred == NULL)
linfo->inferred = jl_nothing;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's generally invalid to change the ->inferred field, only jl_set_method_inferred should be doing that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to handle this in jl_compile_linfo? It's not clear to me why jl_compile_linfo does goto locked_out if li->inferred is NULL.

jl_llvm_functions_t decls = jl_compile_linfo(&linfo, NULL, world, &jl_default_cgparams);

if (decls.functionObject == NULL && linfo->jlcall_api == JL_API_CONST && jl_is_method(linfo->def.method)) {
// normally we don't generate native code for these functions, so need an exception here
Expand Down Expand Up @@ -4862,7 +4853,7 @@ static std::unique_ptr<Module> emit_function(
ctx.linfo = lam;
ctx.source = src;
ctx.world = world;
ctx.name = jl_symbol_name(jl_is_method(lam->def.method) ? lam->def.method->name : anonymous_sym);
ctx.name = name_from_method_instance(lam);
ctx.funcName = ctx.name;
ctx.params = params;
ctx.spvals_ptr = NULL;
Expand Down
Loading