Skip to content

Commit

Permalink
Separate safe and unsafe math operations
Browse files Browse the repository at this point in the history
Mathematic and logical operations are now fully defined by default for
every input. This change includes:

- Numeric conversions from/to floating point saturate if the value
  doesn't fit in the destination type
- Signed integer division returns 0 on overflow (e.g. I8(-128) / -1)
- Left shift by more bits than the type width returns 0
- Logical (unsigned) right shift by more bits than the type width
  returns 0
- Arithmetic (signed) right shift by more bits than the type width
  extends the sign bit on the low bits
- Left and right bit rotate work correctly for amounts greater than the
  type width
- Signed rotate is removed
- Shift and rotate now take an unsigned amount as argument
- popcount, clz, ctz and bitwidth now always have an unsigned return
  type

Add unsafe operations that can have undefined results for some inputs
but can be faster and allow better optimisations. Unsafe functions are
suffixed with _unsafe. This change includes:

- Unsafe numeric conversions. If the value doesn't fit in the
  destination type, the result is undefined
- Unsafe addition, substraction, multiplication and negation. For
  integers, the result is undefined on overflow. For floating points,
  fast math semantics are enabled
- Unsafe division and modulus. For integers, the result is undefined
  on division/modulus by 0. For floating points, fast math semantics
  are enabled
- Unsafe left shift. On overflow, the result is undefined
- Unsafe right shift. If non-zero bits are shifted out, the result is
  undefined
- Unsafe floating point comparisons. Fast math semantics are enabled
- Unsafe square root. If the operand is negative, the result is
  undefined
- Unsafe clz/ctz. If the operand is 0, the result is undefined

Fast math semantics mean that the result is undefined if the
computation involves NaN and/or +/- infinity. In addition, full
compliance to IEEE-754 isn't required.

For reference, the semantics of undefined results and of the associated
as-if rule are defined as follows.

As-if rule

The compiler is permitted to perform any changes to the program as long
as the following remains true for every behaviour in the program.

- At the end of a behaviour, the associated actor is exactly in the
  state it would be if the program was executed as written
- If an object has a finaliser, that finaliser will eventually be
  called exactly once
- Messages are sent as if the behaviour was executed as written, in the
  same order and with the same contents
- FFI calls to well-defined C functions are executed as written, in the
  same order and with the same arguments. The runtime functions (every
  function prefixed with pony_) are free from this rule and from the
  following one
- Message sends and FFI calls are not reordered with respect to each
  other
- Capability security (both object capability and reference capability)
  is maintained

Undefined results

If an expression with undefined results is evaluated:

- Every subsequent expression depending on udefined result also has
  undefined results
- If the constructor of an object with a finaliser has undefined
  results, that finaliser isn't guaranteed to be called
- If a message send or a FFI call has undefined results, it is free
  from the as-if rule
- The implementation is allowed to generate platform-specific
  exceptions (which are not Pony exceptions)
- Capability security is always maintained
  • Loading branch information
Benoit Vey committed Jan 5, 2017
1 parent 269c3ac commit fa5dc34
Show file tree
Hide file tree
Showing 23 changed files with 1,828 additions and 223 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ All notable changes to the Pony compiler and standard library will be documented
### Added

