/* ** Copyright (C) 2018 Martin Brain ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /* ** simpleExecutable.h ** ** Martin Brain ** martin.brain@cs.ox.ac.uk ** 03/06/14 ** ** The most simple executable implementation of bit-vectors. ** Limited in the ranges it supports but fast and suitable for reasoning. ** ** Unless otherwise stated, bit operations require all operands to have the ** same widths and the result will have the same width (SMT-LIB style, ** but see the 'expanding' instructions below). Also underflow and ** overflow are considered errors (unlike SMT-LIB but see the 'modular' instructions ** below) although error checking is not perfect as overflows in the ** underlying data type can mask errors. ** */ #include "symfpu/utils/properties.h" #include "symfpu/core/ite.h" #include "symfpu/baseTypes/shared.h" #include #include #include #ifndef SYMFPU_SIMPLE_EXECUTABLE #define SYMFPU_SIMPLE_EXECUTABLE namespace symfpu { namespace simpleExecutable { typedef symfpu::shared::bitWidthType bitWidthType; typedef symfpu::shared::executable_proposition proposition; typedef symfpu::shared::floatingPointTypeInfo floatingPointTypeInfo; // Forwards definitions class roundingMode; template class bitVector; // This is the class that is used as a template argument class traits { public : typedef bitWidthType bwt; typedef roundingMode rm; typedef floatingPointTypeInfo fpt; typedef proposition prop; typedef bitVector< int64_t> sbv; typedef bitVector ubv; static roundingMode RNE(void); static roundingMode RNA(void); static roundingMode RTP(void); static roundingMode RTN(void); static roundingMode RTZ(void); // As prop == bool only one set of these is needed inline static void precondition(const bool b) { assert(b); return; } inline static void postcondition(const bool b) { assert(b); return; } inline static void invariant(const bool b) { assert(b); return; } }; // To simplify the property macros typedef traits t; class roundingMode { private : int value; public : roundingMode (int v) : value(v) {} roundingMode (const roundingMode &old) : value(old.value) {} roundingMode & operator = (const roundingMode &op) { this->value = op.value; return (*this); } proposition operator == (const roundingMode &op) const { return proposition(this->value == op.value); } // Only for executable back-ends int getValue (void) const { return this->value; } }; template struct modifySignedness; template <> struct modifySignedness< int64_t> { typedef uint64_t unsignedVersion; typedef int64_t signedVersion; }; template <> struct modifySignedness { typedef uint64_t unsignedVersion; typedef int64_t signedVersion; }; template class bitVector { protected : bitWidthType width; T value; static bitWidthType maxWidth (void) { return sizeof(T)*CHAR_BIT; } static T nOnes (bitWidthType n) { if (n == 0) { return 0; } else { // Not (1 << n) - 1 to avoid overflow for n = maxWidth() bitWidthType shift = bitVector::maxWidth() - n; return ((~0ULL) << shift) >> shift; } } // Bit vectors should store values in 2's complement using the full width // (and thus may have significant bits outside the width). // Thus need to make sure the value stored is representable within // bit-vectors of the specified width. static bool isRepresentable (const bitWidthType w, const T v); // Modular operations need to reduce operations back to // something representable. static T makeRepresentable (const bitWidthType w, const T v); public : // Ideally should protect but is used in subnormal rounding bitVector (const bitWidthType w, const T v) : width(w), value(v) { PRECONDITION(width <= bitVector::maxWidth()); PRECONDITION(0 < width); PRECONDITION(bitVector::isRepresentable(w,v)); } bitVector (const proposition &p) : width(1), value(p) {} bitVector (const bitVector &old) : width(old.width), value(old.value) {} // Constructors // non-det on other instances but not this so that // instantiation catches this bitWidthType getWidth (void) const { return this->width; } // Would it be better to not have this and only have copy? bitVector & operator= (const bitVector &op) { PRECONDITION(op.width == this->width); this->value = op.value; return (*this); } /*** Constant creation and test ***/ static bitVector one (const bitWidthType &w) { return bitVector(w,1); } static bitVector zero (const bitWidthType &w) { return bitVector(w,0); } static bitVector allOnes (const bitWidthType &w) { return bitVector(w,bitVector::nOnes(w)); } inline proposition isAllOnes() const {return proposition(((~this->value) & nOnes(this->width)) == 0);} inline proposition isAllZeros() const {return proposition(this->value == 0);} static bitVector maxValue (const bitWidthType &w); static bitVector minValue (const bitWidthType &w); /*** Operators ***/ // Need to inline the operations where possible inline bitVector operator << (const bitVector &op) const { PRECONDITION(this->width == op.width); PRECONDITION(op.value >= 0U && op.value < (T)this->width); return bitVector(this->width, this->value << op.value); } inline bitVector operator >> (const bitVector &op) const { PRECONDITION(this->width == op.width); PRECONDITION(op.value >= 0U && op.value < (T)this->width); return bitVector(this->width, this->value >> op.value); } inline bitVector operator | (const bitVector &op) const { PRECONDITION(this->width == op.width); return bitVector(this->width, this->value | op.value); } inline bitVector operator & (const bitVector &op) const { PRECONDITION(this->width == op.width); return bitVector(this->width, this->value & op.value); } inline bitVector operator + (const bitVector &op) const { PRECONDITION(this->width == op.width); return bitVector(this->width, this->value + op.value); } inline bitVector operator - (const bitVector &op) const { PRECONDITION(this->width == op.width); return bitVector(this->width, this->value - op.value); } inline bitVector operator * (const bitVector &op) const { PRECONDITION(this->width == op.width); return bitVector(this->width, this->value * op.value); } inline bitVector operator / (const bitVector &op) const { PRECONDITION(op.value != 0); PRECONDITION(this->width == op.width); return bitVector(this->width, this->value / op.value); } inline bitVector operator % (const bitVector &op) const { PRECONDITION(op.value != 0); PRECONDITION(this->width == op.width); return bitVector(this->width, this->value % op.value); } bitVector operator - (void) const; bitVector operator ~ (void) const; inline bitVector increment () const { return bitVector(this->width, this->value + 1); } inline bitVector decrement () const { return bitVector(this->width, this->value - 1); } bitVector signExtendRightShift (const bitVector &op) const; /*** Modular operations ***/ bitVector modularLeftShift (const bitVector &op) const; bitVector modularRightShift (const bitVector &op) const; inline bitVector modularIncrement () const { return bitVector(this->width, bitVector::makeRepresentable(this->width, this->value + 1)); } inline bitVector modularDecrement () const { return bitVector(this->width, bitVector::makeRepresentable(this->width, this->value - 1)); } inline bitVector modularAdd (const bitVector &op) const { return bitVector(this->width, bitVector::makeRepresentable(this->width, this->value + op.value)); } bitVector modularNegate () const; /*** Comparisons ***/ inline proposition operator == (const bitVector &op) const { PRECONDITION(this->width == op.width); return proposition(this->value == op.value); } inline proposition operator <= (const bitVector &op) const { PRECONDITION(this->width == op.width); return proposition(this->value <= op.value); } inline proposition operator >= (const bitVector &op) const { PRECONDITION(this->width == op.width); return proposition(this->value >= op.value); } inline proposition operator < (const bitVector &op) const { PRECONDITION(this->width == op.width); return proposition(this->value < op.value); } inline proposition operator > (const bitVector &op) const { PRECONDITION(this->width == op.width); return proposition(this->value > op.value); } /*** Type conversion ***/ bitVector::signedVersion> toSigned (void) const; bitVector::unsignedVersion> toUnsigned (void) const; /*** Bit hacks ***/ inline bitVector extend (bitWidthType extension) const { PRECONDITION(this->width + extension <= bitVector::maxWidth()); // No extension needed, even in the signed case as already correctly represented return bitVector(this->width + extension, this->value); } inline bitVector contract (bitWidthType reduction) const { PRECONDITION(this->width > reduction); return bitVector(this->width - reduction, this->value); } inline bitVector resize (bitWidthType newSize) const { return bitVector(newSize, bitVector::makeRepresentable(newSize, this->value)); } inline bitVector matchWidth (const bitVector &op) const { PRECONDITION(this->width <= op.width); return this->extend(op.width - this->width); } bitVector append(const bitVector &op) const; // Inclusive of end points, thus if the same, extracts just one bit bitVector extract(bitWidthType upper, bitWidthType lower) const; // Only meaningful for executable implementations T contents (void) const { return this->value; } }; } #define SEITEDFN(T) template <> \ struct ite { \ static const T & iteOp (const simpleExecutable::traits::prop &cond, \ const T &l, \ const T &r) { \ if (cond) { \ return l; \ } else { \ return r; \ } \ } \ } SEITEDFN(simpleExecutable::traits::rm); SEITEDFN(simpleExecutable::traits::prop); #undef SEITEDFN #define SEITEDFNW(T) template <> \ struct ite { \ static const T & iteOp (const simpleExecutable::traits::prop &cond, \ const T &l, \ const T &r) { \ assert(l.getWidth() == r.getWidth()); \ \ if (cond) { \ return l; \ } else { \ return r; \ } \ } \ } SEITEDFNW(simpleExecutable::traits::sbv); SEITEDFNW(simpleExecutable::traits::ubv); #undef SEITEDFNW } // For testing only; bitwise implementation is way slower for software #if 0 #include "../core/operations.h" namespace symfpu { template <> simpleExecutable::traits::ubv orderEncode (const simpleExecutable::traits::ubv &b); } #endif #endif