Files
swift-mirror/stdlib/core/FixedPoint.swift.gyb
Dmitri Hrybenko ff771d0984 stdlib: use fixed-width Builtin.IntXX types for Swift.Int and UInt
Using the unknown-sized Builtin.Word types complicates producing
compile-time overflow diagnostics.  If we don't know the target Word
size, we don't know if there is an overflow.  But SIL optimizer does not
know the size of Word, this is the point of having the Word type in the
first place.

Also, this opens up more possibilities for optimizations.

rdar://17604532

Swift SVN r24788
2015-01-28 05:22:42 +00:00

655 lines
19 KiB
Swift

//===--- FixedPoint.swift.gyb ---------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
%{
from SwiftIntTypes import *
#
# Utility code for later in this template
#
def hexify(n):
"""Return a legible hex representation of n, using '_' separators """
z = '%X' % n
l = len(z)
r = []
while z:
r.insert(0, z[-4:])
z = z[:-4]
return '0x' + '_'.join(r)
# Number of bits in the Builtin.Word type
word_bits = int(CMAKE_SIZEOF_VOID_P) * 8
# Number of bits in integer literals.
builtinIntLiteralBits = 2048
def maskBits(n):
"""Return an n-bit mask in hex"""
return hexify((1 << n) - 1)
}%
/// The largest native signed integer type
public typealias IntMax = Int${int_max_bits}
/// The largest native unsigned integer type
public typealias UIntMax = UInt${int_max_bits}
/// This protocol is an implementation detail of `IntegerType`; do
/// not use it directly.
///
/// Its requirements are inherited by `IntegerType` and thus must
/// be satisfied by types conforming to that protocol.
public protocol _IntegerType
: _BuiltinIntegerLiteralConvertible,
IntegerLiteralConvertible,
Printable,
Hashable,
IntegerArithmeticType,
BitwiseOperationsType,
_Incrementable
{
}
/// A set of common requirements for Swift's integer types.
public protocol IntegerType : _IntegerType, RandomAccessIndexType {
}
/// This protocol is an implementation detail of `SignedIntegerType`;
/// do not use it directly.
///
/// Its requirements are inherited by `SignedIntegerType` and thus
/// must be satisfied by types conforming to that protocol.
public protocol _SignedIntegerType : _IntegerType, SignedNumberType {
/// Represent this number using Swift's widest native signed integer
/// type.
func toIntMax() -> IntMax
/// Convert from Swift's widest signed integer type, trapping on
/// overflow.
init(IntMax)
}
/// A set of common requirements for Swift's signed integer types.
public protocol SignedIntegerType : _SignedIntegerType, IntegerType {
}
/// This protocol is an implementation detail of `SignedIntegerType`;
/// do not use it directly.
///
/// Its requirements are inherited by `SignedIntegerType` and thus
/// must be satisfied by types conforming to that protocol.
public protocol _UnsignedIntegerType : _IntegerType {
// Used to create a deliberate ambiguity in cases like UInt(1) +
// Int(1), which would otherwise compile due to the arithmetic
// operators defined for Strideable types (unsigned types are
// Strideable).
typealias _DisallowMixedSignArithmetic : SignedIntegerType = Int
/// Represent this number using Swift's widest native unsigned
/// integer type.
func toUIntMax() -> UIntMax
/// Convert from Swift's widest unsigned integer type, trapping on
/// overflow.
init(UIntMax)
}
/// A set of common requirements for Swift's unsigned integer types.
public protocol UnsignedIntegerType : _UnsignedIntegerType, IntegerType {
}
/// Convert `x` to type `U`, trapping on overflow in -Onone and -O
/// builds.
///
/// Typically used to do conversion to any contextually-deduced
/// integer type::
///
/// func f(x: Int32) {}
/// func g(x: Int64) { f(numericCast(x)) }
public func numericCast<
T : _SignedIntegerType, U : _SignedIntegerType
>(x: T) -> U {
return U(x.toIntMax())
}
/// Convert `x` to type `U`, trapping on overflow in -Onone and -O
/// builds.
///
/// Typically used to do conversion to any contextually-deduced
/// integer type::
///
/// func f(x: UInt32) {}
/// func g(x: UInt64) { f(numericCast(x)) }
public func numericCast<
T : _UnsignedIntegerType, U : _UnsignedIntegerType
>(x: T) -> U {
return U(x.toUIntMax())
}
/// Convert `x` to type `U`, trapping on overflow in -Onone and -O
/// builds.
///
/// Typically used to do conversion to any contextually-deduced
/// integer type::
///
/// func f(x: UInt32) {}
/// func g(x: Int64) { f(numericCast(x)) }
public func numericCast<
T : _SignedIntegerType, U : _UnsignedIntegerType
>(x: T) -> U {
return U(UIntMax(x.toIntMax()))
}
/// Convert `x` to type `U`, trapping on overflow in -Onone and -O
/// builds.
///
/// Typically used to do conversion to any contextually-deduced
/// integer type::
///
/// func f(x: Int32) {}
/// func g(x: UInt64) { f(numericCast(x)) }
public func numericCast<
T : _UnsignedIntegerType, U : _SignedIntegerType
>(x: T) -> U {
return U(IntMax(x.toUIntMax()))
}
% for self_ty in all_integer_types(word_bits):
% bits = self_ty.bits
% signed = self_ty.is_signed
% (sign, ext) = ('s', 'sext') if signed else ('u', 'zext')
% Self = self_ty.stdlib_name
% BuiltinName = self_ty.builtin_name
% OtherSelf = self_ty.get_opposite_signedness().stdlib_name
/// A ${bits}-bit ${'un' if sign == 'u' else ''}signed integer value
/// type.
public struct ${Self}
: ${'SignedIntegerType' if sign == 's' else 'UnsignedIntegerType'} {
public var value: Builtin.${BuiltinName}
// FIXME: this declaration should be inferred.
// <rdar://problem/18379938> Type checker refuses to use the default for
// Int.Distance associated type
/// A type that can represent the number of steps between pairs of
/// values.
public typealias Distance = Int
/// Create an instance initialized to zero.
@transparent public
init() {
let maxWidthZero: IntMax = 0
value = Builtin.truncOrBitCast_Int${int_max_bits}_${BuiltinName}(
maxWidthZero.value)
}
@transparent public
init(_ _v: Builtin.${BuiltinName}) {
value = _v
}
% if self_ty.is_word:
@transparent
public // @testable
init(_ v: Builtin.Word) {
% if BuiltinName == 'Int32':
value = Builtin.truncOrBitCast_Word_Int32(v)
% elif BuiltinName == 'Int64':
value = Builtin.zextOrBitCast_Word_Int64(v)
% end
}
@transparent
public // @testable
var _builtinWordValue: Builtin.Word {
% if BuiltinName == 'Int32':
return Builtin.zextOrBitCast_Int32_Word(value)
% elif BuiltinName == 'Int64':
return Builtin.truncOrBitCast_Int64_Word(value)
% end
}
% end
/// Create an instance initialized to `value`.
@transparent public
init(_ value: ${Self}) { self = value }
% if bits > 8:
/// Creates an integer from its big-endian representation, changing the
/// byte order if necessary.
@transparent public
init(bigEndian value: ${Self}) {
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64)
self = ${Self}(Builtin.int_bswap_${BuiltinName}(value.value) )
#else
_UnsupportedArchitectureError()
#endif
}
/// Creates an integer from its little-endian representation, changing the
/// byte order if necessary.
@transparent public
init(littleEndian value: ${Self}) {
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64)
self = value
#else
_UnsupportedArchitectureError()
#endif
}
% end
@transparent public
init(_builtinIntegerLiteral value: Builtin.Int${builtinIntLiteralBits}) {
self = ${Self}(Builtin.s_to_${sign}_checked_trunc_Int${builtinIntLiteralBits}_${BuiltinName}(value).0)
}
/// Create an instance initialized to `value`.
@transparent public
init(integerLiteral value: ${Self}) {
self = value
}
% if bits > 8:
/// Returns the big-endian representation of the integer, changing the
/// byte order if necessary.
public var bigEndian: ${Self} {
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64)
return ${Self}(Builtin.int_bswap_${BuiltinName}(value) )
#else
_UnsupportedArchitectureError()
#endif
}
/// Returns the little-endian representation of the integer, changing the
/// byte order if necessary.
public var littleEndian: ${Self} {
#if arch(i386) || arch(x86_64) || arch(arm) || arch(arm64)
return self
#else
_UnsupportedArchitectureError()
#endif
}
% end
% if bits > 8:
/// Returns the current integer with the byte order swapped.
public var byteSwapped: ${Self} {
return ${Self}(Builtin.int_bswap_${BuiltinName}(value))
}
% end
% max = maskBits((bits - 1) if signed else bits)
@transparent public
static var max: ${Self} { return ${max} }
@transparent public
static var min: ${Self} { return ${'-%s-1' % max if signed else '0'} }
@transparent
public static var _sizeInBits: ${Self} { return ${bits} }
}
extension ${Self} : Hashable {
/// The hash value.
///
/// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
///
/// **Note:** the hash value is not guaranteed to be stable across
/// different invocations of the same program. Do not persist the
/// hash value across program runs.
public var hashValue: Int {
% if bits <= word_bits and signed:
// Sign extend the value.
return Int(self)
% elif bits <= word_bits and not signed:
// Sign extend the value.
return Int(${OtherSelf}(bitPattern: self))
% elif bits == word_bits * 2:
// We have twice as many bits as we need to return.
return
Int(truncatingBitPattern: self) ^
Int(truncatingBitPattern: self >> 32)
% else:
_Unimplemented()
% end
}
}
extension ${Self} : Printable {
/// A textual representation of `self`.
public var description: String {
% if signed:
return _int64ToString(self.toIntMax())
% else:
return _uint64ToString(self.toUIntMax())
% end
}
}
// When ${Self} is used as an index, there are always bounds---more
// restrictive than the full range of ${Self}---against which we're
// not able to check. Overflows are not useful indicators of
// precondition violations in this context. Therefore, we use masked
// arithmetic in this conformance, and we need to be be sure that
// generic implementations of the arithmetic operators for
// RandomAccessIndexType's are all shadowed by more-specific
// implementations that *do* check for overflows.
@transparent
extension ${Self} : RandomAccessIndexType {
// HACK: Disable indexing and slicing Ranges of IntegerType types
// outside of a generic context. See the implementation of Range
// for details of how this works.
typealias _DisabledRangeIndex = ${Self}
/// Returns the next consecutive value after `self`.
///
/// Requires: the next value is representable.
@transparent public
func successor() -> ${Self} {
return self &+ 1
}
/// Returns the previous consecutive value before `self`.
///
/// Requires: the previous value is representable.
@transparent public
func predecessor() -> ${Self} {
return self &- 1
}
/// Return the minimum number of applications of `successor` or
/// `predecessor` required to reach `other` from `self`.
///
/// Complexity: O(1).
@transparent public
func distanceTo(other: ${Self}) -> ${Self}.Distance {
return numericCast((numericCast(other) as IntMax) &- numericCast(self))
}
/// Return `self` offset by `n` steps.
///
/// :returns: If `n > 0`, the result of applying `successor` to
/// `self` `n` times. If `n < 0`, the result of applying
/// `predecessor` to `self` `-n` times. Otherwise, `self`.
///
/// Complexity: O(1)
@transparent public
func advancedBy(amount: ${Self}.Distance) -> ${Self} {
return numericCast((numericCast(self) as IntMax) &+ numericCast(amount))
}
}
// Operations that return an overflow bit in addition to a partial result,
// helpful for checking for overflow when you want to handle it.
extension ${Self} {
% for Method,op in [('add', 'add'), ('subtract', 'sub'), ('multiply', 'mul')]:
/// ${Method.capitalize()} `lhs` and `rhs`, returning a result and a
/// `Bool` that is true iff the operation caused an arithmetic
/// overflow.
@transparent public
static func ${Method}WithOverflow(lhs: ${Self}, _ rhs: ${Self}) -> (${Self}, overflow: Bool) {
let tmp = Builtin.${sign}${op}_with_overflow_${BuiltinName}(lhs.value, rhs.value, false.value)
return (${Self}(tmp.0), Bool(tmp.1))
}
% end
% for Method,op in [('divide', 'div'), ('remainder', 'rem')]:
/// Divide `lhs` and `rhs`, returning
/// ${'a result' if op == 'div' else 'the remainder'} and a `Bool`
/// that is true iff the operation caused an arithmetic overflow.
@transparent public
static func ${Method}WithOverflow(lhs: ${Self}, _ rhs: ${Self}) -> (${Self}, overflow: Bool) {
if rhs == 0 {
return (0, true)
}
% if signed:
if lhs == ${Self}.min && rhs == -1 {
return (0, true)
}
% end
// FIXME: currently doesn't detect overflow -- blocked by:
// <rdar://15735295> Need [su]{div,rem}_with_overflow IR
let tmp = Builtin.${sign}${op}_${BuiltinName}(lhs.value, rhs.value)
return (${Self}(tmp), false)
}
%end
% (U, un) = ('','') if signed else ('U','un')
/// Represent this number using Swift's widest native ${un}signed
/// integer type.
@transparent public
func to${U}IntMax() -> ${U}IntMax {
return ${'self' if Self == U+'Int%s'%int_max_bits else U+'IntMax(self)'}
}
% if not signed:
/// Explicitly convert to `IntMax`${', trapping on overflow (except in -Ounchecked builds)' if bits == int_max_bits else ''}.
@transparent public
func toIntMax() -> IntMax {
return IntMax(toUIntMax())
}
% end
}
% if signed:
@transparent
extension ${Self} : SignedNumberType {}
% end
%# FIXME: checked conversions of Word types
// construction from other integer types
@transparent
extension ${Self} {
% for src_ty in all_integer_types(word_bits):
% srcBits = src_ty.bits
% srcSigned = src_ty.is_signed
% Src = src_ty.stdlib_name
% (srcSign, srcExt) = ('s', 'sext') if srcSigned else ('u', 'zext')
% if Self != Src:
public init(_ v: ${Src}) {
%
let srcNotWord = v.value
%
% if srcBits == bits and srcSign == sign:
let dstNotWord = srcNotWord
%
% elif srcBits == bits:
let tmp = Builtin.${srcSign}_to_${sign}_checked_conversion_Int${srcBits}(srcNotWord)
Builtin.condfail(tmp.1)
let dstNotWord = tmp.0
%
% elif srcBits > bits:
let tmp = Builtin.${srcSign}_to_${sign}_checked_trunc_Int${srcBits}_Int${bits}(srcNotWord)
Builtin.condfail(tmp.1)
let dstNotWord = tmp.0
%
% elif srcSigned and not signed:
let tmp = Builtin.s_to_u_checked_conversion_Int${srcBits}(srcNotWord)
Builtin.condfail(tmp.1)
let dstNotWord = Builtin.${srcExt}_Int${srcBits}_Int${bits}(tmp.0)
%
% else:
let dstNotWord = Builtin.${srcExt}_Int${srcBits}_Int${bits}(srcNotWord)
% end
%
value = dstNotWord
}
% end
% if should_define_truncating_bit_pattern_init(src_ty=src_ty, dst_ty=self_ty):
/// Construct a `${Self}` having the same bitwise representation as
/// the least significant bits of the provided bit pattern.
///
/// No range or overflow checking occurs.
@transparent
public init(truncatingBitPattern: ${Src}) {
%
let srcNotWord = truncatingBitPattern.value
%
% if self_ty.bits == src_ty.bits:
let dstNotWord = srcNotWord
% else:
let dstNotWord = Builtin.trunc_Int${srcBits}_Int${bits}(srcNotWord)
% end
%
value = dstNotWord
}
% end
% end
/// Construct a `${Self}` having the same memory representation as
/// the `${OtherSelf}` `bitPattern`. No range or overflow checking
/// occurs, and the resulting `${Self}` may not have the same numeric
/// value as `bitPattern`--it is only guaranteed to use the same
/// pattern of bits.
@transparent
public init(bitPattern: ${OtherSelf}) {
value = bitPattern.value
}
}
// Operations with potentially-static overflow checking
//
// FIXME: must use condfail in these operators, rather than
// overflowChecked, pending <rdar://problem/16271923> so that we don't
// foil static checking for numeric overflows.
% for op,method in ('+','add'), ('*','mul'), ('-','sub'):
@transparent
public func ${op} (lhs: ${Self}, rhs: ${Self}) -> ${Self} {
let (result, error) = Builtin.${sign}${method}_with_overflow_${BuiltinName}(
lhs.value, rhs.value, true.value)
// return overflowChecked((${Self}(result), Bool(error)))
Builtin.condfail(error)
return ${Self}(result)
}
% end
% for op,inst in [('/', 'div'), ('%', 'rem')]:
@transparent
public func ${op}(lhs: ${Self}, rhs: ${Self}) -> ${Self} {
Builtin.condfail((rhs == 0).value)
% if signed:
Builtin.condfail(((lhs == ${Self}.min) && (rhs == -1)).value)
% end
// FIXME: currently doesn't detect overflow -- blocked by:
// <rdar://15735295> Need [su]{div,rem}_with_overflow IR
let tmp = Builtin.${sign}${inst}_${BuiltinName}(lhs.value, rhs.value)
return ${Self}(tmp)
}
%end
// Bitwise negate
@transparent
public prefix func ~(rhs: ${Self}) -> ${Self} {
let mask = ${Self}.subtractWithOverflow(0, 1).0
return ${Self}(Builtin.xor_${BuiltinName}(rhs.value, mask.value))
}
% for op, name in (
% ('==','eq'), ('!=','ne'),
% ('<',sign+'lt'), ('<=',sign+'le'),
% ('>',sign+'gt'), ('>=',sign+'ge')):
@transparent
public func ${op} (lhs: ${Self}, rhs: ${Self}) -> Bool {
return Bool(Builtin.cmp_${name}_${BuiltinName}(lhs.value, rhs.value))
}
% end
% for op, name in (('<<','shl'), ('>>','ashr' if signed else 'lshr')):
@transparent
public func ${op} (lhs: ${Self}, rhs: ${Self}) -> ${Self} {
% if signed:
_precondition(U${Self}(rhs) < U${Self}._sizeInBits,
"shift amount is larger than type size in bits")
% else:
_precondition(rhs < ${Self}._sizeInBits,
"shift amount is larger than type size in bits")
% end
return ${Self}(Builtin.${name}_${BuiltinName}(lhs.value, rhs.value))
}
% end
% for op, name in (('&','and'), ('^','xor'), ('|','or')):
@transparent
public func ${op} (lhs: ${Self}, rhs: ${Self}) -> ${Self} {
return ${Self}(Builtin.${name}_${BuiltinName}(lhs.value, rhs.value))
}
% end
// bitwise operations
@transparent
extension ${Self} : BitwiseOperationsType {
/// The empty bitset of type ${Self}.
public static var allZeros: ${Self} { return 0 }
}
// Compound assignments
% for op in '+', '-', '*', '<<', '>>', '&', '|', '^':
@transparent
public func ${op}=(inout lhs: ${Self}, rhs: ${Self}) {
lhs = lhs ${op} rhs
}
% end
// Prefix and postfix increment and decrement.
// We already have generic versions of these, but when compiling
// -Onone we are unable to devirtualize the generic call, which
// results in worse performance than we would like for these simple
// operations (tracked by <rdar://problem/17692569>).
@transparent
public prefix func ++ (inout x: ${Self}) -> ${Self} {
x = x + 1
return x
}
@transparent
public postfix func ++ (inout x: ${Self}) -> ${Self} {
var ret = x
x = x + 1
return ret
}
@transparent
public prefix func -- (inout x: ${Self}) -> ${Self} {
x = x - 1
return x
}
@transparent
public postfix func -- (inout x: ${Self}) -> ${Self} {
var ret = x
x = x - 1
return ret
}
% if signed:
// TODO: Consider removing the underscore.
/// Returns the argument and specifies that the value is not negative.
/// It has only an effect if the argument is a load or call.
@transparent
public func _assumeNonNegative(x: ${Self}) -> ${Self} {
_sanityCheck(x >= 0)
return ${Self}(Builtin.assumeNonNegative_${BuiltinName}(x.value))
}
% end
% end # for bits in allInts
/// A signed integer type that occupies one machine word
public typealias Word = Int
/// An unsigned integer type that occupies one machine word
public typealias UWord = UInt
// ${'Local Variables'}:
// eval: (read-only-mode 1)
// End: