Skip to content

Commit

Permalink
change checked_add/sub/mul intrinsics to return overflow flag instead…
Browse files Browse the repository at this point in the history
… of error.

This is to allow us to catch overflow in a more efficient manner.
  • Loading branch information
simonbyrne committed Oct 7, 2016
1 parent 38a63bd commit 752ea51
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 124 deletions.
193 changes: 95 additions & 98 deletions base/checked.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,9 @@ checked_cld(x::Integer, y::Integer) = checked_cld(promote(x,y)...)
# but no method exists to handle those types
checked_neg{T<:Integer}(x::T) = no_op_err("checked_neg", T)
checked_abs{T<:Integer}(x::T) = no_op_err("checked_abs", T)
checked_add{T<:Integer}(x::T, y::T) = no_op_err("checked_add", T)
checked_sub{T<:Integer}(x::T, y::T) = no_op_err("checked_sub", T)
checked_mul{T<:Integer}(x::T, y::T) = no_op_err("checked_mul", T)
checked_div{T<:Integer}(x::T, y::T) = no_op_err("checked_div", T)
checked_rem{T<:Integer}(x::T, y::T) = no_op_err("checked_rem", T)
checked_fld{T<:Integer}(x::T, y::T) = no_op_err("checked_fld", T)
checked_mod{T<:Integer}(x::T, y::T) = no_op_err("checked_mod", T)
checked_cld{T<:Integer}(x::T, y::T) = no_op_err("checked_cld", T)

typealias SignedInt Union{Int8,Int16,Int32,Int64,Int128}
typealias UnsignedInt Union{UInt8,UInt16,UInt32,UInt64,UInt128,Bool}
typealias UnsignedInt Union{UInt8,UInt16,UInt32,UInt64,UInt128}

# LLVM has several code generation bugs for checked integer arithmetic (see e.g.
# #4905). We thus distinguish between operations that can be implemented via
Expand Down Expand Up @@ -95,12 +87,7 @@ represent `-typemin(Int)`, thus leading to an overflow.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_neg end

function checked_neg{T<:SignedInt}(x::T)
checked_sub(T(0), x)
end
function checked_neg{T<:UnsignedInt}(x::T)
function checked_neg{T<:Integer}(x::T)
checked_sub(T(0), x)
end
if BrokenSignedInt != Union{}
Expand Down Expand Up @@ -134,43 +121,54 @@ function checked_abs(x::SignedInt)
r
end
checked_abs(x::UnsignedInt) = x
checked_abs(x::Bool) = x

"""
Base.checked_add(x, y)

Calculates `x+y`, checking for overflow errors where applicable.

The overflow protection may impose a perceptible performance penalty.
"""
function checked_add end
Base.add_with_overflow(x, y) -> (r, f)
Calculates `r = x+y`, with the flag `f` indicating whether overflow has occurred.
"""
function add_with_overflow end
add_with_overflow{T<:SignedInt}(x::T, y::T) = checked_sadd_int(x, y)
add_with_overflow{T<:UnsignedInt}(x::T, y::T) = checked_uadd_int(x, y)
add_with_overflow(x::Bool, y::Bool) = x+y, false

function checked_add{T<:SignedInt}(x::T, y::T)
box(T, checked_sadd_int(unbox(T,x), unbox(T,y)))
end
function checked_add{T<:UnsignedInt}(x::T, y::T)
box(T, checked_uadd_int(unbox(T,x), unbox(T,y)))
end
if BrokenSignedInt != Union{}
function checked_add{T<:BrokenSignedInt}(x::T, y::T)
function add_with_overflow{T<:BrokenSignedInt}(x::T, y::T)
r = x + y
# x and y have the same sign, and the result has a different sign
(x<0) == (y<0) != (r<0) && throw(OverflowError())
r
f = (x<0) == (y<0) != (r<0)
r, f
end
end
if BrokenUnsignedInt != Union{}
function checked_add{T<:BrokenUnsignedInt}(x::T, y::T)
function add_with_overflow{T<:BrokenUnsignedInt}(x::T, y::T)
# x + y > typemax(T)
# Note: ~y == -y-1
x > ~y && throw(OverflowError())
x + y
x + y, x > ~y
end
end
checked_add(x::Bool, y::Bool) = x + y
checked_add(x::Bool) = +x


"""
Base.checked_add(x, y)
Calculates `x+y`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_add{T<:Integer}(x::T, y::T)
z, b = add_with_overflow(x, y)
b && throw(OverflowError())
z
end

# Handle multiple arguments
checked_add(x) = x
checked_add(x::Bool) = +x

checked_add{T}(x1::T, x2::T, x3::T) =
checked_add(checked_add(x1, x2), x3)
checked_add{T}(x1::T, x2::T, x3::T, x4::T) =
Expand All @@ -184,95 +182,109 @@ checked_add{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) =
checked_add{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) =
checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7, x8)

