From 54e40e318d0396ee60e190e815788ed6b817a572 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 22 May 2012 00:06:03 -0400 Subject: [PATCH] adding checked float to int conversions for #725 checked_fptoui32, checked_fptosi32, checked_fptoui64, checked_fptosi64 adding integer arithmetic intrinsics with overflow checking for #855 checked_sadd, checked_uadd, checked_ssub, checked_usub, checked_smul, checked_umul domain error for int^(negative int), closes #745 --- base/array.jl | 8 +-- base/base.jl | 6 +-- base/intfuncs.jl | 8 +-- base/random.jl | 2 +- src/alloc.c | 3 ++ src/array.c | 2 +- src/boot.jl | 3 ++ src/builtins.c | 12 +---- src/cgutils.cpp | 26 ++++++++-- src/codegen.cpp | 33 ++++++------ src/init.c | 8 ++- src/intrinsics.cpp | 126 ++++++++++++++++++++++++++++++++++----------- src/julia.h | 5 +- 13 files changed, 162 insertions(+), 80 deletions(-) diff --git a/base/array.jl b/base/array.jl index e89821d177f44..fdf3c669368b7 100644 --- a/base/array.jl +++ b/base/array.jl @@ -618,7 +618,7 @@ end function .^{S<:Integer,T<:Integer}(A::Array{S}, B::Array{T}) F = Array(Float64, promote_shape(size(A), size(B))) for i=1:numel(A) - F[i] = A[i]^B[i] + F[i] = float64(A[i])^float64(B[i]) end return F end @@ -626,14 +626,14 @@ end function .^{T<:Integer}(A::Integer, B::Array{T}) F = similar(B, Float64) for i=1:numel(B) - F[i] = A^B[i] + F[i] = float64(A)^float64(B[i]) end return F end -function _jl_power_array_int_body(F, A, B) +function _jl_power_array_int_body{T}(F::Array{T}, A, B) for i=1:numel(A) - F[i] = A[i]^B + F[i] = A[i]^convert(T,B) end return F end diff --git a/base/base.jl b/base/base.jl index bf7f7392da154..2eff8d074fbd0 100644 --- a/base/base.jl +++ b/base/base.jl @@ -31,9 +31,9 @@ type ArgumentError <: Exception msg::String end -type UnboundError <: Exception - var::Symbol -end +#type UnboundError <: Exception +# var::Symbol +#end type KeyError <: Exception key diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 6bfd2a6f4f6ba..b0f64f56ead6b 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -93,7 +93,7 @@ function power_by_squaring(x, p::Integer) elseif p == 0 return one(x) elseif p < 0 - error("power_by_squaring: exponent must be non-negative") + throw(DomainError()) elseif p == 2 return x*x end @@ -120,8 +120,8 @@ function power_by_squaring(x, p::Integer) return x end -^{T<:Integer}(x::T, p::T) = p < 0 ? x^float(p) : power_by_squaring(x,p) -^(x::Number, p::Integer) = p < 0 ? x^float(p) : power_by_squaring(x,p) +^{T<:Integer}(x::T, p::T) = power_by_squaring(x,p) +^(x::Number, p::Integer) = power_by_squaring(x,p) ^(x, p::Integer) = power_by_squaring(x,p) # x^p mod m @@ -129,7 +129,7 @@ function powermod(x::Integer, p::Integer, m::Integer) if p == 0 return one(x) elseif p < 0 - error("powermod: exponent must be non-negative") + throw(DomainError()) end t = 1 while t <= p diff --git a/base/random.jl b/base/random.jl index 0f923ec2a0ce3..9901f9a80457a 100644 --- a/base/random.jl +++ b/base/random.jl @@ -246,7 +246,7 @@ const chi2rnd = randchi2 # alias chi2rnd # http://www.johndcook.com/julia_rng.html function randbeta(a, b) if a <= 0 || b <= 0 - error("Beta parameters must be positive") + error("beta parameters must be positive") end ## There are more efficient methods for generating beta samples. diff --git a/src/alloc.c b/src/alloc.c index 5e86e84a7a783..98419260c9cfb 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -51,6 +51,9 @@ jl_bits_type_t *jl_pointer_type; jl_value_t *jl_an_empty_cell=NULL; jl_value_t *jl_stackovf_exception; jl_value_t *jl_divbyzero_exception; +jl_value_t *jl_domain_exception; +jl_value_t *jl_overflow_exception; +jl_value_t *jl_inexact_exception; jl_value_t *jl_undefref_exception; jl_value_t *jl_interrupt_exception; jl_value_t *jl_memory_exception; diff --git a/src/array.c b/src/array.c index 536eaafcaf809..187320743c48c 100644 --- a/src/array.c +++ b/src/array.c @@ -371,7 +371,7 @@ jl_value_t *jl_arrayref(jl_array_t *a, size_t i) else { elt = ((jl_value_t**)a->data)[i]; if (elt == NULL) { - jl_undef_ref_error(); + jl_raise(jl_undefref_exception); } } return elt; diff --git a/src/boot.jl b/src/boot.jl index 138cd5c1089c3..6c3957ee7fd3a 100644 --- a/src/boot.jl +++ b/src/boot.jl @@ -160,6 +160,9 @@ abstract Exception type BoundsError <: Exception end type DivideByZeroError <: Exception end +type DomainError <: Exception end +type OverflowError <: Exception end +type InexactError <: Exception end type MemoryError <: Exception end type IOError <: Exception end type StackOverflowError <: Exception end diff --git a/src/builtins.c b/src/builtins.c index 5e757068c9452..181b33dadb58d 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -77,16 +77,6 @@ void jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got) jl_type_error_rt(fname, "", expected, got); } -void jl_undef_ref_error(void) -{ - jl_raise(jl_undefref_exception); -} - -void jl_divide_by_zero_error(void) -{ - jl_raise(jl_divbyzero_exception); -} - JL_CALLABLE(jl_f_throw) { JL_NARGS(throw, 1, 1); @@ -610,7 +600,7 @@ static jl_value_t *nth_field(jl_value_t *v, size_t i) { jl_value_t *fld = ((jl_value_t**)v)[1+i]; if (fld == NULL) - jl_undef_ref_error(); + jl_raise(jl_undefref_exception); return fld; } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ac509b9d84ff5..a009673656466 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -89,23 +89,39 @@ static void error_unless(Value *cond, const std::string &msg, jl_codectx_t *ctx) builder.SetInsertPoint(passBB); } -static void call_error_func_unless(Value *cond, Function *errfunc, - jl_codectx_t *ctx) +static void raise_exception_unless(Value *cond, Value *exc, jl_codectx_t *ctx) { BasicBlock *failBB = BasicBlock::Create(getGlobalContext(),"fail",ctx->f); BasicBlock *passBB = BasicBlock::Create(getGlobalContext(),"pass"); builder.CreateCondBr(cond, passBB, failBB); builder.SetInsertPoint(failBB); - builder.CreateCall(errfunc); + builder.CreateCall(jlraise_func, exc); builder.CreateBr(passBB); ctx->f->getBasicBlockList().push_back(passBB); builder.SetInsertPoint(passBB); } +static void raise_exception_unless(Value *cond, GlobalVariable *exc, + jl_codectx_t *ctx) +{ + raise_exception_unless(cond, (Value*)builder.CreateLoad(exc, false), ctx); +} + +static void raise_exception_if(Value *cond, Value *exc, jl_codectx_t *ctx) +{ + raise_exception_unless(builder.CreateXor(cond, ConstantInt::get(T_int1,-1)), + exc, ctx); +} + +static void raise_exception_if(Value *cond, GlobalVariable *exc, + jl_codectx_t *ctx) +{ + raise_exception_if(cond, (Value*)builder.CreateLoad(exc, false), ctx); +} + static void null_pointer_check(Value *v, jl_codectx_t *ctx) { - call_error_func_unless(builder.CreateICmpNE(v, V_null), - jluniniterror_func, ctx); + raise_exception_unless(builder.CreateICmpNE(v,V_null), jlundeferr_var, ctx); } static Value *boxed(Value *v); diff --git a/src/codegen.cpp b/src/codegen.cpp index 068eb9f49232e..b8c29b34b79b1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -89,13 +89,16 @@ static GlobalVariable *jlfloat32temp_var; static GlobalVariable *jlpgcstack_var; #endif static GlobalVariable *jlexc_var; +static GlobalVariable *jldiverr_var; +static GlobalVariable *jlundeferr_var; +static GlobalVariable *jldomerr_var; +static GlobalVariable *jlovferr_var; +static GlobalVariable *jlinexacterr_var; // important functions static Function *jlnew_func; static Function *jlraise_func; static Function *jlerror_func; -static Function *jluniniterror_func; -static Function *jldiverror_func; static Function *jltypeerror_func; static Function *jlcheckassign_func; static Function *jldeclareconst_func; @@ -1906,6 +1909,16 @@ static void init_julia_llvm_env(Module *m) jlnull_var = global_to_llvm("jl_null", (void*)&jl_null); jlexc_var = global_to_llvm("jl_exception_in_transit", (void*)&jl_exception_in_transit); + jldiverr_var = global_to_llvm("jl_divbyzero_exception", + (void*)&jl_divbyzero_exception); + jlundeferr_var = global_to_llvm("jl_undefref_exception", + (void*)&jl_undefref_exception); + jldomerr_var = global_to_llvm("jl_domain_exception", + (void*)&jl_domain_exception); + jlovferr_var = global_to_llvm("jl_overflow_exception", + (void*)&jl_overflow_exception); + jlinexacterr_var = global_to_llvm("jl_inexact_exception", + (void*)&jl_inexact_exception); jlfloat32temp_var = new GlobalVariable(*jl_Module, T_float32, false, GlobalVariable::PrivateLinkage, @@ -1937,22 +1950,6 @@ static void init_julia_llvm_env(Module *m) (void*)&jl_new_struct_uninit); std::vector empty_args(0); - jluniniterror_func = - Function::Create(FunctionType::get(T_void, empty_args, false), - Function::ExternalLinkage, - "jl_undef_ref_error", jl_Module); - jluniniterror_func->setDoesNotReturn(); - jl_ExecutionEngine->addGlobalMapping(jluniniterror_func, - (void*)&jl_undef_ref_error); - - jldiverror_func = - Function::Create(FunctionType::get(T_void, empty_args, false), - Function::ExternalLinkage, - "jl_divide_by_zero_error", jl_Module); - jldiverror_func->setDoesNotReturn(); - jl_ExecutionEngine->addGlobalMapping(jldiverror_func, - (void*)&jl_divide_by_zero_error); - setjmp_func = Function::Create(FunctionType::get(T_int32, args1, false), Function::ExternalLinkage, "_setjmp", jl_Module); diff --git a/src/init.c b/src/init.c index 5f5e1452a4d02..b64a41540a692 100644 --- a/src/init.c +++ b/src/init.c @@ -53,7 +53,7 @@ void fpe_handler(int arg) sigaddset(&sset, SIGFPE); sigprocmask(SIG_UNBLOCK, &sset, NULL); - jl_divide_by_zero_error(); + jl_raise(jl_divbyzero_exception); } void segv_handler(int sig, siginfo_t *info, void *context) @@ -260,6 +260,12 @@ void jl_get_builtin_hooks(void) jl_apply((jl_function_t*)core("StackOverflowError"), NULL, 0); jl_divbyzero_exception = jl_apply((jl_function_t*)core("DivideByZeroError"), NULL, 0); + jl_domain_exception = + jl_apply((jl_function_t*)core("DomainError"), NULL, 0); + jl_overflow_exception = + jl_apply((jl_function_t*)core("OverflowError"), NULL, 0); + jl_inexact_exception = + jl_apply((jl_function_t*)core("InexactError"), NULL, 0); jl_undefref_exception = jl_apply((jl_function_t*)core("UndefRefError"),NULL,0); jl_interrupt_exception = diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 132ecb30bf669..a8795fde2b322 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -35,6 +35,10 @@ namespace JL_I { abs_float32, abs_float64, copysign_float32, copysign_float64, flipsign_int32, flipsign_int64, + // checked arithmetic + checked_sadd, checked_uadd, checked_ssub, checked_usub, + checked_smul, checked_umul, + checked_fptoui32, checked_fptosi32, checked_fptoui64, checked_fptosi64, // c interface ccall, }; @@ -271,6 +275,28 @@ static Value *generic_zext(jl_value_t *targ, jl_value_t *x, jl_codectx_t *ctx) return builder.CreateZExt(JL_INT(auto_unbox(x,ctx)), to); } +static Value *emit_eqfsi64(Value *x, Value *y) +{ + x = FP(x); + Value *fy = JL_INT(y); + return builder.CreateAnd + (builder.CreateFCmpOEQ(x, builder.CreateSIToFP(fy, T_float64)), + builder.CreateICmpEQ(fy, builder.CreateFPToSI + (builder.CreateSIToFP(fy, T_float64), + T_int64))); +} + +static Value *emit_eqfui64(Value *x, Value *y) +{ + x = FP(x); + Value *fy = JL_INT(y); + return builder.CreateAnd + (builder.CreateFCmpOEQ(x, builder.CreateUIToFP(fy, T_float64)), + builder.CreateICmpEQ(fy, builder.CreateFPToUI + (builder.CreateUIToFP(fy, T_float64), + T_int64))); +} + #define HANDLE(intr,n) \ case intr: if (nargs!=n) jl_error(#intr": wrong number of arguments"); @@ -356,15 +382,15 @@ static Value *emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs, HANDLE(mul_int,2) return builder.CreateMul(JL_INT(x), JL_INT(y)); HANDLE(sdiv_int,2) den = JL_INT(y); - call_error_func_unless(builder.CreateICmpNE(den, + raise_exception_unless(builder.CreateICmpNE(den, ConstantInt::get(t,0)), - jldiverror_func, ctx); + jldiverr_var, ctx); return builder.CreateSDiv(JL_INT(x), den); HANDLE(udiv_int,2) den = JL_INT(y); - call_error_func_unless(builder.CreateICmpNE(den, + raise_exception_unless(builder.CreateICmpNE(den, ConstantInt::get(t,0)), - jldiverror_func, ctx); + jldiverr_var, ctx); return builder.CreateUDiv(JL_INT(x), den); HANDLE(srem_int,2) return builder.CreateSRem(JL_INT(x), JL_INT(y)); @@ -377,6 +403,34 @@ static Value *emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs, HANDLE(div_float,2) return builder.CreateFDiv(FP(x), FP(y)); HANDLE(rem_float,2) return builder.CreateFRem(FP(x), FP(y)); + HANDLE(checked_sadd,2) + HANDLE(checked_uadd,2) + HANDLE(checked_ssub,2) + HANDLE(checked_usub,2) + HANDLE(checked_smul,2) + HANDLE(checked_umul,2) { + Value *ix = JL_INT(x); Value *iy = JL_INT(y); + Type *atypes[2] = { ix->getType(), iy->getType() }; + Value *res = builder.CreateCall2 + (Intrinsic::getDeclaration(jl_Module, + f==checked_sadd ? + Intrinsic::sadd_with_overflow : + (f==checked_uadd ? + Intrinsic::uadd_with_overflow : + (f==checked_ssub ? + Intrinsic::ssub_with_overflow : + (f==checked_usub ? + Intrinsic::usub_with_overflow : + (f==checked_smul ? + Intrinsic::smul_with_overflow : + Intrinsic::umul_with_overflow)))), + ArrayRef(atypes)), + ix, iy); + Value *obit = builder.CreateExtractValue(res, ArrayRef(1)); + raise_exception_if(obit, jlovferr_var, ctx); + return builder.CreateExtractValue(res, ArrayRef(0)); + } + HANDLE(eq_int,2) return builder.CreateICmpEQ(JL_INT(x), JL_INT(y)); HANDLE(ne_int,2) return builder.CreateICmpNE(JL_INT(x), JL_INT(y)); HANDLE(slt_int,2) return builder.CreateICmpSLT(JL_INT(x), JL_INT(y)); @@ -389,32 +443,8 @@ static Value *emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs, HANDLE(lt_float,2) return builder.CreateFCmpOLT(FP(x), FP(y)); HANDLE(le_float,2) return builder.CreateFCmpOLE(FP(x), FP(y)); - HANDLE(eqfsi64,2) { - x = FP(x); - fy = JL_INT(y); - return builder.CreateAnd( - builder.CreateFCmpOEQ(x, builder.CreateSIToFP(fy, T_float64)), - builder.CreateICmpEQ( - fy, builder.CreateFPToSI( - builder.CreateSIToFP(fy, T_float64), - T_int64 - ) - ) - ); - } - HANDLE(eqfui64,2) { - x = FP(x); - fy = JL_INT(y); - return builder.CreateAnd( - builder.CreateFCmpOEQ(x, builder.CreateUIToFP(fy, T_float64)), - builder.CreateICmpEQ( - fy, builder.CreateFPToUI( - builder.CreateUIToFP(fy, T_float64), - T_int64 - ) - ) - ); - } + HANDLE(eqfsi64,2) return emit_eqfsi64(x, y); + HANDLE(eqfui64,2) return emit_eqfui64(x, y); HANDLE(ltfsi64,2) { x = FP(x); fy = JL_INT(y); @@ -746,6 +776,37 @@ static Value *emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs, return builder.CreateFPExt(builder.CreateLoad(jlfloat32temp_var, true), T_float64); + HANDLE(checked_fptosi32,1) { + x = FP(x); + Value *v = builder.CreateFPToSI(x, T_int32); + raise_exception_unless + (builder.CreateFCmpOEQ(builder.CreateFPExt(x, T_float64), + builder.CreateSIToFP(v, T_float64)), + jlinexacterr_var, ctx); + return v; + } + HANDLE(checked_fptoui32,1) { + x = FP(x); + Value *v = builder.CreateFPToUI(x, T_int32); + raise_exception_unless + (builder.CreateFCmpOEQ(builder.CreateFPExt(x, T_float64), + builder.CreateUIToFP(v, T_float64)), + jlinexacterr_var, ctx); + return v; + } + HANDLE(checked_fptosi64,1) { + x = FP(x); + Value *v = builder.CreateFPToSI(x, T_int64); + raise_exception_unless(emit_eqfsi64(x, v), jlinexacterr_var, ctx); + return v; + } + HANDLE(checked_fptoui64,1) { + x = FP(x); + Value *v = builder.CreateFPToUI(x, T_int64); + raise_exception_unless(emit_eqfui64(x, v), jlinexacterr_var, ctx); + return v; + } + HANDLE(abs_float32,1) { Value *bits = builder.CreateBitCast(FP(x), T_int32); @@ -906,5 +967,10 @@ extern "C" void jl_init_intrinsic_functions(void) ADD_I(abs_float32); ADD_I(abs_float64); ADD_I(copysign_float32); ADD_I(copysign_float64); ADD_I(flipsign_int32); ADD_I(flipsign_int64); + ADD_I(checked_sadd); ADD_I(checked_uadd); + ADD_I(checked_ssub); ADD_I(checked_usub); + ADD_I(checked_smul); ADD_I(checked_umul); + ADD_I(checked_fptosi32); ADD_I(checked_fptoui32); + ADD_I(checked_fptosi64); ADD_I(checked_fptoui64); ADD_I(ccall); } diff --git a/src/julia.h b/src/julia.h index eca40b82c11e3..f5177ed740d6c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -308,6 +308,9 @@ extern jl_struct_type_t *jl_backtrace_type; extern jl_value_t *jl_stackovf_exception; extern jl_value_t *jl_memory_exception; extern jl_value_t *jl_divbyzero_exception; +extern jl_value_t *jl_domain_exception; +extern jl_value_t *jl_overflow_exception; +extern jl_value_t *jl_inexact_exception; extern jl_value_t *jl_undefref_exception; extern jl_value_t *jl_interrupt_exception; extern jl_value_t *jl_an_empty_cell; @@ -697,8 +700,6 @@ void jl_type_error(const char *fname, jl_value_t *expected, jl_value_t *got); void jl_type_error_rt(const char *fname, const char *context, jl_value_t *ty, jl_value_t *got); jl_value_t *jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na); -void jl_undef_ref_error(void); -void jl_divide_by_zero_error(void); // initialization functions DLLEXPORT void julia_init(char *imageFile);