Skip to content

Commit

Permalink
Merge pull request #29721 from JuliaLang/jn/interpret-intrinsics
Browse files Browse the repository at this point in the history
fixes for the runtime (interpreted intrinsics)
  • Loading branch information
vtjnash authored Nov 5, 2018
2 parents f23acbd + 386a8c2 commit 1f2b16f
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 68 deletions.
39 changes: 25 additions & 14 deletions base/compiler/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ function basic_blocks_starts(stmts::Vector{Any})
push!(jump_dests, idx)
push!(jump_dests, idx+1)
# The catch block is a jump dest
push!(jump_dests, stmt.args[1])
push!(jump_dests, stmt.args[1]::Int)
elseif stmt.head === :gotoifnot
# also tolerate expr form of IR
push!(jump_dests, idx+1)
push!(jump_dests, stmt.args[2])
push!(jump_dests, stmt.args[2]::Int)
elseif stmt.head === :return
# also tolerate expr form of IR
# This is a fake dest to force the next stmt to start a bb
Expand Down Expand Up @@ -130,7 +130,7 @@ function compute_basic_blocks(stmts::Vector{Any})
# Compute successors/predecessors
for (num, b) in enumerate(blocks)
terminator = stmts[last(b.stmts)]
if isa(terminator, ReturnNode)
if isa(terminator, ReturnNode) || isexpr(terminator, :return)
# return never has any successors
continue
end
Expand All @@ -150,17 +150,28 @@ function compute_basic_blocks(stmts::Vector{Any})
push!(blocks[block′].preds, num)
push!(b.succs, block′)
end
elseif isa(terminator, Expr) && terminator.head == :enter
# :enter gets a virtual edge to the exception handler and
# the exception handler gets a virtual edge from outside
# the function.
# See the devdocs on exception handling in SSA form (or
# bug Keno to write them, if you're reading this and they
# don't exist)
block′ = block_for_inst(basic_block_index, terminator.args[1])
push!(blocks[block′].preds, num)
push!(blocks[block′].preds, 0)
push!(b.succs, block′)
elseif isa(terminator, Expr)
if terminator.head == :enter
# :enter gets a virtual edge to the exception handler and
# the exception handler gets a virtual edge from outside
# the function.
# See the devdocs on exception handling in SSA form (or
# bug Keno to write them, if you're reading this and they
# don't exist)
block′ = block_for_inst(basic_block_index, terminator.args[1]::Int)
push!(blocks[block′].preds, num)
push!(blocks[block′].preds, 0)
push!(b.succs, block′)
elseif terminator.head == :gotoifnot
block′ = block_for_inst(basic_block_index, terminator.args[2]::Int)
if block′ == num + 1
# This GotoIfNot acts like a noop - treat it as such.
# We will drop it during SSA renaming
else
push!(blocks[block′].preds, num)
push!(b.succs, block′)
end
end
end
# statement fall-through
if num + 1 <= length(blocks)
Expand Down
29 changes: 17 additions & 12 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ end

