/*
** 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