Skip to content

Commit

Permalink
Extend checked integer arithmetic
Browse files Browse the repository at this point in the history
Provide checked_{div, rem, fld, mod, cld} to complete the set of checked_* integer functions. Note that their regular counterparts also check for overflow, so that they currently have identical behaviour.
Rename the intrinsic sdiv, udiv, srem, urem to checked_*, since this is what they do.
Provide non-checking intrinsics (under the old, now unused names) for efficiency.
Implement mod in Julia instead of in C++ (significantly shorter). Use a more efficient algorithm based on a single div instead of on two rem.
Add tests ensuring that the new checked_* functions throw when they need to.
Add tests checking the return types of all integer arithmetic operators (regular and checked), since I was bitten during development.
Update documentation.

Note: The tests include flipsign, assuming that PR #14299 is present.
  • Loading branch information
eschnett committed Dec 7, 2015
1 parent f0bbab7 commit 93bb096
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 89 deletions.
1 change: 1 addition & 0 deletions base/boot.jl
Expand Up @@ -162,6 +162,7 @@ export
#ccall, cglobal, llvmcall, abs_float, add_float, add_int, and_int, ashr_int,
#box, bswap_int, checked_fptosi, checked_fptoui, checked_sadd,
#checked_smul, checked_ssub, checked_uadd, checked_umul, checked_usub,
#checked_sdiv, checked_srem, checked_udiv, checked_urem,
#checked_trunc_sint, checked_trunc_uint, check_top_bit,
#nan_dom_err, copysign_float, ctlz_int, ctpop_int, cttz_int,
#div_float, eq_float, eq_int, eqfsi64, eqfui64, flipsign_int, select_value,
Expand Down
115 changes: 100 additions & 15 deletions base/int.jl
Expand Up @@ -126,22 +126,37 @@ const Unsigned64Types = (UInt8,UInt16,UInt32,UInt64)
typealias Integer64 Union{Signed64Types...,Unsigned64Types...}

for T in Signed64Types
@eval div(x::$T, y::$T) = box($T,sdiv_int(unbox($T,x),unbox($T,y)))
@eval rem(x::$T, y::$T) = box($T,srem_int(unbox($T,x),unbox($T,y)))
@eval mod(x::$T, y::$T) = box($T,smod_int(unbox($T,x),unbox($T,y)))
@eval div(x::$T, y::$T) = box($T,checked_sdiv(unbox($T,x),unbox($T,y)))
@eval rem(x::$T, y::$T) = box($T,checked_srem(unbox($T,x),unbox($T,y)))
end
for T in Unsigned64Types
@eval div(x::$T, y::$T) = box($T,udiv_int(unbox($T,x),unbox($T,y)))
@eval rem(x::$T, y::$T) = box($T,urem_int(unbox($T,x),unbox($T,y)))
@eval div(x::$T, y::$T) = box($T,checked_udiv(unbox($T,x),unbox($T,y)))
@eval rem(x::$T, y::$T) = box($T,checked_urem(unbox($T,x),unbox($T,y)))
end

# x == fld(x,y)*y + mod(x,y)
mod{T<:Unsigned}(x::T, y::T) = rem(x,y)
function mod{T<:Integer}(x::T, y::T)
y == -1 && return T(0) # avoid potential overflow in fld
x - fld(x,y)*y
end

# fld(x,y) == div(x,y) - ((x>=0) != (y>=0) && rem(x,y) != 0 ? 1 : 0)
fld{T<:Unsigned}(x::T, y::T) = div(x,y)
fld{T<:Integer }(x::T, y::T) = div(x,y)-(signbit(x$y)&(rem(x,y)!=0))
function fld{T<:Integer}(x::T, y::T)
d = div(x,y)
d - (signbit(x$y) & (d*y!=x))
end

cld{T<:Unsigned}(x::T, y::T) = div(x,y)+(rem(x,y)!=0)
cld{T<:Integer }(x::T, y::T) = div(x,y)+(!signbit(x$y)&(rem(x,y)!=0))
# cld(x,y) = div(x,y) + ((x>0) == (y>0) && rem(x,y) != 0 ? 1 : 0)
function cld{T<:Unsigned}(x::T, y::T)
d = div(x,y)
d + (d*y!=x)
end
function cld{T<:Integer}(x::T, y::T)
d = div(x,y)
d + (((x>0) == (y>0)) & (d*y!=x))
end

## integer bitwise operations ##

Expand Down Expand Up @@ -508,16 +523,14 @@ else
*(x::Int128, y::Int128) = box(Int128,mul_int(unbox(Int128,x),unbox(Int128,y)))
*(x::UInt128, y::UInt128) = box(UInt128,mul_int(unbox(UInt128,x),unbox(UInt128,y)))

div(x::Int128, y::Int128) = box(Int128,sdiv_int(unbox(Int128,x),unbox(Int128,y)))
div(x::UInt128, y::UInt128) = box(UInt128,udiv_int(unbox(UInt128,x),unbox(UInt128,y)))
div(x::Int128, y::Int128) = box(Int128,checked_sdiv(unbox(Int128,x),unbox(Int128,y)))
div(x::UInt128, y::UInt128) = box(UInt128,checked_udiv(unbox(UInt128,x),unbox(UInt128,y)))

rem(x::Int128, y::Int128) = box(Int128,srem_int(unbox(Int128,x),unbox(Int128,y)))
rem(x::UInt128, y::UInt128) = box(UInt128,urem_int(unbox(UInt128,x),unbox(UInt128,y)))

mod(x::Int128, y::Int128) = box(Int128,smod_int(unbox(Int128,x),unbox(Int128,y)))
rem(x::Int128, y::Int128) = box(Int128,checked_srem(unbox(Int128,x),unbox(Int128,y)))
rem(x::UInt128, y::UInt128) = box(UInt128,checked_urem(unbox(UInt128,x),unbox(UInt128,y)))
end