- AST annotations (RFC 27) (PR #1485)
- Unsafe mathematic and logic operations. Can be faster but can have undefined results for some inputs (issue #993)

### Changed

- Methods returning their receiver to allow call chaining have been changed to return either None or some useful value. Generalised method chaining implemented in version 0.9.0 should be used as a replacement. The full list of updated methods follows. No details means that the method now returns None.
Expand Down Expand Up @@ -145,6 +147,8 @@ All notable changes to the Pony compiler and standard library will be documented
- TCP sockets on Linux now use Epoll One Shot
- Non-sendable locals and parameters are now seen as `tag` inside of recover expressions instead of being inaccessible.
- TCP sockets on FreeBSD and MacOSX now use Kqueue one shot
- All arithmetic and logic operations are now fully defined for every input by default (issue #993)
- Removed compiler flag `--ieee-math`

## [0.10.0] - 2016-12-12

Expand Down
58 changes: 57 additions & 1 deletion packages/builtin/float.pony
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ primitive F32 is FloatingPoint[F32]
new e() => 2.71828182845904523536

new _nan() => compile_intrinsic
new _inf(negative: Bool) => compile_intrinsic

new from_bits(i: U32) => compile_intrinsic
fun bits(): U32 => compile_intrinsic
Expand Down Expand Up @@ -138,6 +139,13 @@ primitive F32 is FloatingPoint[F32]
@"llvm.sqrt.f32"[F32](this)
end

fun sqrt_unsafe(): F32 =>
"""
Unsafe operation.
If this is negative, the result is undefined.
"""
@"llvm.sqrt.f32"[F32](this)

fun cbrt(): F32 => @cbrtf[F32](this)
fun exp(): F32 => @"llvm.exp.f32"[F32](this)
fun exp2(): F32 => @"llvm.exp2.f32"[F32](this)
Expand Down Expand Up @@ -166,12 +174,27 @@ primitive F32 is FloatingPoint[F32]
fun i128(): I128 => f64().i128()
fun u128(): U128 => f64().u128()

fun i128_unsafe(): I128 =>
"""
Unsafe operation.
If the value doesn't fit in the destination type, the result is undefined.
"""
f64_unsafe().i128_unsafe()

fun u128_unsafe(): U128 =>
"""
Unsafe operation.
If the value doesn't fit in the destination type, the result is undefined.
"""
f64_unsafe().u128_unsafe()

primitive F64 is FloatingPoint[F64]
new create(value: F64 = 0) => value
new pi() => 3.14159265358979323846
new e() => 2.71828182845904523536

new _nan() => compile_intrinsic
new _inf(negative: Bool) => compile_intrinsic

new from_bits(i: U64) => compile_intrinsic
fun bits(): U64 => compile_intrinsic
Expand Down Expand Up @@ -306,6 +329,13 @@ primitive F64 is FloatingPoint[F64]
@"llvm.sqrt.f64"[F64](this)
end

fun sqrt_unsafe(): F64 =>
"""
Unsafe operation.
If this is negative, the result is undefined.
"""
@"llvm.sqrt.f64"[F64](this)

fun cbrt(): F64 => @cbrt[F64](this)
fun exp(): F64 => @"llvm.exp.f64"[F64](this)
fun exp2(): F64 => @"llvm.exp2.f64"[F64](this)
Expand All @@ -332,6 +362,12 @@ primitive F64 is FloatingPoint[F64]
fun hash(): U64 => bits().hash()

fun i128(): I128 =>
if this > I128.max_value().f64() then
return I128.max_value()
elseif this < I128.min_value().f64() then
return I128.min_value()
end

let bit = bits()
let high = (bit >> 32).u32()
let ex = ((high and 0x7FF00000) >> 20) - 1023
Expand All @@ -342,7 +378,7 @@ primitive F64 is FloatingPoint[F64]

let s = ((high and 0x80000000) >> 31).i128()
var r = ((bit and 0x000FFFFFFFFFFFFF) or 0x0010000000000000).i128()
let ex' = ex.i128()
let ex' = ex.u128()

if ex' > 52 then
r = r << (ex' - 52)
Expand All @@ -353,6 +389,12 @@ primitive F64 is FloatingPoint[F64]
(r xor s) - s

fun u128(): U128 =>
if this > U128.max_value().f64() then
return U128.max_value()
elseif this < U128.min_value().f64() then
return U128.min_value()
end

let bit = bits()
let high = (bit >> 32).u32()
let ex = ((high and 0x7FF00000) >> 20) - 1023
Expand All @@ -372,4 +414,18 @@ primitive F64 is FloatingPoint[F64]

r.u128()

fun i128_unsafe(): I128 =>
"""
Unsafe operation.
If the value doesn't fit in the destination type, the result is undefined.
"""
i128()

fun u128_unsafe(): U128 =>
"""
Unsafe operation.
If the value doesn't fit in the destination type, the result is undefined.
"""
u128()

type Float is (F32 | F64)
Loading

0 comments on commit fa5dc34

Please sign in to comment.