From 2fdaf10d3abf5189d60dc70cd9829e4b9ecfaf76 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 1 Jan 2015 06:10:22 -0500 Subject: [PATCH 1/3] smarter bounds checking this adds argument information to intrinsic bounds checking operations, optimized to have a no added impact on the runtime cost in the common case of not having an error --- base/base.jl | 1 + base/boot.jl | 27 ++++++++----- base/replutil.jl | 15 +++++++ src/alloc.c | 34 ++++++++++++---- src/array.c | 10 ++--- src/builtins.c | 75 ++++++++++++++++++++++++++++------ src/cgutils.cpp | 52 +++++++++++++++++++----- src/codegen.cpp | 95 +++++++++++++++++++++++++++++++++++++------- src/gc.c | 1 + src/gf.c | 9 +++-- src/init.c | 18 ++++----- src/julia.h | 21 ++++++---- src/julia_internal.h | 2 + 13 files changed, 278 insertions(+), 82 deletions(-) diff --git a/base/base.jl b/base/base.jl index 8ba2c3a931487..daf59b0f9433a 100644 --- a/base/base.jl +++ b/base/base.jl @@ -6,6 +6,7 @@ const Bottom = Union() # constructors for Core types in boot.jl call(T::Type{BoundsError}) = Core.call(T) +call(T::Type{BoundsError}, args...) = Core.call(T, args...) call(T::Type{DivideError}) = Core.call(T) call(T::Type{DomainError}) = Core.call(T) call(T::Type{OverflowError}) = Core.call(T) diff --git a/base/boot.jl b/base/boot.jl index 8c6f984be0770..04723a0c61e3a 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -201,18 +201,25 @@ end abstract Exception -type BoundsError <: Exception end -type DivideError <: Exception end -type DomainError <: Exception end -type OverflowError <: Exception end -type InexactError <: Exception end -type MemoryError <: Exception end -type StackOverflowError <: Exception end -type UndefRefError <: Exception end -type UndefVarError <: Exception +immutable BoundsError <: Exception + a::Any + i::Union(Tuple, Int) + BoundsError() = new() + BoundsError(a::ANY) = new(a) + BoundsError(a::ANY, i::Tuple) = new(a,i) + BoundsError(a::ANY, i::Int) = new(a,i) +end +immutable DivideError <: Exception end +immutable DomainError <: Exception end +immutable OverflowError <: Exception end +immutable InexactError <: Exception end +immutable MemoryError <: Exception end +immutable StackOverflowError <: Exception end +immutable UndefRefError <: Exception end +immutable UndefVarError <: Exception var::Symbol end -type InterruptException <: Exception end +immutable InterruptException <: Exception end abstract AbstractString abstract DirectIndexString <: AbstractString diff --git a/base/replutil.jl b/base/replutil.jl index e5d96e6e1dd94..8cf6942a477d8 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -41,6 +41,21 @@ writemime(io::IO, ::MIME"text/plain", t::Union(KeyIterator, ValueIterator)) = showerror(io::IO, e) = show(io, e) +function show(io::IO, be::BoundsError) + print(io, "BoundsError(") + if isdefined(be, :a) + print(io, "\n of ") + writemime(io, MIME"text/plain"(), be.a) + if isdefined(be, :i) + print(io, "\n at index [") + print_joined(io, be.i, ',') + print(io, ']') + end + print(io, "\n ") + end + print(io, ')') +end + function showerror(io::IO, e::TypeError) ctx = isempty(e.context) ? "" : "in $(e.context), " if e.expected === Bool diff --git a/src/alloc.c b/src/alloc.c index ce6af35c62f67..b8ef62149d211 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -120,7 +120,7 @@ static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) jl_tuple_t *tuple = (jl_tuple_t*)dt; *len = LLT_ALIGN(*len, jl_new_bits_align(dt)); size_t i, l = jl_tuple_len(tuple); - jl_value_t *v = (jl_value_t*) jl_alloc_tuple(l); + jl_value_t *v = (jl_value_t*)jl_alloc_tuple(l); JL_GC_PUSH1(v); for (i = 0; i < l; i++) { jl_tupleset(v,i,jl_new_bits_internal(jl_tupleref(tuple,i), (char*)data, len)); @@ -128,6 +128,20 @@ static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) JL_GC_POP(); return v; } + if (jl_is_ntuple_type(dt)) { + jl_value_t *lenvar = jl_tparam0(dt); + jl_value_t *elty = jl_tparam1(dt); + *len = LLT_ALIGN(*len, jl_new_bits_align(elty)); + assert(jl_is_long(lenvar)); + size_t i, l = jl_unbox_long(lenvar); + jl_value_t *v = (jl_value_t*)jl_alloc_tuple(l); + JL_GC_PUSH1(v); + for (i = 0; i < l; i++) { + jl_tupleset(v, i, jl_new_bits_internal(elty, (char*)data, len)); + } + JL_GC_POP(); + return v; + } jl_datatype_t *bt = (jl_datatype_t*)dt; size_t nb = jl_datatype_size(bt); @@ -233,7 +247,7 @@ jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); if (i >= jl_tuple_len(st->names)) - jl_throw(jl_bounds_exception); + jl_new_bounds_error_i(v, i+1); size_t offs = jl_field_offset(st,i) + sizeof(void*); if (st->fields[i].isptr) { jl_value_t *fval = *(jl_value_t**)((char*)v + offs); @@ -309,12 +323,7 @@ DLLEXPORT jl_tuple_t *jl_tuple(size_t n, ...) va_list args; if (n == 0) return jl_null; va_start(args, n); -#ifdef OVERLAP_TUPLE_LEN - jl_tuple_t *jv = (jl_tuple_t*)newobj((jl_value_t*)jl_tuple_type, n); -#else - jl_tuple_t *jv = (jl_tuple_t*)newobj((jl_value_t*)jl_tuple_type, n+1); -#endif - jl_tuple_set_len_unsafe(jv, n); + jl_tuple_t *jv = jl_alloc_tuple_uninit(n); for(size_t i=0; i < n; i++) { jl_tupleset(jv, i, va_arg(args, jl_value_t*)); } @@ -322,6 +331,15 @@ DLLEXPORT jl_tuple_t *jl_tuple(size_t n, ...) return jv; } +DLLEXPORT jl_tuple_t *jl_tuplev(size_t n, jl_value_t **v) +{ + jl_tuple_t *jv = jl_alloc_tuple_uninit(n); + for(size_t i=0; i < n; i++) { + jl_tupleset(jv, i, v[i]); + } + return jv; +} + jl_tuple_t *jl_tuple1(void *a) { #ifdef OVERLAP_TUPLE_LEN diff --git a/src/array.c b/src/array.c index 96bc7b315a7da..fb55da12e9a7b 100644 --- a/src/array.c +++ b/src/array.c @@ -441,13 +441,13 @@ static size_t array_nd_index(jl_array_t *a, jl_value_t **args, size_t nidxs, i += ii * stride; size_t d = k>=nd ? 1 : jl_array_dim(a, k); if (k < nidxs-1 && ii >= d) - jl_throw(jl_bounds_exception); + jl_new_bounds_error_v((jl_value_t*)a, args, nidxs); stride *= d; } for(; k < nd; k++) stride *= jl_array_dim(a, k); if (i >= stride) - jl_throw(jl_bounds_exception); + jl_new_bounds_error_v((jl_value_t*)a, args, nidxs); return i; } @@ -518,7 +518,7 @@ JL_CALLABLE(jl_f_arrayset) void jl_arrayunset(jl_array_t *a, size_t i) { if (i >= jl_array_len(a)) - jl_throw(jl_bounds_exception); + jl_new_bounds_error_i((jl_value_t*)a, i+1); char *ptail = (char*)a->data + i*a->elsize; if (a->ptrarray) memset(ptail, 0, a->elsize); @@ -619,7 +619,7 @@ void jl_array_del_end(jl_array_t *a, size_t dec) { if (dec == 0) return; if (dec > a->nrows) - jl_throw(jl_bounds_exception); + jl_new_bounds_error_i((jl_value_t*)a, a->nrows - dec); if (a->isshared) array_try_unshare(a); if (a->elsize > 0) { char *ptail = (char*)a->data + (a->nrows-dec)*a->elsize; @@ -694,7 +694,7 @@ void jl_array_del_beg(jl_array_t *a, size_t dec) { if (dec == 0) return; if (dec > a->nrows) - jl_throw(jl_bounds_exception); + jl_new_bounds_error_i((jl_value_t*)a, dec); if (a->isshared) array_try_unshare(a); size_t es = a->elsize; size_t nb = dec*es; diff --git a/src/builtins.c b/src/builtins.c index 7ce82990c90df..a6f6f7df5ca38 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -28,7 +28,7 @@ extern "C" { // exceptions ----------------------------------------------------------------- -DLLEXPORT void jl_error(const char *str) +DLLEXPORT void NORETURN jl_error(const char *str) { if (jl_errorexception_type == NULL) { JL_PRINTF(JL_STDERR, "%s", str); @@ -39,7 +39,7 @@ DLLEXPORT void jl_error(const char *str) jl_throw(jl_new_struct(jl_errorexception_type, msg)); } -DLLEXPORT void jl_errorf(const char *fmt, ...) +DLLEXPORT void NORETURN jl_errorf(const char *fmt, ...) { va_list args; ios_t buf; @@ -56,18 +56,18 @@ DLLEXPORT void jl_errorf(const char *fmt, ...) jl_throw(jl_new_struct(jl_errorexception_type, msg)); } -void jl_too_few_args(const char *fname, int min) +void NORETURN jl_too_few_args(const char *fname, int min) { // TODO: ArgumentError jl_errorf("%s: too few arguments (expected %d)", fname, min); } -void jl_too_many_args(const char *fname, int max) +void NORETURN jl_too_many_args(const char *fname, int max) { jl_errorf("%s: too many arguments (expected %d)", fname, max); } -void jl_type_error_rt(const char *fname, const char *context, +void NORETURN jl_type_error_rt(const char *fname, const char *context, jl_value_t *ty, jl_value_t *got) { jl_value_t *ctxt=NULL; @@ -78,18 +78,18 @@ void jl_type_error_rt(const char *fname, const char *context, jl_throw(ex); } -void jl_type_error_rt_line(const char *fname, const char *context, +void NORETURN jl_type_error_rt_line(const char *fname, const char *context, jl_value_t *ty, jl_value_t *got, int line) { jl_type_error_rt(fname, context, ty, got); } -void jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got) +void NORETURN jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got) { jl_type_error_rt(fname, "", expected, got); } -void jl_undefined_var_error(jl_sym_t *var) +DLLEXPORT void NORETURN jl_undefined_var_error(jl_sym_t *var) { if (var->name[0] == '#') { // convention for renamed variables: #...#original_name @@ -100,6 +100,54 @@ void jl_undefined_var_error(jl_sym_t *var) jl_throw(jl_new_struct(jl_undefvarerror_type, var)); } +DLLEXPORT void NORETURN jl_new_bounds_error(jl_value_t* v, jl_value_t* t) // t::Union(Tuple, Int) +{ + JL_GC_PUSH2(v, t); // root arguments so the caller doesn't need to + jl_throw(jl_new_struct((jl_datatype_t*)jl_bounds_exception->type, v, t)); +} + +DLLEXPORT void NORETURN jl_new_bounds_error_v(jl_value_t* v, jl_value_t **idxs, size_t nidxs) +{ + jl_tuple_t *t = NULL; + JL_GC_PUSH2(v, t); // root arguments so the caller doesn't need to + t = jl_tuplev(nidxs, idxs); + jl_throw(jl_new_struct((jl_datatype_t*)jl_bounds_exception->type, v, t)); +} + +DLLEXPORT void NORETURN jl_new_v_bounds_error_i(jl_value_t** v, size_t nv, size_t i) +{ + jl_new_bounds_error_i((jl_value_t*)jl_tuplev(nv, v), i); +} + +DLLEXPORT void NORETURN jl_new_unboxed_bounds_error_i(void* data, jl_value_t *vt, size_t i) +{ + jl_value_t *t = NULL, *v = NULL; + JL_GC_PUSH2(v, t); + v = jl_new_bits(vt, data); + t = jl_box_long(i); + jl_throw(jl_new_struct((jl_datatype_t*)jl_bounds_exception->type, v, t)); +} + +DLLEXPORT void NORETURN jl_new_bounds_error_i(jl_value_t* v, size_t i) +{ + jl_value_t *t = NULL; + JL_GC_PUSH2(v, t); // root arguments so the caller doesn't need to + t = jl_box_long(i); + jl_throw(jl_new_struct((jl_datatype_t*)jl_bounds_exception->type, v, t)); +} + +DLLEXPORT void NORETURN jl_new_bounds_error_unboxed(jl_value_t* v, size_t *idxs, size_t nidxs) +{ + size_t i; + jl_tuple_t *t = NULL; + JL_GC_PUSH2(v, t); // root arguments so the caller doesn't need to + t = jl_alloc_tuple(nidxs); + for (i = 0; i < nidxs; i++) { + jl_tupleset(t, i, jl_box_long(idxs[i])); + } + jl_throw(jl_new_struct((jl_datatype_t*)jl_bounds_exception->type, v, t)); +} + JL_CALLABLE(jl_f_throw) { JL_NARGS(throw, 1, 1); @@ -404,7 +452,8 @@ JL_CALLABLE(jl_f_kwcall) assert(jl_is_gf(sorter)); jl_function_t *m = jl_method_lookup((jl_methtable_t*)sorter->env, args, nargs, 1); if (m == jl_bottom_func) { - return jl_no_method_error(f, args+1, nargs-1); + jl_no_method_error(f, args+1, nargs-1); + // unreachable } return jl_apply(m, args, nargs); @@ -523,7 +572,7 @@ JL_CALLABLE(jl_f_tupleref) jl_tuple_t *t = (jl_tuple_t*)args[0]; size_t i = jl_unbox_long(args[1])-1; if (i >= jl_tuple_len(t)) - jl_throw(jl_bounds_exception); + jl_new_bounds_error(args[0], args[1]); return jl_tupleref(t, i); } @@ -552,7 +601,7 @@ JL_CALLABLE(jl_f_get_field) if (jl_is_long(args[1])) { idx = jl_unbox_long(args[1])-1; if (idx >= jl_tuple_len(st->names)) - jl_throw(jl_bounds_exception); + jl_new_bounds_error(args[0], args[1]); } else { JL_TYPECHK(getfield, symbol, args[1]); @@ -581,7 +630,7 @@ JL_CALLABLE(jl_f_set_field) if (jl_is_long(args[1])) { idx = jl_unbox_long(args[1])-1; if (idx >= jl_tuple_len(st->names)) - jl_throw(jl_bounds_exception); + jl_new_bounds_error(args[0], args[1]); } else { JL_TYPECHK(setfield!, symbol, args[1]); @@ -605,7 +654,7 @@ JL_CALLABLE(jl_f_field_type) if (jl_is_long(args[1])) { field_index = jl_unbox_long(args[1]) - 1; if (field_index < 0 || field_index >= jl_tuple_len(st->names)) - jl_throw(jl_bounds_exception); + jl_new_bounds_error(args[0], args[1]); } else { JL_TYPECHK(fieldtype, symbol, args[1]); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 792c2dc0b90e3..77843d90c202e 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -872,8 +872,7 @@ static void emit_typecheck(Value *x, jl_value_t *type, const std::string &msg, } #define CHECK_BOUNDS 1 - -static Value *emit_bounds_check(Value *i, Value *len, jl_codectx_t *ctx) +static Value *emit_bounds_check(Value *a, jl_value_t *ty, Value *i, Value *len, jl_codectx_t *ctx) { Value *im1 = builder.CreateSub(i, ConstantInt::get(T_size, 1)); #if CHECK_BOUNDS==1 @@ -881,7 +880,30 @@ static Value *emit_bounds_check(Value *i, Value *len, jl_codectx_t *ctx) jl_compileropts.check_bounds != JL_COMPILEROPT_CHECK_BOUNDS_OFF) || jl_compileropts.check_bounds == JL_COMPILEROPT_CHECK_BOUNDS_ON) { Value *ok = builder.CreateICmpULT(im1, len); - raise_exception_unless(ok, prepare_global(jlboundserr_var), ctx); + BasicBlock *failBB = BasicBlock::Create(getGlobalContext(),"fail",ctx->f); + BasicBlock *passBB = BasicBlock::Create(getGlobalContext(),"pass"); + builder.CreateCondBr(ok, passBB, failBB); + builder.SetInsertPoint(failBB); + if (ty == (jl_value_t*)jl_any_type) { + builder.CreateCall3(prepare_call(jlvboundserror_func), a, len, i); + } + else if (ty) { + if (!a->getType()->isPtrOrPtrVectorTy()) { + Value *tempSpace = builder.CreateAlloca(a->getType()); + builder.CreateStore(a, tempSpace); + a = tempSpace; + } + builder.CreateCall3(prepare_call(jluboundserror_func), + builder.CreatePointerCast(a, T_pint8), + literal_pointer_val(ty), + i); + } + else { + builder.CreateCall2(prepare_call(jlboundserror_func), a, i); + } + builder.CreateUnreachable(); + ctx->f->getBasicBlockList().push_back(passBB); + builder.SetInsertPoint(passBB); } #endif return im1; @@ -972,8 +994,8 @@ static void typed_store(Value *ptr, Value *idx_0based, Value *rhs, static Value *julia_bool(Value *cond) { return builder.CreateSelect(cond, - literal_pointer_val(jl_true), - literal_pointer_val(jl_false)); + tbaa_decorate(tbaa_const, builder.CreateLoad(prepare_global(jltrue_var))), + tbaa_decorate(tbaa_const, builder.CreateLoad(prepare_global(jlfalse_var)))); } // --- get the inferred type of an AST node --- @@ -1239,8 +1261,10 @@ static Value *emit_tupleref(Value *tuple, Value *ival, jl_value_t *jt, jl_codect SwitchInst *sw = builder.CreateSwitch(ival,deflt,n); // Anything else is a bounds error builder.SetInsertPoint(deflt); - builder.CreateCall2(prepare_call(jlthrow_line_func), builder.CreateLoad(prepare_global(jlboundserr_var)), - ConstantInt::get(T_int32, ctx->lineno)); + Value *tmp = builder.CreateAlloca(ty); + builder.CreateStore(tuple, tmp); + jl_add_linfo_root(ctx->linfo, jt); + builder.CreateCall3(prepare_call(jluboundserror_func), builder.CreatePointerCast(tmp, T_pint8), literal_pointer_val(jt), ival); builder.CreateUnreachable(); size_t ntuple = jl_tuple_len(jt); PHINode *ret = PHINode::Create(jl_pvalue_llvmt, ntuple); @@ -1411,9 +1435,12 @@ static Value *emit_array_nd_index(Value *a, jl_value_t *ex, size_t nd, jl_value_ endBB = BasicBlock::Create(getGlobalContext(), "idxend"); } #endif + Value **idxs = (Value**)alloca(sizeof(Value*)*nidxs); for(size_t k=0; k < nidxs; k++) { - Value *ii = emit_unbox(T_size, emit_unboxed(args[k], ctx), NULL); - ii = builder.CreateSub(ii, ConstantInt::get(T_size, 1)); + idxs[k] = emit_unbox(T_size, emit_unboxed(args[k], ctx), NULL); + } + for(size_t k=0; k < nidxs; k++) { + Value *ii = builder.CreateSub(idxs[k], ConstantInt::get(T_size, 1)); i = builder.CreateAdd(i, builder.CreateMul(ii, stride)); if (k < nidxs-1) { Value *d = @@ -1438,8 +1465,11 @@ static Value *emit_array_nd_index(Value *a, jl_value_t *ex, size_t nd, jl_value_ ctx->f->getBasicBlockList().push_back(failBB); builder.SetInsertPoint(failBB); - builder.CreateCall2(prepare_call(jlthrow_line_func), tbaa_decorate(tbaa_const,builder.CreateLoad(prepare_global(jlboundserr_var))), - ConstantInt::get(T_int32, ctx->lineno)); + Value *tmp = builder.CreateAlloca(T_size, ConstantInt::get(T_size, nidxs)); + for(size_t k=0; k < nidxs; k++) { + builder.CreateStore(idxs[k], builder.CreateGEP(tmp, ConstantInt::get(T_size, k))); + } + builder.CreateCall3(prepare_call(jlboundserrorv_func), a, tmp, ConstantInt::get(T_size, nidxs)); builder.CreateUnreachable(); ctx->f->getBasicBlockList().push_back(endBB); diff --git a/src/codegen.cpp b/src/codegen.cpp index 5b956fa2892ea..0ac6828755092 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -244,7 +244,6 @@ static GlobalVariable *jlundeferr_var; static GlobalVariable *jldomerr_var; static GlobalVariable *jlovferr_var; static GlobalVariable *jlinexacterr_var; -static GlobalVariable *jlboundserr_var; static GlobalVariable *jlstderr_var; static GlobalVariable *jlRTLD_DEFAULT_var; #ifdef _OS_WINDOWS_ @@ -262,6 +261,10 @@ static Function *jlthrow_line_func; static Function *jlerror_func; static Function *jltypeerror_func; static Function *jlundefvarerror_func; +static Function *jlboundserror_func; +static Function *jluboundserror_func; +static Function *jlvboundserror_func; +static Function *jlboundserrorv_func; static Function *jlcheckassign_func; static Function *jldeclareconst_func; static Function *jltopeval_func; @@ -1930,7 +1933,8 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, Value *valen = emit_n_varargs(ctx); Value *idx = emit_unbox(T_size, emit_unboxed(args[2], ctx),ity); - idx = emit_bounds_check(idx, valen, ctx); + idx = emit_bounds_check(builder.CreateGEP(ctx->argArray, ConstantInt::get(T_size, ctx->nReqArgs)), + (jl_value_t*)jl_any_type, idx, valen, ctx); idx = builder.CreateAdd(idx, ConstantInt::get(T_size, ctx->nReqArgs)); JL_GC_POP(); return tbaa_decorate(tbaa_user, builder. @@ -1948,9 +1952,21 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, return emit_tupleref(arg1,ConstantInt::get(T_size,idx),tty,ctx); } if (idx==0 || (!isseqt && idx > tlen)) { - builder.CreateCall2(prepare_call(jlthrow_line_func), - tbaa_decorate(tbaa_const, builder.CreateLoad(prepare_global(jlboundserr_var))), - ConstantInt::get(T_int32, ctx->lineno)); + // known to be out of bounds + if (arg1->getType() != jl_pvalue_llvmt) { + Value *tmp = builder.CreateAlloca(arg1->getType()); + builder.CreateStore(arg1, tmp); + jl_add_linfo_root(ctx->linfo, tty); + builder.CreateCall3(prepare_call(jluboundserror_func), + builder.CreatePointerCast(tmp, T_pint8), + literal_pointer_val(tty), + ConstantInt::get(T_size, idx)); + } + else { + builder.CreateCall2(prepare_call(jlboundserror_func), + arg1, + ConstantInt::get(T_size, idx)); + } JL_GC_POP(); return V_null; } @@ -1958,7 +1974,12 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, Value *tlen = emit_tuplelen(arg1,tty); Value *idx = emit_unbox(T_size, emit_unboxed(args[2], ctx), ity); - emit_bounds_check(idx, tlen, ctx); + bool unbox = false; + if (arg1->getType() != jl_pvalue_llvmt) { + unbox = true; + jl_add_linfo_root(ctx->linfo, tty); + } + emit_bounds_check(arg1, unbox ? tty : NULL, idx, tlen, ctx); JL_GC_POP(); return emit_tupleref(arg1,idx,tty,ctx); } @@ -1966,7 +1987,7 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, else if (f->fptr == &jl_f_tuple) { if (nargs == 0) { JL_GC_POP(); - return literal_pointer_val((jl_value_t*)jl_null); + return tbaa_decorate(tbaa_const, builder.CreateLoad(prepare_global(jlnull_var))); } size_t i; for(i=0; i < nargs; i++) { @@ -2229,7 +2250,7 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, Type *llvm_st = strct->getType(); if (llvm_st == jl_pvalue_llvmt) { if (is_structtype_all_pointers(stt)) { - idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + idx = emit_bounds_check(strct, NULL, idx, ConstantInt::get(T_size, nfields), ctx); Value *fld = tbaa_decorate(tbaa_user, builder. CreateLoad(builder. @@ -2245,7 +2266,7 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, else if (is_tupletype_homogeneous(stt->types)) { assert(nfields > 0); // nf==0 trapped by all_pointers case jl_value_t *jt = jl_t0(stt->types); - idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + idx = emit_bounds_check(strct, NULL, idx, ConstantInt::get(T_size, nfields), ctx); Value *ptr = data_pointer(strct); JL_GC_POP(); return typed_load(ptr, idx, jt, ctx, stt->mutabl ? tbaa_user : tbaa_immut); @@ -2263,7 +2284,8 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, // frobbing the stack Value *fld; if (nfields == 0) { - emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + emit_bounds_check(tbaa_decorate(tbaa_const, builder.CreateLoad(prepare_global(jlnull_var))), + NULL, idx, ConstantInt::get(T_size, nfields), ctx); fld = UndefValue::get(jl_pvalue_llvmt); } else { @@ -2273,7 +2295,11 @@ static Value *emit_known_call(jl_value_t *ff, jl_value_t **args, size_t nargs, Value *tempSpace = builder.CreateAlloca(llvm_st); builder.CreateStore(strct, tempSpace); jl_value_t *jt = jl_t0(stt->types); - idx = emit_bounds_check(idx, ConstantInt::get(T_size, nfields), ctx); + if (!stt->uid) { + // add root for types not cached + jl_add_linfo_root(ctx->linfo, (jl_value_t*)stt); + } + idx = emit_bounds_check(tempSpace, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), ctx); Value *ptr = builder.CreateGEP(tempSpace, ConstantInt::get(T_size, 0)); fld = typed_load(ptr, idx, jt, ctx, stt->mutabl ? tbaa_user : tbaa_immut); builder.CreateCall(Intrinsic::getDeclaration(jl_Module,Intrinsic::stackrestore), @@ -2804,7 +2830,7 @@ static Value *emit_condition(jl_value_t *cond, const std::string &msg, jl_codect return builder.CreateXor(condV, ConstantInt::get(T_int1,1)); } else if (condV->getType() == jl_pvalue_llvmt) { - return builder.CreateICmpEQ(condV, literal_pointer_val(jl_false)); + return builder.CreateICmpEQ(condV, tbaa_decorate(tbaa_const, builder.CreateLoad(prepare_global(jlfalse_var)))); } // not a boolean return ConstantInt::get(T_int1,0); @@ -4399,8 +4425,6 @@ static void init_julia_llvm_env(Module *m) (void*)&jl_overflow_exception, m); jlinexacterr_var = global_to_llvm("jl_inexact_exception", (void*)&jl_inexact_exception, m); - jlboundserr_var = global_to_llvm("jl_bounds_exception", - (void*)&jl_bounds_exception, m); jlstderr_var = new GlobalVariable(*m, T_int8, true, GlobalVariable::ExternalLinkage, @@ -4458,6 +4482,49 @@ static void init_julia_llvm_env(Module *m) jlundefvarerror_func->setDoesNotReturn(); add_named_global(jlundefvarerror_func, (void*)&jl_undefined_var_error); + std::vector args2_boundserrorv(0); + args2_boundserrorv.push_back(jl_pvalue_llvmt); + args2_boundserrorv.push_back(T_psize); + args2_boundserrorv.push_back(T_size); + jlboundserrorv_func = + Function::Create(FunctionType::get(T_void, args2_boundserrorv, false), + Function::ExternalLinkage, + "jl_new_bounds_error_unboxed", m); + jlboundserrorv_func->setDoesNotReturn(); + add_named_global(jlboundserrorv_func, (void*)&jl_new_bounds_error_unboxed); + + std::vector args2_boundserror(0); + args2_boundserror.push_back(jl_pvalue_llvmt); + args2_boundserror.push_back(T_size); + jlboundserror_func = + Function::Create(FunctionType::get(T_void, args2_boundserror, false), + Function::ExternalLinkage, + "jl_new_bounds_error_i", m); + jlboundserror_func->setDoesNotReturn(); + add_named_global(jlboundserror_func, (void*)&jl_new_bounds_error_i); + + std::vector args3_vboundserror(0); + args3_vboundserror.push_back(jl_ppvalue_llvmt); + args3_vboundserror.push_back(T_size); + args3_vboundserror.push_back(T_size); + jlvboundserror_func = + Function::Create(FunctionType::get(T_void, args3_vboundserror, false), + Function::ExternalLinkage, + "jl_new_v_bounds_error_i", m); + jlvboundserror_func->setDoesNotReturn(); + add_named_global(jlvboundserror_func, (void*)&jl_new_v_bounds_error_i); + + std::vector args3_uboundserror(0); + args3_uboundserror.push_back(T_pint8); + args3_uboundserror.push_back(jl_pvalue_llvmt); + args3_uboundserror.push_back(T_size); + jluboundserror_func = + Function::Create(FunctionType::get(T_void, args3_uboundserror, false), + Function::ExternalLinkage, + "jl_new_unboxed_bounds_error_i", m); + jluboundserror_func->setDoesNotReturn(); + add_named_global(jluboundserror_func, (void*)&jl_new_unboxed_bounds_error_i); + std::vector args2_throw(0); args2_throw.push_back(jl_pvalue_llvmt); args2_throw.push_back(T_int32); diff --git a/src/gc.c b/src/gc.c index 42b6bbad33aff..cf686d00ce07f 100644 --- a/src/gc.c +++ b/src/gc.c @@ -869,6 +869,7 @@ static void gc_mark(void) gc_push_root(jl_null, 0); gc_push_root(jl_true, 0); gc_push_root(jl_false, 0); + gc_push_root(jl_bounds_exception, 0); jl_mark_box_caches(); diff --git a/src/gf.c b/src/gf.c index fdf295efce3d0..5dc3cede44578 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1345,14 +1345,13 @@ jl_methlist_t *jl_method_table_insert(jl_methtable_t *mt, jl_tuple_t *type, return ml; } -jl_value_t *jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na) +void NORETURN jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na) { jl_value_t *argtup = jl_f_tuple(NULL, args, na); JL_GC_PUSH1(&argtup); jl_value_t *fargs[3] = { (jl_value_t*)jl_methoderror_type, (jl_value_t*)f, argtup }; jl_throw(jl_apply(jl_module_call_func(jl_base_module), fargs, 3)); // not reached - return jl_nothing; } static jl_tuple_t *arg_type_tuple(jl_value_t **args, size_t nargs) @@ -1637,7 +1636,8 @@ JL_CALLABLE(jl_apply_generic) show_call(F, args, nargs); #endif JL_GC_POP(); - return jl_no_method_error((jl_function_t*)F, args, nargs); + jl_no_method_error((jl_function_t*)F, args, nargs); + // unreachable } assert(!mfunc->linfo || !mfunc->linfo->inInference); jl_value_t *res = jl_apply(mfunc, args, nargs); @@ -1679,7 +1679,8 @@ jl_value_t *jl_gf_invoke(jl_function_t *gf, jl_tuple_t *types, } if (m == JL_NULL) { - return jl_no_method_error(gf, args, nargs); + jl_no_method_error(gf, args, nargs); + // unreachable } // now we have found the matching definition. diff --git a/src/init.c b/src/init.c index 47e1dc9f95330..a31a205ded99d 100644 --- a/src/init.c +++ b/src/init.c @@ -1228,16 +1228,16 @@ void jl_get_builtin_hooks(void) jl_floatingpoint_type = (jl_datatype_t*)core("FloatingPoint"); jl_number_type = (jl_datatype_t*)core("Number"); - jl_stackovf_exception = jl_new_struct((jl_datatype_t*)core("StackOverflowError")); - jl_diverror_exception = jl_new_struct((jl_datatype_t*)core("DivideError")); - jl_domain_exception = jl_new_struct((jl_datatype_t*)core("DomainError")); - jl_overflow_exception = jl_new_struct((jl_datatype_t*)core("OverflowError")); - jl_inexact_exception = jl_new_struct((jl_datatype_t*)core("InexactError")); - jl_undefref_exception = jl_new_struct((jl_datatype_t*)core("UndefRefError")); + jl_stackovf_exception = jl_new_struct_uninit((jl_datatype_t*)core("StackOverflowError")); + jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError")); + jl_domain_exception = jl_new_struct_uninit((jl_datatype_t*)core("DomainError")); + jl_overflow_exception = jl_new_struct_uninit((jl_datatype_t*)core("OverflowError")); + jl_inexact_exception = jl_new_struct_uninit((jl_datatype_t*)core("InexactError")); + jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError")); jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError"); - jl_interrupt_exception = jl_new_struct((jl_datatype_t*)core("InterruptException")); - jl_bounds_exception = jl_new_struct((jl_datatype_t*)core("BoundsError")); - jl_memory_exception = jl_new_struct((jl_datatype_t*)core("MemoryError")); + jl_interrupt_exception = jl_new_struct_uninit((jl_datatype_t*)core("InterruptException")); + jl_bounds_exception = jl_new_struct_uninit((jl_datatype_t*)core("BoundsError")); + jl_memory_exception = jl_new_struct_uninit((jl_datatype_t*)core("MemoryError")); jl_ascii_string_type = (jl_datatype_t*)core("ASCIIString"); jl_utf8_string_type = (jl_datatype_t*)core("UTF8String"); diff --git a/src/julia.h b/src/julia.h index 37e1ddf4e4ca9..51155173f57da 100644 --- a/src/julia.h +++ b/src/julia.h @@ -646,6 +646,7 @@ DLLEXPORT jl_function_t *jl_new_closure(jl_fptr_t proc, jl_value_t *env, jl_lambda_info_t *li); DLLEXPORT jl_lambda_info_t *jl_new_lambda_info(jl_value_t *ast, jl_tuple_t *sparams); DLLEXPORT jl_tuple_t *jl_tuple(size_t n, ...); +DLLEXPORT jl_tuple_t *jl_tuplev(size_t n, jl_value_t **v); DLLEXPORT jl_tuple_t *jl_tuple1(void *a); DLLEXPORT jl_tuple_t *jl_tuple2(void *a, void *b); DLLEXPORT jl_tuple_t *jl_alloc_tuple(size_t n); @@ -825,16 +826,20 @@ DLLEXPORT jl_value_t *jl_environ(int i); // throwing common exceptions DLLEXPORT void NORETURN jl_error(const char *str); DLLEXPORT void NORETURN jl_errorf(const char *fmt, ...); -DLLEXPORT void jl_too_few_args(const char *fname, int min); -DLLEXPORT void jl_too_many_args(const char *fname, int max); -DLLEXPORT void jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got); -DLLEXPORT void jl_type_error_rt(const char *fname, const char *context, +DLLEXPORT void NORETURN jl_too_few_args(const char *fname, int min); +DLLEXPORT void NORETURN jl_too_many_args(const char *fname, int max); +DLLEXPORT void NORETURN jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got); +DLLEXPORT void NORETURN jl_type_error_rt(const char *fname, const char *context, jl_value_t *ty, jl_value_t *got); -DLLEXPORT void jl_type_error_rt_line(const char *fname, const char *context, +DLLEXPORT void NORETURN jl_type_error_rt_line(const char *fname, const char *context, jl_value_t *ty, jl_value_t *got, int line); -jl_value_t *jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na); -DLLEXPORT void jl_undefined_var_error(jl_sym_t *var); -void jl_check_type_tuple(jl_tuple_t *t, jl_sym_t *name, const char *ctx); +DLLEXPORT void NORETURN jl_undefined_var_error(jl_sym_t *var); +DLLEXPORT void NORETURN jl_new_bounds_error(jl_value_t* v, jl_value_t* t); +DLLEXPORT void NORETURN jl_new_bounds_error_v(jl_value_t* v, jl_value_t **idxs, size_t nidxs); +DLLEXPORT void NORETURN jl_new_bounds_error_i(jl_value_t* v, size_t i); +DLLEXPORT void NORETURN jl_new_v_bounds_error_i(jl_value_t** v, size_t nv, size_t i); +DLLEXPORT void NORETURN jl_new_unboxed_bounds_error_i(void *v, jl_value_t *vt, size_t i); +DLLEXPORT void NORETURN jl_new_bounds_error_unboxed(jl_value_t* v, size_t *idxs, size_t nidxs); DLLEXPORT jl_value_t *jl_exception_occurred(void); DLLEXPORT void jl_exception_clear(void); diff --git a/src/julia_internal.h b/src/julia_internal.h index 25a44e873680a..a491498303742 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -29,6 +29,8 @@ void jl_set_t_uid_ctr(int i); uint32_t jl_get_gs_ctr(void); void jl_set_gs_ctr(uint32_t ctr); +void NORETURN jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na); +void jl_check_type_tuple(jl_tuple_t *t, jl_sym_t *name, const char *ctx); #define JL_CALLABLE(name) \ DLLEXPORT jl_value_t *name(jl_value_t *F, jl_value_t **args, uint32_t nargs) From 49e59edf18340614bb9c278b2324c8dcccfc5d1c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 1 Jan 2015 15:10:53 -0500 Subject: [PATCH 2/3] add BoundsError tests --- test/core.jl | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/core.jl b/test/core.jl index 16e0fde716972..41e401cd860d1 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2011,3 +2011,42 @@ f9520c(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, args...) = 46 @test invoke(f9520b, (Any, Any, Any, Any, Any, Any), 1, 2, 3, 4, 5, 6) == 23 @test invoke(f9520c, (Any, Any, Any, Any, Any, Any), 1, 2, 3, 4, 5, 6) == 46 @test invoke(f9520c, (Any, Any, Any, Any, Any, Any, Any), 1, 2, 3, 4, 5, 6, 7) == 46 + +# pull request #9534 +@test try; a,b,c = 1,2; catch ex; (ex::BoundsError).a === (1,2) && ex.i == 3; end +@test try; [][]; catch ex; isempty((ex::BoundsError).a::Array{Any,1}) && ex.i == (1,); end +@test try; [][1,2]; catch ex; isempty((ex::BoundsError).a::Array{Any,1}) && ex.i == (1,2); end +@test try; [][10]; catch ex; isempty((ex::BoundsError).a::Array{Any,1}) && ex.i == (10,); end +f9534a() = (a=1+2im; a.(-100)) +f9534a(x) = (a=1+2im; a.(x)) +@test try; f9534a() catch ex; (ex::BoundsError).a === 1+2im && ex.i == -100; end +@test try; f9534a(3) catch ex; (ex::BoundsError).a === 1+2im && ex.i == 3; end +f9534b() = (a=(1,2.,""); a[5]) +f9534b(x) = (a=(1,2.,""); a[x]) +@test try; f9534b() catch ex; (ex::BoundsError).a == (1,2.,"") && ex.i == 5; end +@test try; f9534b(4) catch ex; (ex::BoundsError).a == (1,2.,"") && ex.i == 4; end +f9534c() = (a=(1,2.); a[3]) +f9534c(x) = (a=(1,2.); a[x]) +@test try; f9534c() catch ex; (ex::BoundsError).a === (1,2.) && ex.i == 3; end +@test try; f9534c(0) catch ex; (ex::BoundsError).a === (1,2.) && ex.i == 0; end +f9534d() = (a=(1,2,4,6,7); a[7]) +f9534d(x) = (a=(1,2,4,6,7); a[x]) +@test try; f9534d() catch ex; (ex::BoundsError).a === (1,2,4,6,7) && ex.i == 7; end +@test try; f9534d(-1) catch ex; (ex::BoundsError).a === (1,2,4,6,7) && ex.i == -1; end +f9534e(x) = (a=IOBuffer(); a.(x) = 3) +@test try; f9534e(-2) catch ex; is((ex::BoundsError).a,IOBuffer) && ex.i == -2; end +f9534f() = (a=IOBuffer(); a.(-2)) +f9534f(x) = (a=IOBuffer(); a.(x)) +@test try; f9534f() catch ex; isa((ex::BoundsError).a,IOBuffer) && ex.i == -2; end +@test try; f9534f(typemin(Int64)+2) catch ex; isa((ex::BoundsError).a,IOBuffer) && ex.i == typemin(Int64)+2; end +x9634 = 3 +@test try; getfield(1+2im, x9634); catch ex; (ex::BoundsError).a === 1+2im && ex.i == 3; end +@test try; error(BoundsError()) catch ex; !isdefined((ex::BoundsError), :a) && !isdefined((ex::BoundsError), :i); end +@test try; error(BoundsError(Int)) catch ex; (ex::BoundsError).a == Int && !isdefined((ex::BoundsError), :i); end +@test try; error(BoundsError(Int, typemin(Int))) catch ex; (ex::BoundsError).a == Int && (ex::BoundsError).i == typemin(Int); end +@test try; error(BoundsError(Int, (:a,))) catch ex; (ex::BoundsError).a == Int && (ex::BoundsError).i == (:a,); end +f9534g(a,b,c...) = c[0] +@test try; f9534g(1,2,3,4,5,6) catch ex; (ex::BoundsError).a === (3,4,5,6) && ex.i == 0; end +f9534h(a,b,c...) = c[a] +@test f9534h(4,2,3,4,5,6) == 6 +@test try; f9534h(5,2,3,4,5,6) catch ex; (ex::BoundsError).a === (3,4,5,6) && ex.i == 5; end From 59d221e40c359cb3fdfbdfe7730cf247a2116425 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 1 Jan 2015 16:08:26 -0500 Subject: [PATCH 3/3] better BoundsError message and fix 32bit test failure --- base/replutil.jl | 2 +- test/core.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/replutil.jl b/base/replutil.jl index 8cf6942a477d8..b6a62ba88d352 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -44,7 +44,7 @@ showerror(io::IO, e) = show(io, e) function show(io::IO, be::BoundsError) print(io, "BoundsError(") if isdefined(be, :a) - print(io, "\n of ") + print(io, "\n attempt to access ") writemime(io, MIME"text/plain"(), be.a) if isdefined(be, :i) print(io, "\n at index [") diff --git a/test/core.jl b/test/core.jl index 41e401cd860d1..7822fe18b0c8a 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2038,7 +2038,7 @@ f9534e(x) = (a=IOBuffer(); a.(x) = 3) f9534f() = (a=IOBuffer(); a.(-2)) f9534f(x) = (a=IOBuffer(); a.(x)) @test try; f9534f() catch ex; isa((ex::BoundsError).a,IOBuffer) && ex.i == -2; end -@test try; f9534f(typemin(Int64)+2) catch ex; isa((ex::BoundsError).a,IOBuffer) && ex.i == typemin(Int64)+2; end +@test try; f9534f(typemin(Int)+2) catch ex; isa((ex::BoundsError).a,IOBuffer) && ex.i == typemin(Int)+2; end x9634 = 3 @test try; getfield(1+2im, x9634); catch ex; (ex::BoundsError).a === 1+2im && ex.i == 3; end @test try; error(BoundsError()) catch ex; !isdefined((ex::BoundsError), :a) && !isdefined((ex::BoundsError), :i); end