From 55125539f5e39af512156ed0d03b8dcf2abd7b57 Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Fri, 30 Sep 2016 13:13:14 +0100 Subject: [PATCH] replace checked_fptosi intrinsics with Julia implementation (#14763) Replaces checked_fptosi/checked_fptoui intrinsics with Julia implementations. Fixes #14549. Explain logic behind float->integer conversion checking (cherry picked from commit f935a5035d2815c8a02a67f88ccabdfe4d61e714) --- base/float.jl | 46 +++++++++++++++++++----- base/int.jl | 9 +---- src/intrinsics.cpp | 78 ---------------------------------------- src/intrinsics.h | 2 -- src/julia_internal.h | 2 -- src/runtime_intrinsics.c | 9 ----- test/numbers.jl | 7 ++++ 7 files changed, 46 insertions(+), 107 deletions(-) diff --git a/base/float.jl b/base/float.jl index 01767dd3d4123..a8f21b0e47347 100644 --- a/base/float.jl +++ b/base/float.jl @@ -453,21 +453,51 @@ prevfloat(x::AbstractFloat) = nextfloat(x,-1) for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128) for Tf in (Float32, Float64) - if sizeof(Ti) < sizeof(Tf) || Ti <: Unsigned # Tf(typemin(Ti))-1 is exact - @eval function trunc(::Type{$Ti},x::$Tf) - $(Tf(typemin(Ti))-one(Tf)) < x < $(Tf(typemax(Ti))+one(Tf)) || throw(InexactError()) - unsafe_trunc($Ti,x) + if Ti <: Unsigned || sizeof(Ti) < sizeof(Tf) + # Here `Tf(typemin(Ti))-1` is exact, so we can compare the lower-bound + # directly. `Tf(typemax(Ti))+1` is either always exactly representable, or + # rounded to `Inf` (e.g. when `Ti==UInt128 && Tf==Float32`). + @eval begin + function trunc(::Type{$Ti},x::$Tf) + if $(Tf(typemin(Ti))-one(Tf)) < x < $(Tf(typemax(Ti))+one(Tf)) + return unsafe_trunc($Ti,x) + else + throw(InexactError()) + end + end + function convert(::Type{$Ti}, x::$Tf) + if ($(Tf(typemin(Ti))) <= x <= $(Tf(typemax(Ti)))) && (trunc(x) == x) + return unsafe_trunc($Ti,x) + else + throw(InexactError()) + end + end end else - @eval function trunc(::Type{$Ti},x::$Tf) - $(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))) || throw(InexactError()) - unsafe_trunc($Ti,x) + # Here `eps(Tf(typemin(Ti))) > 1`, so the only value which can be truncated to + # `Tf(typemin(Ti)` is itself. Similarly, `Tf(typemax(Ti))` is inexact and will + # be rounded up. This assumes that `Tf(typemin(Ti)) > -Inf`, which is true for + # these types, but not for `Float16` or larger integer types. + @eval begin + function trunc(::Type{$Ti},x::$Tf) + if $(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))) + return unsafe_trunc($Ti,x) + else + throw(InexactError()) + end + end + function convert(::Type{$Ti}, x::$Tf) + if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti)))) && (trunc(x) == x) + return unsafe_trunc($Ti,x) + else + throw(InexactError()) + end + end end end end end - @eval begin issubnormal(x::Float32) = (abs(x) < $(box(Float32,unbox(UInt32,0x00800000)))) & (x!=0) issubnormal(x::Float64) = (abs(x) < $(box(Float64,unbox(UInt64,0x0010000000000000)))) & (x!=0) diff --git a/base/int.jl b/base/int.jl index 6b244499f4b0b..336013ec31b65 100644 --- a/base/int.jl +++ b/base/int.jl @@ -236,14 +236,7 @@ rem{T<:Integer}(x::T, ::Type{T}) = x rem(x::Integer, ::Type{Bool}) = ((x&1)!=0) mod{T<:Integer}(x::Integer, ::Type{T}) = rem(x, T) -convert{Tf<:Union{Float32,Float64}}(T::BitSigned64T, x::Tf) = - box(T,checked_fptosi(T,unbox(Tf,x))) -convert{Tf<:Union{Float32,Float64}}(T::BitUnsigned64T, x::Tf) = - box(T,checked_fptoui(T,unbox(Tf,x))) - -convert{Tf<:Union{Float32,Float64}}(T::Union{Type{Int128},Type{UInt128}}, x::Tf) = - (isinteger(x) || throw(InexactError()) ; trunc(T,x)) - +unsafe_trunc{T<:Integer}(::Type{T}, x::Integer) = rem(x, T) for (Ts, Tu) in ((Int8, UInt8), (Int16, UInt16), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128)) @eval convert(::Type{Signed}, x::$Tu) = convert($Ts, x) @eval convert(::Type{Unsigned}, x::$Ts) = convert($Tu, x) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 47452dc53ead3..74bdc8efe47a2 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -653,80 +653,6 @@ static jl_cgval_t generic_zext(jl_value_t *targ, jl_value_t *x, jl_codectx_t *ct return mark_julia_type(ans, false, jlto, ctx); } -static Value *emit_eqfsi(Value *x, Value *y) -{ - Value *fy = JL_INT(y); - - // using all 64-bit is slightly faster than using mixed sizes - Value *xx = x, *vv = fy; - if (x->getType() == T_float32) - xx = builder.CreateFPExt(xx, T_float64); - if (vv->getType()->getPrimitiveSizeInBits() < 64) - vv = builder.CreateSExt(vv, T_int64); - - Value *back = builder.CreateSIToFP(vv, xx->getType()); - return builder.CreateAnd - (builder.CreateFCmpOEQ(xx, back), - builder.CreateICmpEQ(vv, builder.CreateFPToSI(back, vv->getType()))); -} - -static Value *emit_eqfui(Value *x, Value *y) -{ - Value *fy = JL_INT(y); - - // using all 64-bit is slightly faster than using mixed sizes - Value *xx = x, *vv = fy; - if (x->getType() == T_float32) - xx = builder.CreateFPExt(xx, T_float64); - if (vv->getType()->getPrimitiveSizeInBits() < 64) - vv = builder.CreateZExt(vv, T_int64); - - Value *back = builder.CreateUIToFP(vv, xx->getType()); - return builder.CreateAnd - (builder.CreateFCmpOEQ(xx, back), - builder.CreateICmpEQ(vv, builder.CreateFPToUI(back, vv->getType()))); -} - -static jl_cgval_t emit_checked_fptosi(jl_value_t *targ, jl_value_t *x, jl_codectx_t *ctx) -{ - jl_value_t *jlto = staticeval_bitstype(targ, "checked_fptosi", ctx); - if (!jlto) return jl_cgval_t(); - Type *to = staticeval_bitstype(jlto); - Value *fx = FP(auto_unbox(x, ctx)); - if (fx->getType() == T_void) return jl_cgval_t(); // auto_unbox threw an error - Value *ans = builder.CreateFPToSI(fx, to); - if (fx->getType() == T_float32 && to == T_int32) { - raise_exception_unless - (builder.CreateFCmpOEQ(builder.CreateFPExt(fx, T_float64), - builder.CreateSIToFP(ans, T_float64)), - literal_pointer_val(jl_inexact_exception), ctx); - } - else { - raise_exception_unless(emit_eqfsi(fx, ans), literal_pointer_val(jl_inexact_exception), ctx); - } - return mark_julia_type(ans, false, jlto, ctx); -} - -static jl_cgval_t emit_checked_fptoui(jl_value_t *targ, jl_value_t *x, jl_codectx_t *ctx) -{ - jl_value_t *jlto = staticeval_bitstype(targ, "checked_fptoui", ctx); - if (!jlto) return jl_cgval_t(); - Type *to = staticeval_bitstype(jlto); - Value *fx = FP(auto_unbox(x, ctx)); - if (fx->getType() == T_void) return jl_cgval_t(); // auto_unbox threw an error - Value *ans = builder.CreateFPToUI(fx, to); - if (fx->getType() == T_float32 && to == T_int32) { - raise_exception_unless - (builder.CreateFCmpOEQ(builder.CreateFPExt(fx, T_float64), - builder.CreateUIToFP(ans, T_float64)), - literal_pointer_val(jl_inexact_exception), ctx); - } - else { - raise_exception_unless(emit_eqfui(fx, ans), literal_pointer_val(jl_inexact_exception), ctx); - } - return mark_julia_type(ans, false, jlto, ctx); -} - static jl_cgval_t emit_runtime_pointerref(jl_value_t *e, jl_value_t *i, jl_value_t *align, jl_codectx_t *ctx) { jl_cgval_t parg = emit_expr(e, ctx); @@ -989,10 +915,6 @@ static jl_cgval_t emit_intrinsic(intrinsic f, jl_value_t **args, size_t nargs, return generic_sext(args[1], args[2], ctx); case zext_int: return generic_zext(args[1], args[2], ctx); - case checked_fptosi: - return emit_checked_fptosi(args[1], args[2], ctx); - case checked_fptoui: - return emit_checked_fptoui(args[1], args[2], ctx); case uitofp: { jl_value_t *bt = staticeval_bitstype(args[1], "uitofp", ctx); diff --git a/src/intrinsics.h b/src/intrinsics.h index eb72f3244e816..6c15b14bf6d09 100644 --- a/src/intrinsics.h +++ b/src/intrinsics.h @@ -68,8 +68,6 @@ ADD_I(fptrunc, 2) \ ADD_I(fpext, 2) \ /* checked conversion */ \ - ADD_I(checked_fptosi, 2) \ - ADD_I(checked_fptoui, 2) \ ADD_I(checked_trunc_sint, 2) \ ADD_I(checked_trunc_uint, 2) \ ADD_I(check_top_bit, 1) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index 0b7ae35b870f2..3a4d8d2272439 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -619,8 +619,6 @@ JL_DLLEXPORT jl_value_t *jl_fpext(jl_value_t *ty, jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_fptoui_auto(jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_fptosi_auto(jl_value_t *a); -JL_DLLEXPORT jl_value_t *jl_checked_fptoui(jl_value_t *ty, jl_value_t *a); -JL_DLLEXPORT jl_value_t *jl_checked_fptosi(jl_value_t *ty, jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_checked_trunc_sint(jl_value_t *ty, jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_checked_trunc_uint(jl_value_t *ty, jl_value_t *a); diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 6ea3a9d7dfcc2..405eff4731fa8 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -824,15 +824,6 @@ static unsigned check_trunc_uint(unsigned isize, unsigned osize, void *pa) } cvt_iintrinsic_checked(LLVMTrunc, check_trunc_uint, checked_trunc_uint) -#define checked_fptosi(pr, a) \ - if (!LLVMFPtoSI_exact(sizeof(a) * host_char_bit, pa, osize, pr)) \ - jl_throw(jl_inexact_exception); -un_fintrinsic_withtype(checked_fptosi, checked_fptosi) -#define checked_fptoui(pr, a) \ - if (!LLVMFPtoUI_exact(sizeof(a) * host_char_bit, pa, osize, pr)) \ - jl_throw(jl_inexact_exception); -un_fintrinsic_withtype(checked_fptoui, checked_fptoui) - JL_DLLEXPORT jl_value_t *jl_check_top_bit(jl_value_t *a) { jl_value_t *ty = jl_typeof(a); diff --git a/test/numbers.jl b/test/numbers.jl index 25dbf557d2ec2..5975953e7f9fe 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -2453,6 +2453,13 @@ end @test_throws InexactError convert(Int16, typemax(UInt64)) @test_throws InexactError convert(Int, typemax(UInt64)) +# issue #14549 +for T in (Int8, Int16, UInt8, UInt16) + for F in (Float32,Float64) + @test_throws InexactError convert(T, F(200000.0)) + end +end + let x = big(-0.0) @test signbit(x) && !signbit(abs(x)) end