| /* |
| * Copyright (c) 2010-2013, 2019 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef __ARCH_ARM_INSTS_VFP_HH__ |
| #define __ARCH_ARM_INSTS_VFP_HH__ |
| |
| #include <fenv.h> |
| |
| #include <cmath> |
| |
| #include "arch/arm/insts/misc.hh" |
| #include "arch/arm/miscregs.hh" |
| |
| namespace ArmISA |
| { |
| |
| enum VfpMicroMode { |
| VfpNotAMicroop, |
| VfpMicroop, |
| VfpFirstMicroop, |
| VfpLastMicroop |
| }; |
| |
| template<class T> |
| static inline void |
| setVfpMicroFlags(VfpMicroMode mode, T &flags) |
| { |
| switch (mode) { |
| case VfpMicroop: |
| flags[StaticInst::IsMicroop] = true; |
| break; |
| case VfpFirstMicroop: |
| flags[StaticInst::IsMicroop] = |
| flags[StaticInst::IsFirstMicroop] = true; |
| break; |
| case VfpLastMicroop: |
| flags[StaticInst::IsMicroop] = |
| flags[StaticInst::IsLastMicroop] = true; |
| break; |
| case VfpNotAMicroop: |
| break; |
| } |
| if (mode == VfpMicroop || mode == VfpFirstMicroop) { |
| flags[StaticInst::IsDelayedCommit] = true; |
| } |
| } |
| |
| enum FeExceptionBit |
| { |
| FeDivByZero = FE_DIVBYZERO, |
| FeInexact = FE_INEXACT, |
| FeInvalid = FE_INVALID, |
| FeOverflow = FE_OVERFLOW, |
| FeUnderflow = FE_UNDERFLOW, |
| FeAllExceptions = FE_ALL_EXCEPT |
| }; |
| |
| enum FeRoundingMode |
| { |
| FeRoundDown = FE_DOWNWARD, |
| FeRoundNearest = FE_TONEAREST, |
| FeRoundZero = FE_TOWARDZERO, |
| FeRoundUpward = FE_UPWARD |
| }; |
| |
| enum VfpRoundingMode |
| { |
| VfpRoundNearest = 0, |
| VfpRoundUpward = 1, |
| VfpRoundDown = 2, |
| VfpRoundZero = 3, |
| VfpRoundAway = 4 |
| }; |
| |
| static inline float bitsToFp(uint64_t, float); |
| static inline double bitsToFp(uint64_t, double); |
| static inline uint32_t fpToBits(float); |
| static inline uint64_t fpToBits(double); |
| |
| template <class fpType> |
| static inline bool |
| flushToZero(fpType &op) |
| { |
| fpType junk = 0.0; |
| if (std::fpclassify(op) == FP_SUBNORMAL) { |
| uint64_t bitMask = ULL(0x1) << (sizeof(fpType) * 8 - 1); |
| op = bitsToFp(fpToBits(op) & bitMask, junk); |
| return true; |
| } |
| return false; |
| } |
| |
| template <class fpType> |
| static inline bool |
| flushToZero(fpType &op1, fpType &op2) |
| { |
| bool flush1 = flushToZero(op1); |
| bool flush2 = flushToZero(op2); |
| return flush1 || flush2; |
| } |
| |
| template <class fpType> |
| static inline void |
| vfpFlushToZero(FPSCR &fpscr, fpType &op) |
| { |
| if (fpscr.fz == 1 && flushToZero(op)) { |
| fpscr.idc = 1; |
| } |
| } |
| |
| template <class fpType> |
| static inline void |
| vfpFlushToZero(FPSCR &fpscr, fpType &op1, fpType &op2) |
| { |
| vfpFlushToZero(fpscr, op1); |
| vfpFlushToZero(fpscr, op2); |
| } |
| |
| static inline uint32_t |
| fpToBits(float fp) |
| { |
| union |
| { |
| float fp; |
| uint32_t bits; |
| } val; |
| val.fp = fp; |
| return val.bits; |
| } |
| |
| static inline uint64_t |
| fpToBits(double fp) |
| { |
| union |
| { |
| double fp; |
| uint64_t bits; |
| } val; |
| val.fp = fp; |
| return val.bits; |
| } |
| |
| static inline float |
| bitsToFp(uint64_t bits, float junk) |
| { |
| union |
| { |
| float fp; |
| uint32_t bits; |
| } val; |
| val.bits = bits; |
| return val.fp; |
| } |
| |
| static inline double |
| bitsToFp(uint64_t bits, double junk) |
| { |
| union |
| { |
| double fp; |
| uint64_t bits; |
| } val; |
| val.bits = bits; |
| return val.fp; |
| } |
| |
| template <class fpType> |
| static inline bool |
| isSnan(fpType val) |
| { |
| const bool single = (sizeof(fpType) == sizeof(float)); |
| const uint64_t qnan = |
| single ? 0x7fc00000 : ULL(0x7ff8000000000000); |
| return std::isnan(val) && ((fpToBits(val) & qnan) != qnan); |
| } |
| |
| typedef int VfpSavedState; |
| |
| VfpSavedState prepFpState(uint32_t rMode); |
| void finishVfp(FPSCR &fpscr, VfpSavedState state, bool flush, FPSCR mask = FpscrExcMask); |
| |
| template <class fpType> |
| fpType fixDest(FPSCR fpscr, fpType val, fpType op1); |
| |
| template <class fpType> |
| fpType fixDest(FPSCR fpscr, fpType val, fpType op1, fpType op2); |
| |
| template <class fpType> |
| fpType fixDivDest(FPSCR fpscr, fpType val, fpType op1, fpType op2); |
| |
| float fixFpDFpSDest(FPSCR fpscr, double val); |
| double fixFpSFpDDest(FPSCR fpscr, float val); |
| |
| uint16_t vcvtFpSFpH(FPSCR &fpscr, bool flush, bool defaultNan, |
| uint32_t rMode, bool ahp, float op); |
| uint16_t vcvtFpDFpH(FPSCR &fpscr, bool flush, bool defaultNan, |
| uint32_t rMode, bool ahp, double op); |
| |
| float vcvtFpHFpS(FPSCR &fpscr, bool defaultNan, bool ahp, uint16_t op); |
| double vcvtFpHFpD(FPSCR &fpscr, bool defaultNan, bool ahp, uint16_t op); |
| |
| static inline double |
| makeDouble(uint32_t low, uint32_t high) |
| { |
| double junk = 0.0; |
| return bitsToFp((uint64_t)low | ((uint64_t)high << 32), junk); |
| } |
| |
| static inline uint32_t |
| lowFromDouble(double val) |
| { |
| return fpToBits(val); |
| } |
| |
| static inline uint32_t |
| highFromDouble(double val) |
| { |
| return fpToBits(val) >> 32; |
| } |
| |
| static inline void |
| setFPExceptions(int exceptions) { |
| feclearexcept(FeAllExceptions); |
| feraiseexcept(exceptions); |
| } |
| |
| template <typename T> |
| uint64_t |
| vfpFpToFixed(T val, bool isSigned, uint8_t width, uint8_t imm, bool |
| useRmode = true, VfpRoundingMode roundMode = VfpRoundZero, |
| bool aarch64 = false) |
| { |
| int rmode; |
| bool roundAwayFix = false; |
| |
| if (!useRmode) { |
| rmode = fegetround(); |
| } else { |
| switch (roundMode) |
| { |
| case VfpRoundNearest: |
| rmode = FeRoundNearest; |
| break; |
| case VfpRoundUpward: |
| rmode = FeRoundUpward; |
| break; |
| case VfpRoundDown: |
| rmode = FeRoundDown; |
| break; |
| case VfpRoundZero: |
| rmode = FeRoundZero; |
| break; |
| case VfpRoundAway: |
| // There is no equivalent rounding mode, use round down and we'll |
| // fix it later |
| rmode = FeRoundDown; |
| roundAwayFix = true; |
| break; |
| default: |
| panic("Unsupported roundMode %d\n", roundMode); |
| } |
| } |
| __asm__ __volatile__("" : "=m" (rmode) : "m" (rmode)); |
| fesetround(FeRoundNearest); |
| val = val * pow(2.0, imm); |
| __asm__ __volatile__("" : "=m" (val) : "m" (val)); |
| fesetround(rmode); |
| feclearexcept(FeAllExceptions); |
| __asm__ __volatile__("" : "=m" (val) : "m" (val)); |
| T origVal = val; |
| val = rint(val); |
| __asm__ __volatile__("" : "=m" (val) : "m" (val)); |
| |
| int exceptions = fetestexcept(FeAllExceptions); |
| |
| int fpType = std::fpclassify(val); |
| if (fpType == FP_SUBNORMAL || fpType == FP_NAN) { |
| if (fpType == FP_NAN) { |
| exceptions |= FeInvalid; |
| } |
| val = 0.0; |
| } else if (origVal != val) { |
| switch (rmode) { |
| case FeRoundNearest: |
| if (origVal - val > 0.5) |
| val += 1.0; |
| else if (val - origVal > 0.5) |
| val -= 1.0; |
| break; |
| case FeRoundDown: |
| if (roundAwayFix) { |
| // The ordering on the subtraction looks a bit odd in that we |
| // don't do the obvious origVal - val, instead we do |
| // -(val - origVal). This is required to get the corruct bit |
| // exact behaviour when very close to the 0.5 threshold. |
| volatile T error = val; |
| error -= origVal; |
| error = -error; |
| if ( (error > 0.5) || |
| ((error == 0.5) && (val >= 0)) ) |
| val += 1.0; |
| } else { |
| if (origVal < val) |
| val -= 1.0; |
| } |
| break; |
| case FeRoundUpward: |
| if (origVal > val) |
| val += 1.0; |
| break; |
| } |
| exceptions |= FeInexact; |
| } |
| |
| __asm__ __volatile__("" : "=m" (val) : "m" (val)); |
| |
| if (isSigned) { |
| bool outOfRange = false; |
| int64_t result = (int64_t) val; |
| uint64_t finalVal; |
| |
| if (!aarch64) { |
| if (width == 16) { |
| finalVal = (int16_t)val; |
| } else if (width == 32) { |
| finalVal =(int32_t)val; |
| } else if (width == 64) { |
| finalVal = result; |
| } else { |
| panic("Unsupported width %d\n", width); |
| } |
| |
| // check if value is in range |
| int64_t minVal = ~mask(width-1); |
| if ((double)val < minVal) { |
| outOfRange = true; |
| finalVal = minVal; |
| } |
| int64_t maxVal = mask(width-1); |
| if ((double)val > maxVal) { |
| outOfRange = true; |
| finalVal = maxVal; |
| } |
| } else { |
| bool isNeg = val < 0; |
| finalVal = result & mask(width); |
| // If the result is supposed to be less than 64 bits check that the |
| // upper bits that got thrown away are just sign extension bits |
| if (width != 64) { |
| outOfRange = ((uint64_t) result >> (width - 1)) != |
| (isNeg ? mask(64-width+1) : 0); |
| } |
| // Check if the original floating point value doesn't matches the |
| // integer version we are also out of range. So create a saturated |
| // result. |
| if (isNeg) { |
| outOfRange |= val < result; |
| if (outOfRange) { |
| finalVal = 1LL << (width-1); |
| } |
| } else { |
| outOfRange |= val > result; |
| if (outOfRange) { |
| finalVal = mask(width-1); |
| } |
| } |
| } |
| |
| // Raise an exception if the value was out of range |
| if (outOfRange) { |
| exceptions |= FeInvalid; |
| exceptions &= ~FeInexact; |
| } |
| setFPExceptions(exceptions); |
| return finalVal; |
| } else { |
| if ((double)val < 0) { |
| exceptions |= FeInvalid; |
| exceptions &= ~FeInexact; |
| setFPExceptions(exceptions); |
| return 0; |
| } |
| |
| uint64_t result = ((uint64_t) val) & mask(width); |
| if (val > result) { |
| exceptions |= FeInvalid; |
| exceptions &= ~FeInexact; |
| setFPExceptions(exceptions); |
| return mask(width); |
| } |
| |
| setFPExceptions(exceptions); |
| return result; |
| } |
| }; |
| |
| |
| float vfpUFixedToFpS(bool flush, bool defaultNan, |
| uint64_t val, uint8_t width, uint8_t imm); |
| float vfpSFixedToFpS(bool flush, bool defaultNan, |
| int64_t val, uint8_t width, uint8_t imm); |
| |
| double vfpUFixedToFpD(bool flush, bool defaultNan, |
| uint64_t val, uint8_t width, uint8_t imm); |
| double vfpSFixedToFpD(bool flush, bool defaultNan, |
| int64_t val, uint8_t width, uint8_t imm); |
| |
| float fprSqrtEstimate(FPSCR &fpscr, float op); |
| uint32_t unsignedRSqrtEstimate(uint32_t op); |
| |
| float fpRecipEstimate(FPSCR &fpscr, float op); |
| uint32_t unsignedRecipEstimate(uint32_t op); |
| |
| FPSCR |
| fpStandardFPSCRValue(const FPSCR &fpscr); |
| |
| class VfpMacroOp : public PredMacroOp |
| { |
| public: |
| static bool |
| inScalarBank(IntRegIndex idx) |
| { |
| return (idx % 32) < 8; |
| } |
| |
| protected: |
| bool wide; |
| |
| VfpMacroOp(const char *mnem, ExtMachInst _machInst, |
| OpClass __opClass, bool _wide) : |
| PredMacroOp(mnem, _machInst, __opClass), wide(_wide) |
| {} |
| |
| IntRegIndex addStride(IntRegIndex idx, unsigned stride); |
| void nextIdxs(IntRegIndex &dest, IntRegIndex &op1, IntRegIndex &op2); |
| void nextIdxs(IntRegIndex &dest, IntRegIndex &op1); |
| void nextIdxs(IntRegIndex &dest); |
| }; |
| |
| template <typename T> |
| static inline T |
| fpAdd(T a, T b) |
| { |
| return a + b; |
| }; |
| |
| template <typename T> |
| static inline T |
| fpSub(T a, T b) |
| { |
| return a - b; |
| }; |
| |
| static inline float |
| fpAddS(float a, float b) |
| { |
| return a + b; |
| } |
| |
| static inline double |
| fpAddD(double a, double b) |
| { |
| return a + b; |
| } |
| |
| static inline float |
| fpSubS(float a, float b) |
| { |
| return a - b; |
| } |
| |
| static inline double |
| fpSubD(double a, double b) |
| { |
| return a - b; |
| } |
| |
| static inline float |
| fpDivS(float a, float b) |
| { |
| return a / b; |
| } |
| |
| static inline double |
| fpDivD(double a, double b) |
| { |
| return a / b; |
| } |
| |
| template <typename T> |
| static inline T |
| fpDiv(T a, T b) |
| { |
| return a / b; |
| }; |
| |
| template <typename T> |
| static inline T |
| fpMulX(T a, T b) |
| { |
| uint64_t opData; |
| uint32_t sign1; |
| uint32_t sign2; |
| const bool single = (sizeof(T) == sizeof(float)); |
| if (single) { |
| opData = (fpToBits(a)); |
| sign1 = opData>>31; |
| opData = (fpToBits(b)); |
| sign2 = opData>>31; |
| } else { |
| opData = (fpToBits(a)); |
| sign1 = opData>>63; |
| opData = (fpToBits(b)); |
| sign2 = opData>>63; |
| } |
| bool inf1 = (std::fpclassify(a) == FP_INFINITE); |
| bool inf2 = (std::fpclassify(b) == FP_INFINITE); |
| bool zero1 = (std::fpclassify(a) == FP_ZERO); |
| bool zero2 = (std::fpclassify(b) == FP_ZERO); |
| if ((inf1 && zero2) || (zero1 && inf2)) { |
| if (sign1 ^ sign2) |
| return (T)(-2.0); |
| else |
| return (T)(2.0); |
| } else { |
| return (a * b); |
| } |
| }; |
| |
| |
| template <typename T> |
| static inline T |
| fpMul(T a, T b) |
| { |
| return a * b; |
| }; |
| |
| static inline float |
| fpMulS(float a, float b) |
| { |
| return a * b; |
| } |
| |
| static inline double |
| fpMulD(double a, double b) |
| { |
| return a * b; |
| } |
| |
| template <typename T> |
| static inline T |
| // @todo remove this when all calls to it have been replaced with the new fplib implementation |
| fpMulAdd(T op1, T op2, T addend) |
| { |
| T result; |
| |
| if (sizeof(T) == sizeof(float)) |
| result = fmaf(op1, op2, addend); |
| else |
| result = fma(op1, op2, addend); |
| |
| // ARM doesn't generate signed nan's from this opperation, so fix up the result |
| if (std::isnan(result) && !std::isnan(op1) && |
| !std::isnan(op2) && !std::isnan(addend)) |
| { |
| uint64_t bitMask = ULL(0x1) << ((sizeof(T) * 8) - 1); |
| result = bitsToFp(fpToBits(result) & ~bitMask, op1); |
| } |
| return result; |
| } |
| |
| template <typename T> |
| static inline T |
| fpRIntX(T a, FPSCR &fpscr) |
| { |
| T rVal; |
| |
| rVal = rint(a); |
| if (rVal != a && !std::isnan(a)) |
| fpscr.ixc = 1; |
| return (rVal); |
| }; |
| |
| template <typename T> |
| static inline T |
| fpMaxNum(T a, T b) |
| { |
| const bool single = (sizeof(T) == sizeof(float)); |
| const uint64_t qnan = single ? 0x7fc00000 : ULL(0x7ff8000000000000); |
| |
| if (std::isnan(a)) |
| return ((fpToBits(a) & qnan) == qnan) ? b : a; |
| if (std::isnan(b)) |
| return ((fpToBits(b) & qnan) == qnan) ? a : b; |
| // Handle comparisons of +0 and -0. |
| if (!std::signbit(a) && std::signbit(b)) |
| return a; |
| return fmax(a, b); |
| }; |
| |
| template <typename T> |
| static inline T |
| fpMax(T a, T b) |
| { |
| if (std::isnan(a)) |
| return a; |
| if (std::isnan(b)) |
| return b; |
| return fpMaxNum<T>(a, b); |
| }; |
| |
| template <typename T> |
| static inline T |
| fpMinNum(T a, T b) |
| { |
| const bool single = (sizeof(T) == sizeof(float)); |
| const uint64_t qnan = single ? 0x7fc00000 : ULL(0x7ff8000000000000); |
| |
| if (std::isnan(a)) |
| return ((fpToBits(a) & qnan) == qnan) ? b : a; |
| if (std::isnan(b)) |
| return ((fpToBits(b) & qnan) == qnan) ? a : b; |
| // Handle comparisons of +0 and -0. |
| if (std::signbit(a) && !std::signbit(b)) |
| return a; |
| return fmin(a, b); |
| }; |
| |
| template <typename T> |
| static inline T |
| fpMin(T a, T b) |
| { |
| if (std::isnan(a)) |
| return a; |
| if (std::isnan(b)) |
| return b; |
| return fpMinNum<T>(a, b); |
| }; |
| |
| template <typename T> |
| static inline T |
| fpRSqrts(T a, T b) |
| { |
| int fpClassA = std::fpclassify(a); |
| int fpClassB = std::fpclassify(b); |
| T aXb; |
| int fpClassAxB; |
| |
| if ((fpClassA == FP_ZERO && fpClassB == FP_INFINITE) || |
| (fpClassA == FP_INFINITE && fpClassB == FP_ZERO)) { |
| return 1.5; |
| } |
| aXb = a*b; |
| fpClassAxB = std::fpclassify(aXb); |
| if (fpClassAxB == FP_SUBNORMAL) { |
| feraiseexcept(FeUnderflow); |
| return 1.5; |
| } |
| return (3.0 - (a * b)) / 2.0; |
| }; |
| |
| template <typename T> |
| static inline T |
| fpRecps(T a, T b) |
| { |
| int fpClassA = std::fpclassify(a); |
| int fpClassB = std::fpclassify(b); |
| T aXb; |
| int fpClassAxB; |
| |
| if ((fpClassA == FP_ZERO && fpClassB == FP_INFINITE) || |
| (fpClassA == FP_INFINITE && fpClassB == FP_ZERO)) { |
| return 2.0; |
| } |
| aXb = a*b; |
| fpClassAxB = std::fpclassify(aXb); |
| if (fpClassAxB == FP_SUBNORMAL) { |
| feraiseexcept(FeUnderflow); |
| return 2.0; |
| } |
| return 2.0 - (a * b); |
| }; |
| |
| |
| static inline float |
| fpRSqrtsS(float a, float b) |
| { |
| int fpClassA = std::fpclassify(a); |
| int fpClassB = std::fpclassify(b); |
| float aXb; |
| int fpClassAxB; |
| |
| if ((fpClassA == FP_ZERO && fpClassB == FP_INFINITE) || |
| (fpClassA == FP_INFINITE && fpClassB == FP_ZERO)) { |
| return 1.5; |
| } |
| aXb = a*b; |
| fpClassAxB = std::fpclassify(aXb); |
| if (fpClassAxB == FP_SUBNORMAL) { |
| feraiseexcept(FeUnderflow); |
| return 1.5; |
| } |
| return (3.0 - (a * b)) / 2.0; |
| } |
| |
| static inline float |
| fpRecpsS(float a, float b) |
| { |
| int fpClassA = std::fpclassify(a); |
| int fpClassB = std::fpclassify(b); |
| float aXb; |
| int fpClassAxB; |
| |
| if ((fpClassA == FP_ZERO && fpClassB == FP_INFINITE) || |
| (fpClassA == FP_INFINITE && fpClassB == FP_ZERO)) { |
| return 2.0; |
| } |
| aXb = a*b; |
| fpClassAxB = std::fpclassify(aXb); |
| if (fpClassAxB == FP_SUBNORMAL) { |
| feraiseexcept(FeUnderflow); |
| return 2.0; |
| } |
| return 2.0 - (a * b); |
| } |
| |
| template <typename T> |
| static inline T |
| roundNEven(T a) { |
| T val; |
| |
| val = round(a); |
| if (a - val == 0.5) { |
| if ( (((int) a) & 1) == 0 ) val += 1.0; |
| } |
| else if (a - val == -0.5) { |
| if ( (((int) a) & 1) == 0 ) val -= 1.0; |
| } |
| return val; |
| } |
| |
| |
| |
| class FpOp : public PredOp |
| { |
| protected: |
| FpOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass) : |
| PredOp(mnem, _machInst, __opClass) |
| {} |
| |
| virtual float |
| doOp(float op1, float op2) const |
| { |
| panic("Unimplemented version of doOp called.\n"); |
| } |
| |
| virtual float |
| doOp(float op1) const |
| { |
| panic("Unimplemented version of doOp called.\n"); |
| } |
| |
| virtual double |
| doOp(double op1, double op2) const |
| { |
| panic("Unimplemented version of doOp called.\n"); |
| } |
| |
| virtual double |
| doOp(double op1) const |
| { |
| panic("Unimplemented version of doOp called.\n"); |
| } |
| |
| double |
| dbl(uint32_t low, uint32_t high) const |
| { |
| double junk = 0.0; |
| return bitsToFp((uint64_t)low | ((uint64_t)high << 32), junk); |
| } |
| |
| uint32_t |
| dblLow(double val) const |
| { |
| return fpToBits(val); |
| } |
| |
| uint32_t |
| dblHi(double val) const |
| { |
| return fpToBits(val) >> 32; |
| } |
| |
| template <class fpType> |
| fpType |
| processNans(FPSCR &fpscr, bool &done, bool defaultNan, |
| fpType op1, fpType op2) const; |
| |
| template <class fpType> |
| fpType |
| ternaryOp(FPSCR &fpscr, fpType op1, fpType op2, fpType op3, |
| fpType (*func)(fpType, fpType, fpType), |
| bool flush, bool defaultNan, uint32_t rMode) const; |
| |
| template <class fpType> |
| fpType |
| binaryOp(FPSCR &fpscr, fpType op1, fpType op2, |
| fpType (*func)(fpType, fpType), |
| bool flush, bool defaultNan, uint32_t rMode) const; |
| |
| template <class fpType> |
| fpType |
| unaryOp(FPSCR &fpscr, fpType op1, |
| fpType (*func)(fpType), |
| bool flush, uint32_t rMode) const; |
| |
| void |
| advancePC(PCState &pcState) const |
| { |
| if (flags[IsLastMicroop]) { |
| pcState.uEnd(); |
| } else if (flags[IsMicroop]) { |
| pcState.uAdvance(); |
| } else { |
| pcState.advance(); |
| } |
| } |
| |
| float |
| fpSqrt (FPSCR fpscr,float x) const |
| { |
| |
| return unaryOp(fpscr,x,sqrtf,fpscr.fz,fpscr.rMode); |
| |
| } |
| |
| double |
| fpSqrt (FPSCR fpscr,double x) const |
| { |
| |
| return unaryOp(fpscr,x,sqrt,fpscr.fz,fpscr.rMode); |
| |
| } |
| }; |
| |
| class FpCondCompRegOp : public FpOp |
| { |
| protected: |
| IntRegIndex op1, op2; |
| ConditionCode condCode; |
| uint8_t defCc; |
| |
| FpCondCompRegOp(const char *mnem, ExtMachInst _machInst, |
| OpClass __opClass, IntRegIndex _op1, IntRegIndex _op2, |
| ConditionCode _condCode, uint8_t _defCc) : |
| FpOp(mnem, _machInst, __opClass), |
| op1(_op1), op2(_op2), condCode(_condCode), defCc(_defCc) |
| {} |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpCondSelOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest, op1, op2; |
| ConditionCode condCode; |
| |
| FpCondSelOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2, |
| ConditionCode _condCode) : |
| FpOp(mnem, _machInst, __opClass), |
| dest(_dest), op1(_op1), op2(_op2), condCode(_condCode) |
| {} |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegRegOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex op1; |
| |
| FpRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _op1, |
| VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegImmOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| uint64_t imm; |
| |
| FpRegImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, |
| IntRegIndex _dest, uint64_t _imm, |
| VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), dest(_dest), imm(_imm) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegRegImmOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex op1; |
| uint64_t imm; |
| |
| FpRegRegImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _op1, |
| uint64_t _imm, VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), imm(_imm) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegRegRegOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex op1; |
| IntRegIndex op2; |
| |
| FpRegRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2, |
| VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), op2(_op2) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegRegRegCondOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex op1; |
| IntRegIndex op2; |
| ConditionCode cond; |
| |
| FpRegRegRegCondOp(const char *mnem, ExtMachInst _machInst, |
| OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1, |
| IntRegIndex _op2, ConditionCode _cond, |
| VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), op2(_op2), |
| cond(_cond) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegRegRegRegOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex op1; |
| IntRegIndex op2; |
| IntRegIndex op3; |
| |
| FpRegRegRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2, |
| IntRegIndex _op3, VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), op2(_op2), |
| op3(_op3) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| class FpRegRegRegImmOp : public FpOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex op1; |
| IntRegIndex op2; |
| uint64_t imm; |
| |
| FpRegRegRegImmOp(const char *mnem, ExtMachInst _machInst, |
| OpClass __opClass, IntRegIndex _dest, |
| IntRegIndex _op1, IntRegIndex _op2, |
| uint64_t _imm, VfpMicroMode mode = VfpNotAMicroop) : |
| FpOp(mnem, _machInst, __opClass), |
| dest(_dest), op1(_op1), op2(_op2), imm(_imm) |
| { |
| setVfpMicroFlags(mode, flags); |
| } |
| |
| std::string generateDisassembly( |
| Addr pc, const Loader::SymbolTable *symtab) const override; |
| }; |
| |
| } |
| |
| #endif //__ARCH_ARM_INSTS_VFP_HH__ |