"""
Base.checked_sub(x, y)

Calculates `x-y`, checking for overflow errors where applicable.
"""
Base.sub_with_overflow(x, y) -> (r,f)
The overflow protection may impose a perceptible performance penalty.
Calculates `r = x-y`, with the flag `f` indicating whether overflow has occurred.
"""
function checked_sub end
function sub_with_overflow end
sub_with_overflow{T<:SignedInt}(x::T, y::T) = checked_ssub_int(x, y)
sub_with_overflow{T<:UnsignedInt}(x::T, y::T) = checked_usub_int(x, y)
sub_with_overflow(x::Bool, y::Bool) = x-y, false

function checked_sub{T<:SignedInt}(x::T, y::T)
box(T, checked_ssub_int(unbox(T,x), unbox(T,y)))
end
function checked_sub{T<:UnsignedInt}(x::T, y::T)
box(T, checked_usub_int(unbox(T,x), unbox(T,y)))
end
if BrokenSignedInt != Union{}
function checked_sub{T<:BrokenSignedInt}(x::T, y::T)
function sub_with_overflow{T<:BrokenSignedInt}(x::T, y::T)
r = x - y
# x and y have different signs, and the result has a different sign than x
(x<0) != (y<0) == (r<0) && throw(OverflowError())
r
f = (x<0) != (y<0) == (r<0)
r, f
end
end
if BrokenUnsignedInt != Union{}
function checked_sub{T<:BrokenUnsignedInt}(x::T, y::T)
function sub_with_overflow{T<:BrokenUnsignedInt}(x::T, y::T)
# x - y < 0
x < y && throw(OverflowError())
x - y
x - y, x < y
end
end
checked_sub(x::Bool, y::Bool) = x - y
checked_sub(x::Bool) = -x

"""
Base.checked_mul(x, y)
Base.checked_sub(x, y)
Calculates `x*y`, checking for overflow errors where applicable.
Calculates `x-y`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_mul end

function checked_mul{T<:SignedInt}(x::T, y::T)
box(T, checked_smul_int(unbox(T,x), unbox(T,y)))
end
function checked_mul{T<:UnsignedInt}(x::T, y::T)
box(T, checked_umul_int(unbox(T,x), unbox(T,y)))
function checked_sub{T<:Integer}(x::T, y::T)
z, b = sub_with_overflow(x, y)
b && throw(OverflowError())
z
end


"""
Base.mul_with_overflow(x, y) -> (r,f)
Calculates `r = x*y`, with the flag `f` indicating whether overflow has occurred.
"""
function mul_with_overflow end
mul_with_overflow{T<:SignedInt}(x::T, y::T) = checked_smul_int(x, y)
mul_with_overflow{T<:UnsignedInt}(x::T, y::T) = checked_umul_int(x, y)
mul_with_overflow(x::Bool, y::Bool) = x*y, false

if BrokenSignedIntMul != Union{} && BrokenSignedIntMul != Int128
function checked_mul{T<:BrokenSignedIntMul}(x::T, y::T)
function mul_with_overflow{T<:BrokenSignedIntMul}(x::T, y::T)
r = widemul(x, y)
r % T != r && throw(OverflowError())
r % T
f = r % T != r
r % T, f
end
end
if BrokenUnsignedIntMul != Union{} && BrokenUnsignedIntMul != UInt128
function checked_mul{T<:BrokenUnsignedIntMul}(x::T, y::T)
function mul_with_overflow{T<:BrokenUnsignedIntMul}(x::T, y::T)
r = widemul(x, y)
r % T != r && throw(OverflowError())
r % T
f = r % T != r
r % T, f
end
end
if Int128 <: BrokenSignedIntMul
# Avoid BigInt
function checked_mul{T<:Int128}(x::T, y::T)
if y > 0
function mul_with_overflow{T<:Int128}(x::T, y::T)
f = if y > 0
# x * y > typemax(T)
# x * y < typemin(T)
x > fld(typemax(T), y) && throw(OverflowError())
x < cld(typemin(T), y) && throw(OverflowError())
x > fld(typemax(T), y) || x < cld(typemin(T), y)
elseif y < 0
# x * y > typemax(T)
# x * y < typemin(T)
x < cld(typemax(T), y) && throw(OverflowError())
# y == -1 can overflow fld
y != -1 && x > fld(typemin(T), y) && throw(OverflowError())
x < cld(typemax(T), y) || y != -1 && x > fld(typemin(T), y)
else
false
end
x * y
x*y, f
end
end
if UInt128 <: BrokenUnsignedIntMul
# Avoid BigInt
function checked_mul{T<:UInt128}(x::T, y::T)
function mul_with_overflow{T<:UInt128}(x::T, y::T)
# x * y > typemax(T)
y > 0 && x > fld(typemax(T), y) && throw(OverflowError())
x * y
x * y, y > 0 && x > fld(typemax(T), y)
end
end