## checked +, - and *
## checked +, -, *, div, rem, fld, mod

"""
Base.checked_add(x, y)
Expand Down Expand Up @@ -546,6 +559,51 @@ The overflow protection may impose a perceptible performance penalty.
"""
function checked_mul end

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

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

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

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

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

# requires int arithmetic defined, for the loops to work

for T in (Int8,Int16,Int32,Int64)#,Int128) ## FIXME: #4905
Expand Down Expand Up @@ -655,3 +713,30 @@ function checked_mul(x::UInt128, y::UInt128)
y > 0 && x > fld(typemax(UInt128), y) && throw(OverflowError())
x * y
end

# These implementations check by default
checked_div{T<:Union{IntTypes...}}(x::T, y::T) = div(x,y)
checked_rem{T<:Union{IntTypes...}}(x::T, y::T) = rem(x,y)
checked_fld{T<:Union{IntTypes...}}(x::T, y::T) = fld(x,y)
checked_mod{T<:Union{IntTypes...}}(x::T, y::T) = mod(x,y)
checked_cld{T<:Union{IntTypes...}}(x::T, y::T) = cld(x,y)

# Handle multiple arguments
checked_add(x) = x
checked_mul(x) = x
for f in (:checked_add, :checked_mul)
@eval begin
($f){T}(x1::T, x2::T, x3::T) =
($f)(($f)(x1, x2), x3)
($f){T}(x1::T, x2::T, x3::T, x4::T) =
($f)(($f)(x1, x2), x3, x4)
($f){T}(x1::T, x2::T, x3::T, x4::T, x5::T) =
($f)(($f)(x1, x2), x3, x4, x5)
($f){T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) =
($f)(($f)(x1, x2), x3, x4, x5, x6)
($f){T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) =
($f)(($f)(x1, x2), x3, x4, x5, x6, x7)
($f){T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) =
($f)(($f)(x1, x2), x3, x4, x5, x6, x7, x8)
end
end
40 changes: 40 additions & 0 deletions doc/stdlib/math.rst
Expand Up @@ -1140,6 +1140,46 @@ Mathematical Functions

The overflow protection may impose a perceptible performance penalty.

.. function:: Base.checked_div(x, y)

.. Docstring generated from Julia source
Calculates ``div(x,y)``\ , checking for overflow errors where applicable.

The overflow protection may impose a perceptible performance penalty.

.. function:: Base.checked_rem(x, y)

.. Docstring generated from Julia source
Calculates ``x%y``\ , checking for overflow errors where applicable.

The overflow protection may impose a perceptible performance penalty.

.. function:: Base.checked_fld(x, y)

.. Docstring generated from Julia source
Calculates ``fld(x,y)``\ , checking for overflow errors where applicable.

The overflow protection may impose a perceptible performance penalty.

.. function:: Base.checked_mod(x, y)

.. Docstring generated from Julia source
Calculates ``mod(x,y)``\ , checking for overflow errors where applicable.

The overflow protection may impose a perceptible performance penalty.

.. function:: Base.checked_cld(x, y)

.. Docstring generated from Julia source
Calculates ``cld(x,y)``\ , checking for overflow errors where applicable.

The overflow protection may impose a perceptible performance penalty.

.. function:: abs2(x)

.. Docstring generated from Julia source
Expand Down
40 changes: 40 additions & 0 deletions src/APInt-C.cpp
Expand Up @@ -261,6 +261,46 @@ int LLVMMul_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart
return Overflow;
}

extern "C" JL_DLLEXPORT
int LLVMDiv_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr) {
CREATE(a)
CREATE(b)
bool Overflow;
a = a.sdiv_ov(b, Overflow);
ASSIGN(r, a)
return Overflow;
}

extern "C" JL_DLLEXPORT
int LLVMDiv_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr) {
CREATE(a)
CREATE(b)
a = a.udiv(b);
ASSIGN(r, a)
// unsigned division cannot overflow
return false;
}

extern "C" JL_DLLEXPORT
int LLVMRem_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr) {
CREATE(a)
CREATE(b)
a = a.srem(b);
ASSIGN(r, a)
// signed remainder cannot overflow
return false;
}

extern "C" JL_DLLEXPORT
int LLVMRem_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr) {
CREATE(a)
CREATE(b)
a = a.urem(b);
ASSIGN(r, a)
// unsigned remainder cannot overflow
return false;
}

extern "C" JL_DLLEXPORT
void LLVMByteSwap(unsigned numbits, integerPart *pa, integerPart *pr) {
CREATE(a)
Expand Down
4 changes: 4 additions & 0 deletions src/APInt-C.h
Expand Up @@ -46,6 +46,10 @@ JL_DLLEXPORT int LLVMSub_uov(unsigned numbits, integerPart *pa, integerPart *pb,
JL_DLLEXPORT int LLVMSub_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);
JL_DLLEXPORT int LLVMMul_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);
JL_DLLEXPORT int LLVMMul_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);
JL_DLLEXPORT int LLVMDiv_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);
JL_DLLEXPORT int LLVMDiv_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);
JL_DLLEXPORT int LLVMRem_sov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);
JL_DLLEXPORT int LLVMRem_uov(unsigned numbits, integerPart *pa, integerPart *pb, integerPart *pr);

JL_DLLEXPORT unsigned LLVMCountPopulation(unsigned numbits, integerPart *pa);
JL_DLLEXPORT unsigned LLVMCountTrailingOnes(unsigned numbits, integerPart *pa);
Expand Down

0 comments on commit 93bb096

Please sign in to comment.