Use -d:builtinOverflow for builtin overflow checks

of recent Clang and GCC >= 5.0.

Maybe it's better that it's optional after all. GCC's code with builtin
overflow checks seems to be a bit slower actually, while Clang's is 3
times faster.
This commit is contained in:
def 2015-05-08 03:58:44 +02:00
commit fef21e9003

View file

@ -8,75 +8,6 @@
# #
# Only clang has __has_builtin (so far)
#
# TODO: This is emitted at the wrong position so we don't actually have an
# emit. Could we add this to nimbase.h instead?
{.emit: """#ifndef __has_builtin
#define __has_builtin(x) 0
#endif""".}
# Builtin compiler functions for improved performance
proc checkFunction(name: string): string =
"((__has_builtin(__builtin_" & name & "_overflow)) || __GNUC__ >= 5)"
# TODO: This is totally ugly. But we can't reliably detect this from Nim,
# especially with cross-compiling where the user may be using an older compiler
# version. Switching this on/off manually with a define seems weird as well.
when sizeof(clong) == 8:
const hasAddInt64Overflow = checkFunction("saddl")
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
const hasSubInt64Overflow = checkFunction("ssubl")
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
const hasMulInt64Overflow = checkFunction("smull")
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
elif sizeof(clonglong) == 8:
const hasAddInt64Overflow = checkFunction("saddll")
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
const hasSubInt64Overflow = checkFunction("ssubll")
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
const hasMulInt64Overflow = checkFunction("smulll")
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
when sizeof(int) == 8:
const hasAddIntOverflow = hasAddInt64Overflow
proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
addInt64Overflow(a, b, c)
const hasSubIntOverflow = hasSubInt64Overflow
proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
subInt64Overflow(a, b, c)
const hasMulIntOverflow = hasMulInt64Overflow
proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
mulInt64Overflow(a, b, c)
elif sizeof(int) == 4 and sizeof(cint) == 4:
const hasAddIntOverflow = checkFunction("sadd")
proc addIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
const hasSubIntOverflow = checkFunction("ssub")
proc subIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
const hasMulIntOverflow = checkFunction("smul")
proc mulIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
# simple integer arithmetic with overflow checking # simple integer arithmetic with overflow checking
proc raiseOverflow {.compilerproc, noinline, noreturn.} = proc raiseOverflow {.compilerproc, noinline, noreturn.} =
@ -86,49 +17,71 @@ proc raiseOverflow {.compilerproc, noinline, noreturn.} =
proc raiseDivByZero {.compilerproc, noinline, noreturn.} = proc raiseDivByZero {.compilerproc, noinline, noreturn.} =
sysFatal(DivByZeroError, "division by zero") sysFatal(DivByZeroError, "division by zero")
when defined(builtinOverflow):
# Builtin compiler functions for improved performance
when sizeof(clong) == 8:
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
elif sizeof(clonglong) == 8:
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
when sizeof(int) == 8:
proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
addInt64Overflow(a, b, c)
proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
subInt64Overflow(a, b, c)
proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
mulInt64Overflow(a, b, c)
elif sizeof(int) == 4 and sizeof(cint) == 4:
proc addIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
proc subIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
proc mulIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} = proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
{.emit: "#if `hasAddInt64Overflow`".}
if addInt64Overflow(a, b, result): if addInt64Overflow(a, b, result):
raiseOverflow() raiseOverflow()
{.emit: "#else".}
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
if subInt64Overflow(a, b, result):
raiseOverflow()
proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} =
if mulInt64Overflow(a, b, result):
raiseOverflow()
else:
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
result = a +% b result = a +% b
if (result xor a) >= int64(0) or (result xor b) >= int64(0): if (result xor a) >= int64(0) or (result xor b) >= int64(0):
return result return result
raiseOverflow() raiseOverflow()
{.emit: "#endif".}
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} = proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
{.emit: "#if `hasSubInt64Overflow`".}
if subInt64Overflow(a, b, result):
raiseOverflow()
{.emit: "#else".}
result = a -% b result = a -% b
if (result xor a) >= int64(0) or (result xor not b) >= int64(0): if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
return result return result
raiseOverflow() raiseOverflow()
{.emit: "#endif".}
proc negInt64(a: int64): int64 {.compilerProc, inline.} =
if a != low(int64): return -a
raiseOverflow()
proc absInt64(a: int64): int64 {.compilerProc, inline.} =
if a != low(int64):
if a >= 0: return a
else: return -a
raiseOverflow()
proc divInt64(a, b: int64): int64 {.compilerProc, inline.} =
if b == int64(0):
raiseDivByZero()
if a == low(int64) and b == int64(-1):
raiseOverflow()
return a div b
proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
if b == int64(0):
raiseDivByZero()
return a mod b
# #
# This code has been inspired by Python's source code. # This code has been inspired by Python's source code.
@ -151,10 +104,6 @@ proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
# product that must have overflowed. # product that must have overflowed.
# #
proc mulInt64(a, b: int64): int64 {.compilerproc.} = proc mulInt64(a, b: int64): int64 {.compilerproc.} =
{.emit: "#if `hasMulInt64Overflow`".}
if mulInt64Overflow(a, b, result):
raiseOverflow()
{.emit: "#else".}
var var
resAsFloat, floatProd: float64 resAsFloat, floatProd: float64
result = a *% b result = a *% b
@ -176,8 +125,28 @@ proc mulInt64(a, b: int64): int64 {.compilerproc.} =
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
return result return result
raiseOverflow() raiseOverflow()
{.emit: "#endif".}
proc negInt64(a: int64): int64 {.compilerProc, inline.} =
if a != low(int64): return -a
raiseOverflow()
proc absInt64(a: int64): int64 {.compilerProc, inline.} =
if a != low(int64):
if a >= 0: return a
else: return -a
raiseOverflow()
proc divInt64(a, b: int64): int64 {.compilerProc, inline.} =
if b == int64(0):
raiseDivByZero()
if a == low(int64) and b == int64(-1):
raiseOverflow()
return a div b
proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
if b == int64(0):
raiseDivByZero()
return a mod b
proc absInt(a: int): int {.compilerProc, inline.} = proc absInt(a: int): int {.compilerProc, inline.} =
if a != low(int): if a != low(int):
@ -330,30 +299,35 @@ elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
:"%edx" :"%edx"
""" """
when not declared(addInt) and defined(builtinOverflow):
proc addInt(a, b: int): int {.compilerProc, inline.} =
if addIntOverflow(a, b, result):
raiseOverflow()
when not declared(subInt) and defined(builtinOverflow):
proc subInt(a, b: int): int {.compilerProc, inline.} =
if subIntOverflow(a, b, result):
raiseOverflow()
when not declared(mulInt) and defined(builtinOverflow):
proc mulInt(a, b: int): int {.compilerProc, inline.} =
if mulIntOverflow(a, b, result):
raiseOverflow()
# Platform independent versions of the above (slower!) # Platform independent versions of the above (slower!)
when not declared(addInt): when not declared(addInt):
proc addInt(a, b: int): int {.compilerProc, inline.} = proc addInt(a, b: int): int {.compilerProc, inline.} =
{.emit: "#if `hasAddIntOverflow`".}
if addIntOverflow(a, b, result):
raiseOverflow()
{.emit: "#else".}
result = a +% b result = a +% b
if (result xor a) >= 0 or (result xor b) >= 0: if (result xor a) >= 0 or (result xor b) >= 0:
return result return result
raiseOverflow() raiseOverflow()
{.emit: "#endif".}
when not declared(subInt): when not declared(subInt):
proc subInt(a, b: int): int {.compilerProc, inline.} = proc subInt(a, b: int): int {.compilerProc, inline.} =
{.emit: "#if `hasSubIntOverflow`".}
if subIntOverflow(a, b, result):
raiseOverflow()
{.emit: "#else".}
result = a -% b result = a -% b
if (result xor a) >= 0 or (result xor not b) >= 0: if (result xor a) >= 0 or (result xor not b) >= 0:
return result return result
raiseOverflow() raiseOverflow()
{.emit: "#endif".}
when not declared(negInt): when not declared(negInt):
proc negInt(a: int): int {.compilerProc, inline.} = proc negInt(a: int): int {.compilerProc, inline.} =
@ -396,10 +370,6 @@ when not declared(mulInt):
# native int product that must have overflowed. # native int product that must have overflowed.
# #
proc mulInt(a, b: int): int {.compilerProc.} = proc mulInt(a, b: int): int {.compilerProc.} =
{.emit: "#if `hasMulIntOverflow`".}
if mulIntOverflow(a, b, result):
raiseOverflow()
{.emit: "#else".}
var var
resAsFloat, floatProd: float resAsFloat, floatProd: float
@ -421,7 +391,6 @@ when not declared(mulInt):
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
return result return result
raiseOverflow() raiseOverflow()
{.emit: "#endif".}
# We avoid setting the FPU control word here for compatibility with libraries # We avoid setting the FPU control word here for compatibility with libraries
# written in other languages. # written in other languages.