Skip to content

Commit

Permalink
smarter bounds checking
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vtjnash committed Jan 1, 2015
1 parent a820975 commit 2fdaf10
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 82 deletions.
1 change: 1 addition & 0 deletions base/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
27 changes: 17 additions & 10 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 26 additions & 8 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,28 @@ 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));
}
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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -309,19 +323,23 @@ 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*));
}
va_end(args);
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
Expand Down
10 changes: 5 additions & 5 deletions src/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
75 changes: 62 additions & 13 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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]);
Expand All @@ -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]);
Expand Down
Loading

0 comments on commit 2fdaf10

Please sign in to comment.