1// Copyright (c) 2007 The Hewlett-Packard Development Company 2// Copyright (c) 2012-2013 Mark D. Hill and David A. Wood 3// Copyright (c) 2015 Advanced Micro Devices, Inc. 4// 5// All rights reserved. 6// 7// The license below extends only to copyright in the software and shall 8// not be construed as granting a license to any other intellectual 9// property including but not limited to intellectual property relating 10// to a hardware implementation of the functionality of the software 11// licensed hereunder. You may use the software subject to the license 12// terms below provided that you ensure that this notice is replicated 13// unmodified and in its entirety in all distributions of the software, 14// modified or unmodified, in source code or in binary form. 15// 16// Redistribution and use in source and binary forms, with or without 17// modification, are permitted provided that the following conditions are 18// met: redistributions of source code must retain the above copyright 19// notice, this list of conditions and the following disclaimer; 20// redistributions in binary form must reproduce the above copyright 21// notice, this list of conditions and the following disclaimer in the 22// documentation and/or other materials provided with the distribution; 23// neither the name of the copyright holders nor the names of its 24// contributors may be used to endorse or promote products derived from 25// this software without specific prior written permission. 26// 27// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 30// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 31// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 33// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38// 39// Authors: Gabe Black 40// Nilay Vaish 41 42////////////////////////////////////////////////////////////////////////// 43// 44// FpOp Microop templates 45// 46////////////////////////////////////////////////////////////////////////// 47 48def template MicroFpOpExecute {{ 49 Fault %(class_name)s::execute(ExecContext *xc, 50 Trace::InstRecord *traceData) const 51 { 52 Fault fault = NoFault; 53 54 DPRINTF(X86, "The data size is %d\n", dataSize); 55 %(op_decl)s; 56 %(op_rd)s; 57 58 if(%(cond_check)s) 59 { 60 %(code)s; 61 %(flag_code)s; 62 %(tag_code)s; 63 %(top_code)s; 64 } 65 else 66 { 67 %(else_code)s; 68 } 69 70 //Write the resulting state to the execution context 71 if(fault == NoFault) 72 { 73 %(op_wb)s; 74 } 75 return fault; 76 } 77}}; 78 79def template MicroFpOpDeclare {{ 80 class %(class_name)s : public %(base_class)s 81 { 82 public: 83 %(class_name)s(ExtMachInst _machInst, 84 const char * instMnem, uint64_t setFlags, 85 InstRegIndex _src1, InstRegIndex _src2, InstRegIndex _dest, 86 uint8_t _dataSize, int8_t _spm); 87 88 Fault execute(ExecContext *, Trace::InstRecord *) const; 89 }; 90}}; 91 92def template MicroFpOpConstructor {{ 93 %(class_name)s::%(class_name)s( 94 ExtMachInst machInst, const char * instMnem, uint64_t setFlags, 95 InstRegIndex _src1, InstRegIndex _src2, InstRegIndex _dest, 96 uint8_t _dataSize, int8_t _spm) : 97 %(base_class)s(machInst, "%(mnemonic)s", instMnem, setFlags, 98 _src1, _src2, _dest, _dataSize, _spm, 99 %(op_class)s) 100 { 101 %(constructor)s; 102 } 103}}; 104 105let {{ 106 # Make these empty strings so that concatenating onto 107 # them will always work. 108 header_output = "" 109 decoder_output = "" 110 exec_output = "" 111 112 class FpOpMeta(type): 113 def buildCppClasses(self, name, Name, suffix, \ 114 code, flag_code, cond_check, else_code, op_class): 115 116 # Globals to stick the output in 117 global header_output 118 global decoder_output 119 global exec_output 120 121 # Stick all the code together so it can be searched at once 122 allCode = "|".join((code, flag_code, cond_check, else_code)) 123 124 # If there's something optional to do with flags, generate 125 # a version without it and fix up this version to use it. 126 if flag_code is not "" or cond_check is not "true": 127 self.buildCppClasses(name, Name, suffix, 128 code, "", "true", else_code, op_class) 129 suffix = "Flags" + suffix 130 131 base = "X86ISA::FpOp" 132 133 # Get everything ready for the substitution 134 iop_tag = InstObjParams(name, Name + suffix + "TopTag", base, 135 {"code" : code, 136 "flag_code" : flag_code, 137 "cond_check" : cond_check, 138 "else_code" : else_code, 139 "tag_code" : "FTW = genX87Tags(FTW, TOP, spm);", 140 "top_code" : "TOP = (TOP + spm + 8) % 8;", 141 "op_class" : op_class}) 142 iop_top = InstObjParams(name, Name + suffix + "Top", base, 143 {"code" : code, 144 "flag_code" : flag_code, 145 "cond_check" : cond_check, 146 "else_code" : else_code, 147 "tag_code" : ";", 148 "top_code" : "TOP = (TOP + spm + 8) % 8;", 149 "op_class" : op_class}) 150 iop = InstObjParams(name, Name + suffix, base, 151 {"code" : code, 152 "flag_code" : flag_code, 153 "cond_check" : cond_check, 154 "else_code" : else_code, 155 "tag_code" : ";", 156 "top_code" : ";", 157 "op_class" : op_class}) 158 159 # Generate the actual code (finally!) 160 header_output += MicroFpOpDeclare.subst(iop_tag) 161 decoder_output += MicroFpOpConstructor.subst(iop_tag) 162 exec_output += MicroFpOpExecute.subst(iop_tag) 163 header_output += MicroFpOpDeclare.subst(iop_top) 164 decoder_output += MicroFpOpConstructor.subst(iop_top) 165 exec_output += MicroFpOpExecute.subst(iop_top) 166 header_output += MicroFpOpDeclare.subst(iop) 167 decoder_output += MicroFpOpConstructor.subst(iop) 168 exec_output += MicroFpOpExecute.subst(iop) 169 170 171 def __new__(mcls, Name, bases, dict): 172 abstract = False 173 name = Name.lower() 174 if "abstract" in dict: 175 abstract = dict['abstract'] 176 del dict['abstract'] 177 178 cls = super(FpOpMeta, mcls).__new__(mcls, Name, bases, dict) 179 if not abstract: 180 cls.className = Name 181 cls.mnemonic = name 182 code = cls.code 183 flag_code = cls.flag_code 184 cond_check = cls.cond_check 185 else_code = cls.else_code 186 op_class = cls.op_class 187 188 # Set up the C++ classes 189 mcls.buildCppClasses(cls, name, Name, "", 190 code, flag_code, cond_check, else_code, op_class) 191 192 # Hook into the microassembler dict 193 global microopClasses 194 microopClasses[name] = cls 195 196 return cls 197 198 class FpUnaryOp(X86Microop): 199 __metaclass__ = FpOpMeta 200 # This class itself doesn't act as a microop 201 abstract = True 202 203 # Default template parameter values 204 flag_code = "" 205 cond_check = "true" 206 else_code = ";" 207 op_class = "FloatAddOp" 208 209 def __init__(self, dest, src1, spm=0, \ 210 SetStatus=False, UpdateFTW=True, dataSize="env.dataSize"): 211 self.dest = dest 212 self.src1 = src1 213 self.src2 = "InstRegIndex(0)" 214 self.spm = spm 215 self.dataSize = dataSize 216 if SetStatus: 217 self.className += "Flags" 218 if spm: 219 self.className += "Top" 220 if spm and UpdateFTW: 221 self.className += "Tag" 222 223 def getAllocator(self, microFlags): 224 return '''new %(class_name)s(machInst, macrocodeBlock, 225 %(flags)s, %(src1)s, %(src2)s, %(dest)s, 226 %(dataSize)s, %(spm)d)''' % { 227 "class_name" : self.className, 228 "flags" : self.microFlagsText(microFlags), 229 "src1" : self.src1, "src2" : self.src2, 230 "dest" : self.dest, 231 "dataSize" : self.dataSize, 232 "spm" : self.spm} 233 234 class FpBinaryOp(X86Microop): 235 __metaclass__ = FpOpMeta 236 # This class itself doesn't act as a microop 237 abstract = True 238 239 # Default template parameter values 240 flag_code = "" 241 cond_check = "true" 242 else_code = ";" 243 op_class = "FloatAddOp" 244 245 def __init__(self, dest, src1, src2, spm=0, \ 246 SetStatus=False, UpdateFTW=True, dataSize="env.dataSize"): 247 self.dest = dest 248 self.src1 = src1 249 self.src2 = src2 250 self.spm = spm 251 self.dataSize = dataSize 252 if SetStatus: 253 self.className += "Flags" 254 if spm: 255 self.className += "Top" 256 if spm and UpdateFTW: 257 self.className += "Tag" 258 259 def getAllocator(self, microFlags): 260 return '''new %(class_name)s(machInst, macrocodeBlock, 261 %(flags)s, %(src1)s, %(src2)s, %(dest)s, 262 %(dataSize)s, %(spm)d)''' % { 263 "class_name" : self.className, 264 "flags" : self.microFlagsText(microFlags), 265 "src1" : self.src1, "src2" : self.src2, 266 "dest" : self.dest, 267 "dataSize" : self.dataSize, 268 "spm" : self.spm} 269 270 class Movfp(FpUnaryOp): 271 code = 'FpDestReg_uqw = FpSrcReg1_uqw;' 272 else_code = 'FpDestReg_uqw = FpDestReg_uqw;' 273 cond_check = "checkCondition(ccFlagBits | cfofBits | dfBit | \ 274 ecfBit | ezfBit, src2)" 275 op_class = 'IntAluOp' 276 277 class Xorfp(FpBinaryOp): 278 code = 'FpDestReg_uqw = FpSrcReg1_uqw ^ FpSrcReg2_uqw;' 279 280 class Sqrtfp(FpBinaryOp): 281 code = 'FpDestReg = sqrt(FpSrcReg2);' 282 op_class = 'FloatSqrtOp' 283 284 class Cosfp(FpUnaryOp): 285 code = 'FpDestReg = cos(FpSrcReg1);' 286 op_class = 'FloatSqrtOp' 287 288 class Sinfp(FpUnaryOp): 289 code = 'FpDestReg = sin(FpSrcReg1);' 290 op_class = 'FloatSqrtOp' 291 292 class Tanfp(FpUnaryOp): 293 code = 'FpDestReg = tan(FpSrcReg1);' 294 op_class = 'FloatSqrtOp' 295 296 297 # Conversion microops 298 class ConvOp(FpBinaryOp): 299 abstract = True 300 op_class = 'FloatCvtOp' 301 def __init__(self, dest, src1, **kwargs): 302 super(ConvOp, self).__init__(dest, src1, \ 303 "InstRegIndex(FLOATREG_MICROFP0)", \ 304 **kwargs) 305 306 # These probably shouldn't look at the ExtMachInst directly to figure 307 # out what size to use and should instead delegate that to the macroop's 308 # constructor. That would be more efficient, and it would make the 309 # microops a little more modular. 310 class cvtf_i2d(ConvOp): 311 code = ''' 312 X86IntReg intReg = SSrcReg1; 313 if (REX_W) 314 FpDestReg = intReg.SR; 315 else 316 FpDestReg = intReg.SE; 317 ''' 318 319 class cvtf_i2d_hi(ConvOp): 320 code = 'FpDestReg = bits(SSrcReg1, 63, 32);' 321 322 class cvtf_d2i(ConvOp): 323 code = ''' 324 int64_t intSrcReg1 = static_cast<int64_t>(FpSrcReg1); 325 if (REX_W) 326 SDestReg = intSrcReg1; 327 else 328 SDestReg = merge(SDestReg, intSrcReg1, 4); 329 ''' 330 331 # Convert two integers registers representing an 80-bit floating 332 # point number to an x87 register. 333 class cvtint_fp80(FpBinaryOp): 334 code = ''' 335 uint8_t bits[10]; 336 *(uint64_t *)(bits + 0) = SSrcReg1; 337 *(uint16_t *)(bits + 8) = (uint16_t)SSrcReg2; 338 FpDestReg = loadFloat80(bits); 339 ''' 340 341 # Convert an x87 register (double) into extended precision and 342 # extract the highest 64 bits. 343 class cvtfp80h_int(ConvOp): 344 code = ''' 345 char bits[10]; 346 storeFloat80(bits, FpSrcReg1); 347 SDestReg = *(uint64_t *)(bits + 0); 348 ''' 349 350 # Convert an x87 register (double) into extended precision and 351 # extract the lowest 16 bits. 352 class cvtfp80l_int(ConvOp): 353 code = ''' 354 char bits[10]; 355 storeFloat80(bits, FpSrcReg1); 356 SDestReg = *(uint16_t *)(bits + 8); 357 ''' 358 359 # These need to consider size at some point. They'll always use doubles 360 # for the moment. 361 class addfp(FpBinaryOp): 362 code = 'FpDestReg = FpSrcReg1 + FpSrcReg2;' 363 364 class mulfp(FpBinaryOp): 365 code = 'FpDestReg = FpSrcReg1 * FpSrcReg2;' 366 op_class = 'FloatMultOp' 367 368 class divfp(FpBinaryOp): 369 code = 'FpDestReg = FpSrcReg1 / FpSrcReg2;' 370 op_class = 'FloatDivOp' 371 372 class subfp(FpBinaryOp): 373 code = 'FpDestReg = FpSrcReg1 - FpSrcReg2;' 374 375 class Yl2xFp(FpBinaryOp): 376 code = ''' 377 FpDestReg = FpSrcReg2 * (log(FpSrcReg1) / log(2)); 378 ''' 379 op_class = 'FloatSqrtOp' 380 381 class PremFp(FpBinaryOp): 382 code = ''' 383 RegVal new_fsw = FSW; 384 int src1_exp; 385 int src2_exp; 386 std::frexp(FpSrcReg1, &src1_exp); 387 std::frexp(FpSrcReg2, &src2_exp); 388 389 const int d = src2_exp - src1_exp; 390 if (d < 64) { 391 const int64_t q = std::trunc(FpSrcReg2 / FpSrcReg1); 392 FpDestReg = FpSrcReg2 - FpSrcReg1 * q; 393 new_fsw &= ~(CC0Bit | CC1Bit | CC2Bit | CC2Bit); 394 new_fsw |= (q & 0x1) ? CC1Bit : 0; 395 new_fsw |= (q & 0x2) ? CC3Bit : 0; 396 new_fsw |= (q & 0x4) ? CC0Bit : 0; 397 } else { 398 const int n = 42; 399 const int64_t qq = std::trunc( 400 FpSrcReg2 / std::ldexp(FpSrcReg1, d - n)); 401 FpDestReg = FpSrcReg2 - std::ldexp(FpSrcReg1 * qq, d - n); 402 new_fsw |= CC2Bit; 403 } 404 ''' 405 op_class = 'FloatDivOp' 406 407 flag_code = 'FSW = new_fsw;' 408 409 class Compfp(FpBinaryOp): 410 def __init__(self, src1, src2, spm=0, setStatus=False, updateFTW=True, \ 411 dataSize="env.dataSize"): 412 super(Compfp, self).__init__("InstRegIndex(FLOATREG_MICROFP0)", \ 413 src1, src2, spm, setStatus, updateFTW, dataSize) 414 # This class sets the condition codes in rflags according to the 415 # rules for comparing floating point. 416 code = ''' 417 // ZF PF CF 418 // Unordered 1 1 1 419 // Greater than 0 0 0 420 // Less than 0 0 1 421 // Equal 1 0 0 422 // OF = SF = AF = 0 423 ccFlagBits = ccFlagBits & ~(SFBit | AFBit | ZFBit | PFBit); 424 cfofBits = cfofBits & ~(OFBit | CFBit); 425 426 if (std::isnan(FpSrcReg1) || std::isnan(FpSrcReg2)) { 427 ccFlagBits = ccFlagBits | (ZFBit | PFBit); 428 cfofBits = cfofBits | CFBit; 429 } 430 else if(FpSrcReg1 < FpSrcReg2) 431 cfofBits = cfofBits | CFBit; 432 else if(FpSrcReg1 == FpSrcReg2) 433 ccFlagBits = ccFlagBits | ZFBit; 434 ''' 435 op_class = 'FloatCmpOp' 436 437 class absfp(FpUnaryOp): 438 code = 'FpDestReg = fabs(FpSrcReg1);' 439 flag_code = 'FSW = FSW & (~CC1Bit);' 440 441 class chsfp(FpUnaryOp): 442 code = 'FpDestReg = (-1) * (FpSrcReg1);' 443 flag_code = 'FSW = FSW & (~CC1Bit);' 444 445 class Pop87(FpUnaryOp): 446 def __init__(self, spm=1, UpdateFTW=True): 447 super(Pop87, self).__init__( \ 448 "InstRegIndex(FLOATREG_MICROFP0)", \ 449 "InstRegIndex(FLOATREG_MICROFP0)", \ 450 spm=spm, SetStatus=False, UpdateFTW=UpdateFTW) 451 452 code = '' 453 op_class = 'IntAluOp' 454}}; 455