# compute (and cache) an inferred AST and return type
function typeinf_ext(linfo::MethodInstance, params::Params)
method = linfo.def::Method
for i = 1:2 # test-and-lock-and-test
i == 2 && ccall(:jl_typeinf_begin, Cvoid, ())
if isdefined(linfo, :inferred)
Expand All @@ -538,7 +539,6 @@ function typeinf_ext(linfo::MethodInstance, params::Params)
if min_world(linfo) <= params.world <= max_world(linfo)
inf = linfo.inferred
if invoke_api(linfo) == 2
method = linfo.def::Method
tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ())
tree.code = Any[ Expr(:return, quoted(linfo.inferred_const)) ]
tree.method_for_inference_limit_heuristics = nothing
Expand Down Expand Up @@ -610,17 +610,22 @@ end
# method lambda - infer this specialization via the method cache
return typeinf_ext(linfo, Params(world))
else
# toplevel lambda - infer directly
ccall(:jl_typeinf_begin, Cvoid, ())
result = InferenceResult(linfo)
frame = InferenceState(result, linfo.inferred::CodeInfo,
#=cached=#true, Params(world))
typeinf(frame)
ccall(:jl_typeinf_end, Cvoid, ())
@assert frame.inferred # TODO: deal with this better
@assert frame.linfo === linfo
linfo.rettype = widenconst(frame.bestguess)
return svec(linfo, frame.src)
src = linfo.inferred::CodeInfo
if !src.inferred
# toplevel lambda - infer directly
ccall(:jl_typeinf_begin, Cvoid, ())
if !src.inferred
result = InferenceResult(linfo)
frame = InferenceState(result, src, #=cached=#true, Params(world))
typeinf(frame)
@assert frame.inferred # TODO: deal with this better
@assert frame.linfo === linfo
linfo.rettype = widenconst(frame.bestguess)
src = frame.src
end
ccall(:jl_typeinf_end, Cvoid, ())
end
return svec(linfo, src)
end
end

Expand Down
2 changes: 1 addition & 1 deletion base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1319,7 +1319,7 @@ julia> a = 1//2
1//2
julia> setfield!(a, :num, 3);
ERROR: type Rational is immutable
ERROR: setfield! immutable struct of type Rational cannot be changed
```
"""
setfield!
Expand Down
4 changes: 2 additions & 2 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -677,10 +677,10 @@ JL_CALLABLE(jl_f_setfield)
jl_type_error("setfield!", (jl_value_t*)jl_datatype_type, v);
jl_datatype_t *st = (jl_datatype_t*)vt;
if (!st->mutabl)
jl_errorf("type %s is immutable", jl_symbol_name(st->name->name));
jl_errorf("setfield! immutable struct of type %s cannot be changed", jl_symbol_name(st->name->name));
size_t idx;
if (jl_is_long(args[1])) {
idx = jl_unbox_long(args[1])-1;
idx = jl_unbox_long(args[1]) - 1;
if (idx >= jl_datatype_nfields(st))
jl_bounds_error(args[0], args[1]);
}
Expand Down
6 changes: 4 additions & 2 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2421,8 +2421,10 @@ static void emit_setfield(jl_codectx_t &ctx,
}
}
else {
// TODO: better error
emit_error(ctx, "type is immutable");
std::string msg = "setfield! immutable struct of type "
+ std::string(jl_symbol_name(sty->name->name))
+ " cannot be changed";
emit_error(ctx, msg);
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,10 +568,12 @@ SECT_INTERP static size_t eval_phi(jl_array_t *stmts, interpreter_state *s, size
nphi -= n_oldphi;
}
if (edge != -1) {
// if edges list doesn't contain last branch, this value should be unused.
// if edges list doesn't contain last branch, or the value is explicitly undefined
// then this value should be unused.
jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(e, 1);
val = jl_array_ptr_ref(values, edge);
val = eval_value(val, s);
if (val)
val = eval_value(val, s);
}
phis[i] = val;
}
Expand Down
57 changes: 37 additions & 20 deletions src/runtime_intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,8 @@ static int jl_##name##nbits(unsigned runtime_nbits, void *pa, void *pb, void *pr
{ \
c_type a = *(c_type*)pa; \
c_type b = *(c_type*)pb; \
if (CHECK_OP(a, b)) \
return 1; \
*(c_type*)pr = (c_type)OP(a, b); \
return 0; \
return CHECK_OP(a, b); \
}

// float inputs
Expand Down Expand Up @@ -808,7 +806,7 @@ bi_iintrinsic_fast(LLVMXor, xor_op, xor_int, u)
bi_iintrinsic_cnvtb_fast(LLVMShl, shl_op, shl_int, u, 1)
#define lshr_op(a,b) (b >= 8 * sizeof(a)) ? 0 : a >> b
bi_iintrinsic_cnvtb_fast(LLVMLShr, lshr_op, lshr_int, u, 1)
#define ashr_op(a,b) ((b < 0 || b >= 8 * sizeof(a)) ? a >> (8*sizeof(a) - 1) : a >> b)
#define ashr_op(a,b) ((b < 0 || b >= 8 * sizeof(a)) ? a >> (8 * sizeof(a) - 1) : a >> b)
bi_iintrinsic_cnvtb_fast(LLVMAShr, ashr_op, ashr_int, , 1)
//#define bswap_op(a) __builtin_bswap(a)
//un_iintrinsic_fast(LLVMByteSwap, bswap_op, bswap_int, u)
Expand Down Expand Up @@ -856,20 +854,41 @@ un_fintrinsic_withtype(fptrunc,fptrunc)
un_fintrinsic_withtype(fpext,fpext)

// checked arithmetic
/**
* s_typemin = ((typeof a)~0 << (runtime_nbits - 1))
* s_typemax = ~((typeof a)1 << (runtime_nbits - 1))
* u_typemin = 0
* u_typemax = ((typeof a)1 << runtime_nbits) - 1
* where (a - a) == (typeof(a)0
**/
#define sTYPEMIN(a) \
(8 * sizeof(a) == runtime_nbits \
? ((a - a + ~0) << (8 * sizeof(a) - 1)) \
: ((a - a + ~0) << (runtime_nbits - 1)))
#define sTYPEMAX(a) \
(8 * sizeof(a) == runtime_nbits \
? ~((a - a + ~0) << (8 * sizeof(a) - 1)) \
: ~((a - a + ~0) << (runtime_nbits - 1)))
#define uTYPEMIN(a) (0)
#define uTYPEMAX(a) \
(8 * sizeof(~a) == runtime_nbits \
? (~(a - a)) \
: (~((~(a - a)) << runtime_nbits)))
#define check_sadd_int(a,b) \
/* this test is a reduction of (b > 0) ? (a + b > typemax(a)) : (a + b < typemin(a)) ==> overflow \
* where (a - a) == (typeof(a))0 */ \
(b > 0) ? (a > ~((a - a + 1) << (8 * sizeof(a) - 1)) - b) : (a < ((a - a + 1) << (8 * sizeof(a) - 1)) - b)
/* this test checks for (b >= 0) ? (a + b > typemax) : (a + b < typemin) ==> overflow */ \
(b >= 0) ? (a > sTYPEMAX(a) - b) : (a < sTYPEMIN(a) - b)
checked_iintrinsic_fast(LLVMAdd_sov, check_sadd_int, add, checked_sadd_int, )
#define check_uadd_int(a,b) \
/* this test checks for (a + b) > typemax(a) ==> overflow */ \
a >= -b
a > uTYPEMAX(a) - b
checked_iintrinsic_fast(LLVMAdd_uov, check_uadd_int, add, checked_uadd_int, u)
#define check_ssub_int(a,b) check_sadd_int(a,-b)
#define check_ssub_int(a,b) \
/* this test checks for (b >= 0) ? (a - b < typemin) : (a - b > typemax) ==> overflow */ \
(b >= 0) ? (a < sTYPEMIN(a) + b) : (a > sTYPEMAX(a) + b)
checked_iintrinsic_fast(LLVMSub_sov, check_ssub_int, sub, checked_ssub_int, )
#define check_usub_int(a,b) \
/* this test checks for (a - b) < 0 ==> overflow */ \
a < b
/* this test checks for (a - b) < typemin ==> overflow */ \
a < uTYPEMIN(a) + b
checked_iintrinsic_fast(LLVMSub_uov, check_usub_int, sub, checked_usub_int, u)
checked_iintrinsic_slow(LLVMMul_sov, checked_smul_int, )
checked_iintrinsic_slow(LLVMMul_uov, checked_umul_int, u)
Expand All @@ -883,15 +902,13 @@ checked_iintrinsic_div(LLVMRem_uov, checked_urem_int, u)
#define flipsign(a, b) \
(b >= 0) ? a : -a
bi_iintrinsic_fast(jl_LLVMFlipSign, flipsign, flipsign_int, )
#define abs_float(pr, a) *pr = fp_select(a, fabs)
#define ceil_float(pr, a) *pr = fp_select(a, ceil)
#define floor_float(pr, a) *pr = fp_select(a, floor)
#define trunc_float(pr, a) *pr = fp_select(a, trunc)
#define rint_float(pr, a) *pr = fp_select(a, rint)
#define sqrt_float(pr, a) \
*pr = fp_select(a, sqrt)
#define copysign_float(a, b) \
fp_select2(a, b, copysign)
#define abs_float(pr, a) *pr = fp_select(a, fabs)
#define ceil_float(pr, a) *pr = fp_select(a, ceil)
#define floor_float(pr, a) *pr = fp_select(a, floor)
#define trunc_float(pr, a) *pr = fp_select(a, trunc)
#define rint_float(pr, a) *pr = fp_select(a, rint)
#define sqrt_float(pr, a) *pr = fp_select(a, sqrt)
#define copysign_float(a, b) fp_select2(a, b, copysign)

un_fintrinsic(abs_float,abs_float)
bi_fintrinsic(copysign_float,copysign_float)
Expand Down
28 changes: 17 additions & 11 deletions test/compiler/interpreter_exec.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ let m = Meta.@lower 1 + 1
Expr(:return, Core.SSAValue(6)),
]
nstmts = length(src.code)
src.ssavaluetypes = nstmts
src.ssavaluetypes = Any[ Any for _ = 1:nstmts ]
src.codelocs = fill(Int32(1), nstmts)
src.inferred = true
Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
global test29262 = true
@test :a === @eval $m
Expand All @@ -38,25 +39,29 @@ let m = Meta.@lower 1 + 1
QuoteNode(:c),
GlobalRef(@__MODULE__, :test29262),
# block 2
Core.PhiNode(Any[4, 13], Any[false, true]), # false, true
Core.PhiNode(Any[4, 13], Any[Core.SSAValue(1), Core.SSAValue(2)]), # :a, :b
Core.PhiNode(Any[4, 13], Any[Core.SSAValue(3), Core.SSAValue(6)]), # :c, :a
Core.PhiNode(Any[13], Any[Core.SSAValue(7)]), # NULL, :c
Core.PhiNode(Any[4, 16], Any[false, true]), # false, true
Core.PhiNode(Any[4, 16], Any[Core.SSAValue(1), Core.SSAValue(2)]), # :a, :b
Core.PhiNode(Any[4, 16], Any[Core.SSAValue(3), Core.SSAValue(6)]), # :c, :a
Core.PhiNode(Any[16], Any[Core.SSAValue(7)]), # NULL, :c
# block 3
Core.PhiNode(Any[], Any[]), # NULL, NULL
Core.PhiNode(Any[14, 8], Any[true, Core.SSAValue(4)]), # test29262, test29262, [true]
Core.PhiNode(Any[14, 8], Any[Core.SSAValue(2), Core.SSAValue(8)]), # NULL, :c, [:b]
Core.PhiNode(Any[17, 8], Any[true, Core.SSAValue(4)]), # test29262, test29262, [true]
Core.PhiNode(Any[17], Vector{Any}(undef, 1)), # NULL, NULL
Core.PhiNode(Any[8], Vector{Any}(undef, 1)), # NULL, NULL
Core.PhiNode(Any[], Any[]), # NULL, NULL
Core.PhiNode(Any[17, 8], Any[Core.SSAValue(2), Core.SSAValue(8)]), # NULL, :c, [:b]
Core.PhiNode(Any[], Any[]), # NULL, NULL
Expr(:gotoifnot, Core.SSAValue(5), 5),
# block 4
Expr(:gotoifnot, Core.SSAValue(10), 9),
# block 5
Expr(:call, GlobalRef(Core, :tuple), Core.SSAValue(6), Core.SSAValue(7), Core.SSAValue(8), Core.SSAValue(11)),
Expr(:return, Core.SSAValue(15)),
Expr(:call, GlobalRef(Core, :tuple), Core.SSAValue(6), Core.SSAValue(7), Core.SSAValue(8), Core.SSAValue(14)),
Expr(:return, Core.SSAValue(18)),
]
nstmts = length(src.code)
src.ssavaluetypes = nstmts
src.ssavaluetypes = Any[ Any for _ = 1:nstmts ]
src.codelocs = fill(Int32(1), nstmts)
src.inferred = true
Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
global test29262 = true
@test (:b, :a, :c, :c) === @eval $m
Expand Down Expand Up @@ -91,8 +96,9 @@ let m = Meta.@lower 1 + 1
Expr(:return, Core.SSAValue(11)),
]
nstmts = length(src.code)
src.ssavaluetypes = nstmts
src.ssavaluetypes = Any[ Any for _ = 1:nstmts ]
src.codelocs = fill(Int32(1), nstmts)
src.inferred = true
Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
global test29262 = true
@test :a === @eval $m
Expand Down
3 changes: 2 additions & 1 deletion test/compiler/ssair.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ let
# XXX: missing @test
end

let cmd = `$(Base.julia_cmd()) --compile=min interpreter_exec.jl`
for compile in ("min", "yes")
cmd = `$(Base.julia_cmd()) --compile=$compile interpreter_exec.jl`
if !success(pipeline(Cmd(cmd, dir=@__DIR__); stdout=stdout, stderr=stderr))
error("Interpreter test failed, cmd : $cmd")
end
Expand Down
6 changes: 3 additions & 3 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1069,9 +1069,9 @@ let
strct = LoadError("yofile", 0, "bad")
@test nfields(strct) == 3 # sanity test
@test_throws BoundsError(strct, 10) getfield(strct, 10)
@test_throws ErrorException("type LoadError is immutable") setfield!(strct, 0, "")
@test_throws ErrorException("type LoadError is immutable") setfield!(strct, 4, "")
@test_throws ErrorException("type is immutable") setfield!(strct, :line, 0)
@test_throws ErrorException("setfield! immutable struct of type LoadError cannot be changed") setfield!(strct, 0, "")
@test_throws ErrorException("setfield! immutable struct of type LoadError cannot be changed") setfield!(strct, 4, "")
@test_throws ErrorException("setfield! immutable struct of type LoadError cannot be changed") setfield!(strct, :line, 0)
@test strct.file == "yofile"
@test strct.line === 0
@test strct.error == "bad"
Expand Down

0 comments on commit 1f2b16f

Please sign in to comment.