| // Copyright (c) 2007 The Hewlett-Packard Development Company |
| // Copyright (c) 2012-2013 Mark D. Hill and David A. Wood |
| // Copyright (c) 2015 Advanced Micro Devices, Inc. |
| // |
| // 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. |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // |
| // FpOp Microop templates |
| // |
| ////////////////////////////////////////////////////////////////////////// |
| |
| def template MicroFpOpExecute {{ |
| Fault |
| %(class_name)s::execute(ExecContext *xc, |
| Trace::InstRecord *traceData) const |
| { |
| Fault fault = NoFault; |
| |
| DPRINTF(X86, "The data size is %d\n", dataSize); |
| %(op_decl)s; |
| %(op_rd)s; |
| |
| if (%(cond_check)s) { |
| %(code)s; |
| %(flag_code)s; |
| %(tag_code)s; |
| %(top_code)s; |
| } else { |
| %(else_code)s; |
| } |
| |
| //Write the resulting state to the execution context |
| if (fault == NoFault) { |
| %(op_wb)s; |
| } |
| return fault; |
| } |
| }}; |
| |
| def template MicroFpOpDeclare {{ |
| class %(class_name)s : public %(base_class)s |
| { |
| private: |
| %(reg_idx_arr_decl)s; |
| |
| public: |
| template <typename ...Args> |
| %(class_name)s(ExtMachInst mach_inst, const char *inst_mnem, |
| uint64_t set_flags, uint8_t data_size, int8_t _spm, |
| Args... args); |
| |
| Fault execute(ExecContext *, Trace::InstRecord *) const override; |
| }; |
| }}; |
| |
| def template MicroFpOpConstructor {{ |
| template <typename ...Args> |
| %(class_name)s::%(class_name)s(ExtMachInst mach_inst, |
| const char *inst_mnem, uint64_t set_flags, |
| uint8_t data_size, int8_t _spm, Args... args) : |
| %(base_class)s(mach_inst, "%(mnemonic)s", inst_mnem, set_flags, |
| %(op_class)s, { args... }, data_size, _spm) |
| { |
| %(set_reg_idx_arr)s; |
| %(constructor)s; |
| } |
| }}; |
| |
| let {{ |
| |
| # Make these empty strings so that concatenating onto |
| # them will always work. |
| header_output = "" |
| decoder_output = "" |
| exec_output = "" |
| |
| class FpOpMeta(type): |
| def buildCppClasses(self, name, Name, suffix, code, flag_code, |
| cond_check, else_code, op_class, operand_types): |
| |
| # Globals to stick the output in |
| global header_output |
| global decoder_output |
| global exec_output |
| |
| # Stick all the code together so it can be searched at once |
| allCode = "|".join((code, flag_code, cond_check, else_code)) |
| |
| # If there's something optional to do with flags, generate |
| # a version without it and fix up this version to use it. |
| if flag_code != "" or cond_check != "true": |
| self.buildCppClasses(name, Name, suffix, |
| code, "", "true", else_code, op_class, operand_types) |
| suffix = "Flags" + suffix |
| |
| base = "X86ISA::InstOperands<" + \ |
| ", ".join(["X86ISA::FpOp"] + |
| [op.cxx_class() for op in operand_types]) + ">" |
| |
| # Get everything ready for the substitution |
| iop_tag = InstObjParams(name, Name + suffix + "TopTag", base, |
| {"code" : code, |
| "flag_code" : flag_code, |
| "cond_check" : cond_check, |
| "else_code" : else_code, |
| "tag_code" : "FTW = genX87Tags(FTW, TOP, spm);", |
| "top_code" : "TOP = (TOP + spm + 8) % 8;", |
| "op_class" : op_class}) |
| iop_top = InstObjParams(name, Name + suffix + "Top", base, |
| {"code" : code, |
| "flag_code" : flag_code, |
| "cond_check" : cond_check, |
| "else_code" : else_code, |
| "tag_code" : ";", |
| "top_code" : "TOP = (TOP + spm + 8) % 8;", |
| "op_class" : op_class}) |
| iop = InstObjParams(name, Name + suffix, base, |
| {"code" : code, |
| "flag_code" : flag_code, |
| "cond_check" : cond_check, |
| "else_code" : else_code, |
| "tag_code" : ";", |
| "top_code" : ";", |
| "op_class" : op_class}) |
| |
| # Generate the actual code (finally!) |
| header_output += MicroFpOpDeclare.subst(iop_tag) |
| decoder_output += MicroFpOpConstructor.subst(iop_tag) |
| exec_output += MicroFpOpExecute.subst(iop_tag) |
| header_output += MicroFpOpDeclare.subst(iop_top) |
| decoder_output += MicroFpOpConstructor.subst(iop_top) |
| exec_output += MicroFpOpExecute.subst(iop_top) |
| header_output += MicroFpOpDeclare.subst(iop) |
| decoder_output += MicroFpOpConstructor.subst(iop) |
| exec_output += MicroFpOpExecute.subst(iop) |
| |
| |
| def __new__(mcls, Name, bases, dict): |
| abstract = False |
| name = Name.lower() |
| if "abstract" in dict: |
| abstract = dict['abstract'] |
| del dict['abstract'] |
| |
| cls = super().__new__(mcls, Name, bases, dict) |
| if not abstract: |
| cls.className = Name |
| cls.mnemonic = name |
| code = cls.code |
| flag_code = cls.flag_code |
| cond_check = cls.cond_check |
| else_code = cls.else_code |
| op_class = cls.op_class |
| operand_types = cls.operand_types |
| |
| # Set up the C++ classes |
| mcls.buildCppClasses(cls, name, Name, "", |
| code, flag_code, cond_check, else_code, op_class, |
| operand_types) |
| |
| # Hook into the microassembler dict |
| global microopClasses |
| microopClasses[name] = cls |
| |
| return cls |
| |
| class FpOp(X86Microop, metaclass=FpOpMeta): |
| # This class itself doesn't act as a microop |
| abstract = True |
| |
| # Default template parameter values |
| flag_code = "" |
| cond_check = "true" |
| else_code = ";" |
| op_class = "FloatAddOp" |
| |
| def __init__(self, *ops, spm=0, |
| SetStatus=False, UpdateFTW=True, dataSize="env.dataSize"): |
| self.ops = list(map(str, ops)) |
| self.spm = spm |
| self.dataSize = dataSize |
| if SetStatus: |
| self.className += "Flags" |
| if spm: |
| self.className += "Top" |
| if spm and UpdateFTW: |
| self.className += "Tag" |
| |
| def getAllocator(self, microFlags): |
| # Ensure there's at least one element to join with ","s. |
| op_iter = iter(self.ops) |
| ops = list([Type(op_iter).ctor_args() for |
| Type in self.operand_types]) |
| spm_ops = [str(self.spm)] + ops |
| return '''new %(class_name)s(machInst, macrocodeBlock, |
| %(flags)s, %(dataSize)s, %(spm_ops)s)''' % { |
| "class_name" : self.className, |
| "flags" : self.microFlagsText(microFlags), |
| "dataSize" : self.dataSize, |
| "spm_ops" : ", ".join(spm_ops)} |
| |
| class Fp0Op(FpOp): |
| abstract = True |
| operand_types = () |
| def __init__(self, **kwargs): |
| super().__init__(**kwargs) |
| |
| class Fp1Op(FpOp): |
| abstract = True |
| operand_types = (FloatDestOp) |
| def __init__(self, reg1, **kwargs): |
| super().__init__(reg1, **kwargs) |
| |
| class Fp2Op(FpOp): |
| abstract = True |
| operand_types = (FloatDestOp, FloatSrc1Op) |
| def __init__(self, reg1, reg2, **kwargs): |
| super().__init__(reg1, reg2, **kwargs) |
| |
| class Fp3Op(FpOp): |
| abstract = True |
| operand_types = (FloatDestOp, FloatSrc1Op, FloatSrc2Op) |
| def __init__(self, reg1, reg2, reg3, **kwargs): |
| super().__init__(reg1, reg2, reg3, **kwargs) |
| |
| class Movfp(Fp2Op): |
| code = 'FpDestReg_uqw = FpSrcReg1_uqw;' |
| else_code = 'FpDestReg_uqw = FpDestReg_uqw;' |
| cond_check = "checkCondition(ccFlagBits | cfofBits | dfBit | \ |
| ecfBit | ezfBit, src1)" |
| op_class = 'IntAluOp' |
| |
| class Xorfp(Fp3Op): |
| code = 'FpDestReg_uqw = FpSrcReg1_uqw ^ FpSrcReg2_uqw;' |
| |
| class Sqrtfp(Fp2Op): |
| code = 'FpDestReg = sqrt(FpSrcReg1);' |
| op_class = 'FloatSqrtOp' |
| |
| class Cosfp(Fp2Op): |
| code = 'FpDestReg = cos(FpSrcReg1);' |
| op_class = 'FloatSqrtOp' |
| |
| class Sinfp(Fp2Op): |
| code = 'FpDestReg = sin(FpSrcReg1);' |
| op_class = 'FloatSqrtOp' |
| |
| class Tanfp(Fp2Op): |
| code = 'FpDestReg = tan(FpSrcReg1);' |
| op_class = 'FloatSqrtOp' |
| |
| |
| # Conversion microops |
| class ConvOp(Fp2Op): |
| abstract = True |
| op_class = 'FloatCvtOp' |
| |
| # These probably shouldn't look at the ExtMachInst directly to figure |
| # out what size to use and should instead delegate that to the macroop's |
| # constructor. That would be more efficient, and it would make the |
| # microops a little more modular. |
| class Cvtf_i2d(ConvOp): |
| operand_types = (FloatDestOp, IntSrc1Op) |
| code = ''' |
| X86IntReg intReg = SrcReg1; |
| if (REX_W) |
| FpDestReg = intReg.SR; |
| else |
| FpDestReg = intReg.SE; |
| ''' |
| |
| class Cvtf_i2d_hi(ConvOp): |
| operand_types = (FloatDestOp, IntSrc1Op) |
| code = 'FpDestReg = bits(SrcReg1, 63, 32);' |
| |
| class Cvtf_d2i(ConvOp): |
| operand_types = (FoldedDestOp, FloatSrc1Op) |
| code = ''' |
| int64_t intSrcReg1 = static_cast<int64_t>(FpSrcReg1); |
| DestReg = merge(DestReg, dest, intSrcReg1, dataSize); |
| ''' |
| |
| # Convert two integers registers representing an 80-bit floating |
| # point number to an x87 register. |
| class Cvtint_fp80(Fp3Op): |
| operand_types = (FloatDestOp, IntSrc1Op, IntSrc2Op) |
| code = ''' |
| uint8_t bits[10]; |
| *(uint64_t *)(bits + 0) = SrcReg1; |
| *(uint16_t *)(bits + 8) = (uint16_t)SrcReg2; |
| FpDestReg = loadFloat80(bits); |
| ''' |
| |
| # Convert an x87 register (double) into extended precision and |
| # extract the highest 64 bits. |
| class Cvtfp80h_int(ConvOp): |
| operand_types = (IntDestOp, FloatSrc1Op) |
| code = ''' |
| char bits[10]; |
| storeFloat80(bits, FpSrcReg1); |
| DestReg = *(uint64_t *)(bits + 0); |
| ''' |
| |
| # Convert an x87 register (double) into extended precision and |
| # extract the lowest 16 bits. |
| class Cvtfp80l_int(ConvOp): |
| operand_types = (IntDestOp, FloatSrc1Op) |
| code = ''' |
| char bits[10]; |
| storeFloat80(bits, FpSrcReg1); |
| DestReg = *(uint16_t *)(bits + 8); |
| ''' |
| |
| # These need to consider size at some point. They'll always use doubles |
| # for the moment. |
| class Addfp(Fp3Op): |
| code = 'FpDestReg = FpSrcReg1 + FpSrcReg2;' |
| |
| class Mulfp(Fp3Op): |
| code = 'FpDestReg = FpSrcReg1 * FpSrcReg2;' |
| op_class = 'FloatMultOp' |
| |
| class Divfp(Fp3Op): |
| code = 'FpDestReg = FpSrcReg1 / FpSrcReg2;' |
| op_class = 'FloatDivOp' |
| |
| class Subfp(Fp3Op): |
| code = 'FpDestReg = FpSrcReg1 - FpSrcReg2;' |
| |
| class Yl2xFp(Fp3Op): |
| code = ''' |
| FpDestReg = FpSrcReg2 * (log(FpSrcReg1) / log(2)); |
| ''' |
| op_class = 'FloatSqrtOp' |
| |
| class PremFp(Fp3Op): |
| code = ''' |
| RegVal new_fsw = FSW; |
| int src1_exp; |
| int src2_exp; |
| std::frexp(FpSrcReg1, &src1_exp); |
| std::frexp(FpSrcReg2, &src2_exp); |
| |
| const int d = src2_exp - src1_exp; |
| if (d < 64) { |
| const int64_t q = std::trunc(FpSrcReg2 / FpSrcReg1); |
| FpDestReg = FpSrcReg2 - FpSrcReg1 * q; |
| new_fsw &= ~(CC0Bit | CC1Bit | CC2Bit | CC2Bit); |
| new_fsw |= (q & 0x1) ? CC1Bit : 0; |
| new_fsw |= (q & 0x2) ? CC3Bit : 0; |
| new_fsw |= (q & 0x4) ? CC0Bit : 0; |
| } else { |
| const int n = 42; |
| const int64_t qq = std::trunc( |
| FpSrcReg2 / std::ldexp(FpSrcReg1, d - n)); |
| FpDestReg = FpSrcReg2 - std::ldexp(FpSrcReg1 * qq, d - n); |
| new_fsw |= CC2Bit; |
| } |
| ''' |
| op_class = 'FloatDivOp' |
| |
| flag_code = 'FSW = new_fsw;' |
| |
| class Compfp(Fp2Op): |
| operand_types = (FloatSrc1Op, FloatSrc2Op) |
| # This class sets the condition codes in rflags according to the |
| # rules for comparing floating point. |
| code = ''' |
| // ZF PF CF |
| // Unordered 1 1 1 |
| // Greater than 0 0 0 |
| // Less than 0 0 1 |
| // Equal 1 0 0 |
| // OF = SF = AF = 0 |
| ccFlagBits = ccFlagBits & ~(SFBit | AFBit | ZFBit | PFBit); |
| cfofBits = cfofBits & ~(OFBit | CFBit); |
| |
| if (std::isnan(FpSrcReg1) || std::isnan(FpSrcReg2)) { |
| ccFlagBits = ccFlagBits | (ZFBit | PFBit); |
| cfofBits = cfofBits | CFBit; |
| } |
| else if(FpSrcReg1 < FpSrcReg2) |
| cfofBits = cfofBits | CFBit; |
| else if(FpSrcReg1 == FpSrcReg2) |
| ccFlagBits = ccFlagBits | ZFBit; |
| ''' |
| op_class = 'FloatCmpOp' |
| |
| class absfp(Fp2Op): |
| code = 'FpDestReg = fabs(FpSrcReg1);' |
| flag_code = 'FSW = FSW & (~CC1Bit);' |
| |
| class chsfp(Fp2Op): |
| code = 'FpDestReg = (-1) * (FpSrcReg1);' |
| flag_code = 'FSW = FSW & (~CC1Bit);' |
| |
| class Pop87(Fp0Op): |
| code = '' |
| op_class = 'IntAluOp' |
| }}; |