"""
Base.checked_mul(x, y)
Calculates `x*y`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_mul{T<:Integer}(x::T, y::T)
z, b = mul_with_overflow(x, y)
b && throw(OverflowError())
z
end

# Handle multiple arguments
checked_mul(x) = x
checked_mul{T}(x1::T, x2::T, x3::T) =
Expand All @@ -295,11 +307,8 @@ Calculates `div(x,y)`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_div end

# Base.div already checks; nothing to do here
checked_div{T<:SignedInt}(x::T, y::T) = div(x, y)
checked_div{T<:UnsignedInt}(x::T, y::T) = div(x, y)
checked_div{T<:Integer}(x::T, y::T) = div(x, y)

"""
Base.checked_rem(x, y)
Expand All @@ -308,11 +317,8 @@ Calculates `x%y`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_rem end

# Base.rem already checks; nothing to do here
checked_rem{T<:SignedInt}(x::T, y::T) = rem(x, y)
checked_rem{T<:UnsignedInt}(x::T, y::T) = rem(x, y)
checked_rem{T<:Integer}(x::T, y::T) = rem(x, y)

"""
Base.checked_fld(x, y)
Expand All @@ -321,11 +327,8 @@ Calculates `fld(x,y)`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_fld end

# Base.fld already checks; nothing to do here
checked_fld{T<:SignedInt}(x::T, y::T) = fld(x, y)
checked_fld{T<:UnsignedInt}(x::T, y::T) = fld(x, y)
checked_fld{T<:Integer}(x::T, y::T) = fld(x, y)

"""
Base.checked_mod(x, y)
Expand All @@ -334,11 +337,8 @@ Calculates `mod(x,y)`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_mod end

# Base.mod already checks; nothing to do here
checked_mod{T<:SignedInt}(x::T, y::T) = mod(x, y)
checked_mod{T<:UnsignedInt}(x::T, y::T) = mod(x, y)
checked_mod{T<:Integer}(x::T, y::T) = mod(x, y)

"""
Base.checked_cld(x, y)
Expand All @@ -347,10 +347,7 @@ Calculates `cld(x,y)`, checking for overflow errors where applicable.
The overflow protection may impose a perceptible performance penalty.
"""
function checked_cld end

# Base.cld already checks; nothing to do here
checked_cld{T<:SignedInt}(x::T, y::T) = cld(x, y)
checked_cld{T<:UnsignedInt}(x::T, y::T) = cld(x, y)
checked_cld{T<:Integer}(x::T, y::T) = cld(x, y)

end
9 changes: 9 additions & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,15 @@ add_tfunc(lt_float, 2, 2, cmp_tfunc)
add_tfunc(le_float, 2, 2, cmp_tfunc)
add_tfunc(fpiseq, 2, 2, cmp_tfunc)
add_tfunc(fpislt, 2, 2, cmp_tfunc)

chk_tfunc = (x,y) -> Tuple{widenconst(x),Bool}
add_tfunc(checked_sadd_int, 2, 2, chk_tfunc)
add_tfunc(checked_uadd_int, 2, 2, chk_tfunc)
add_tfunc(checked_ssub_int, 2, 2, chk_tfunc)
add_tfunc(checked_usub_int, 2, 2, chk_tfunc)
add_tfunc(checked_smul_int, 2, 2, chk_tfunc)
add_tfunc(checked_umul_int, 2, 2, chk_tfunc)

add_tfunc(Core.Intrinsics.ccall, 3, IInf,
function(fptr::ANY, rt::ANY, at::ANY, a...)
if !isType(rt)
Expand Down
14 changes: 5 additions & 9 deletions base/parse.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file is a part of Julia. License is MIT: http:https://julialang.org/license

import Base.Checked: add_with_overflow, mul_with_overflow

## string to integer functions ##

function parse{T<:Integer}(::Type{T}, c::Char, base::Integer=36)
Expand Down Expand Up @@ -54,11 +56,6 @@ function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos:
return sgn, base, j
end

safe_add{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? (n1 > (typemax(T) - n2)) : (n1 < (typemin(T) - n2))) ? Nullable{T}() : Nullable{T}(n1 + n2)
safe_mul{T<:Integer}(n1::T, n2::T) = ((n2 > 0) ? ((n1 > div(typemax(T),n2)) || (n1 < div(typemin(T),n2))) :
(n2 < -1) ? ((n1 > div(typemin(T),n2)) || (n1 < div(typemax(T),n2))) :
((n2 == -1) && n1 == typemin(T))) ? Nullable{T}() : Nullable{T}(n1 * n2)

function tryparse_internal{T<:Integer}(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool)
_n = Nullable{T}()
sgn, base, i = parseint_preamble(T<:Signed, Int(base_), s, startpos, endpos)
Expand Down Expand Up @@ -108,13 +105,12 @@ function tryparse_internal{T<:Integer}(::Type{T}, s::AbstractString, startpos::I
end
(T <: Signed) && (d *= sgn)

safe_n = safe_mul(n, base)
isnull(safe_n) || (safe_n = safe_add(get(safe_n), d))
if isnull(safe_n)
n, ov_mul = mul_with_overflow(n, base)
n, ov_add = add_with_overflow(n, d)
if ov_mul | ov_add
raise && throw(OverflowError())
return _n
end
n = get(safe_n)
(i > endpos) && return Nullable{T}(n)
c, i = next(s,i)
end
Expand Down
Loading

0 comments on commit 752ea51

Please sign in to comment.