1# Copyright (C) 2012 Apple Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions 5# are met: 6# 1. Redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer. 8# 2. Redistributions in binary form must reproduce the above copyright 9# notice, this list of conditions and the following disclaimer in the 10# documentation and/or other materials provided with the distribution. 11# 12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 13# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 14# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 16# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 18# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 19# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 20# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 21# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 22# THE POSSIBILITY OF SUCH DAMAGE. 23 24require "config" 25require "ast" 26require "opt" 27 28# The CLoop llint backend is initially based on the ARMv7 backend, and 29# then further enhanced with a few instructions from the x86 backend to 30# support building for X64 targets. Hence, the shape of the generated 31# code and the usage convention of registers will look a lot like the 32# ARMv7 backend's. 33 34def cloopMapType(type) 35 case type 36 when :int; ".i" 37 when :uint; ".u" 38 when :int32; ".i32" 39 when :uint32; ".u32" 40 when :int64; ".i64" 41 when :uint64; ".u64" 42 when :int8; ".i8" 43 when :uint8; ".u8" 44 when :int8Ptr; ".i8p" 45 when :voidPtr; ".vp" 46 when :nativeFunc; ".nativeFunc" 47 when :double; ".d" 48 when :castToDouble; ".castToDouble" 49 when :castToInt64; ".castToInt64" 50 when :opcode; ".opcode" 51 else; 52 raise "Unsupported type" 53 end 54end 55 56 57class SpecialRegister < NoChildren 58 def clDump 59 @name 60 end 61 def clValue(type=:int) 62 @name + cloopMapType(type) 63 end 64end 65 66C_LOOP_SCRATCH_FPR = SpecialRegister.new("d6") 67 68class RegisterID 69 def clDump 70 case name 71 when "t0" 72 "t0" 73 when "t1" 74 "t1" 75 when "t2" 76 "t2" 77 when "t3" 78 "t3" 79 when "t4" 80 "rPC" 81 when "t6" 82 "rBasePC" 83 when "csr1" 84 "tagTypeNumber" 85 when "csr2" 86 "tagMask" 87 when "cfr" 88 "cfr" 89 when "lr" 90 "rRetVPC" 91 when "sp" 92 "sp" 93 else 94 raise "Bad register #{name} for C_LOOP at #{codeOriginString}" 95 end 96 end 97 def clValue(type=:int) 98 clDump + cloopMapType(type) 99 end 100end 101 102class FPRegisterID 103 def clDump 104 case name 105 when "ft0", "fr" 106 "d0" 107 when "ft1" 108 "d1" 109 when "ft2" 110 "d2" 111 when "ft3" 112 "d3" 113 when "ft4" 114 "d4" 115 when "ft5" 116 "d5" 117 else 118 raise "Bad register #{name} for C_LOOP at #{codeOriginString}" 119 end 120 end 121 def clValue(type=:int) 122 clDump + cloopMapType(type) 123 end 124end 125 126class Immediate 127 def clDump 128 "#{value}" 129 end 130 def clValue(type=:int) 131 # There is a case of a very large unsigned number (0x8000000000000000) 132 # which we wish to encode. Unfortunately, the C/C++ compiler 133 # complains if we express that number as a positive decimal integer. 134 # Hence, for positive values, we just convert the number into hex form 135 # to keep the compiler happy. 136 # 137 # However, for negative values, the to_s(16) hex conversion method does 138 # not strip the "-" sign resulting in a meaningless "0x-..." valueStr. 139 # To workaround this, we simply don't encode negative numbers as hex. 140 141 valueStr = (value < 0) ? "#{value}" : "0x#{value.to_s(16)}" 142 143 case type 144 when :int8; "int8_t(#{valueStr})" 145 when :int32; "int32_t(#{valueStr})" 146 when :int64; "int64_t(#{valueStr})" 147 when :int; "intptr_t(#{valueStr})" 148 when :uint8; "uint8_t(#{valueStr})" 149 when :uint32; "uint32_t(#{valueStr})" 150 when :uint64; "uint64_t(#{valueStr})" 151 when :uint; "uintptr_t(#{valueStr})" 152 else 153 raise "Not implemented immediate of type: #{type}" 154 end 155 end 156end 157 158class Address 159 def clDump 160 "[#{base.clDump}, #{offset.value}]" 161 end 162 def clValue(type=:int) 163 case type 164 when :int8; int8MemRef 165 when :int32; int32MemRef 166 when :int64; int64MemRef 167 when :int; intMemRef 168 when :uint8; uint8MemRef 169 when :uint32; uint32MemRef 170 when :uint64; uint64MemRef 171 when :uint; uintMemRef 172 when :opcode; opcodeMemRef 173 when :nativeFunc; nativeFuncMemRef 174 else 175 raise "Unexpected Address type: #{type}" 176 end 177 end 178 def pointerExpr 179 if base.is_a? RegisterID and base.name == "sp" 180 offsetValue = "#{offset.value}" 181 "(ASSERT(#{offsetValue} == offsetof(JITStackFrame, vm)), &sp->vm)" 182 elsif offset.value == 0 183 "#{base.clValue(:int8Ptr)}" 184 elsif offset.value > 0 185 "#{base.clValue(:int8Ptr)} + #{offset.value}" 186 else 187 "#{base.clValue(:int8Ptr)} - #{-offset.value}" 188 end 189 end 190 def int8MemRef 191 "*CAST<int8_t*>(#{pointerExpr})" 192 end 193 def int16MemRef 194 "*CAST<int16_t*>(#{pointerExpr})" 195 end 196 def int32MemRef 197 "*CAST<int32_t*>(#{pointerExpr})" 198 end 199 def int64MemRef 200 "*CAST<int64_t*>(#{pointerExpr})" 201 end 202 def intMemRef 203 "*CAST<intptr_t*>(#{pointerExpr})" 204 end 205 def uint8MemRef 206 "*CAST<uint8_t*>(#{pointerExpr})" 207 end 208 def uint16MemRef 209 "*CAST<uint16_t*>(#{pointerExpr})" 210 end 211 def uint32MemRef 212 "*CAST<uint32_t*>(#{pointerExpr})" 213 end 214 def uint64MemRef 215 "*CAST<uint64_t*>(#{pointerExpr})" 216 end 217 def uintMemRef 218 "*CAST<uintptr_t*>(#{pointerExpr})" 219 end 220 def nativeFuncMemRef 221 "*CAST<NativeFunction*>(#{pointerExpr})" 222 end 223 def opcodeMemRef 224 "*CAST<Opcode*>(#{pointerExpr})" 225 end 226 def dblMemRef 227 "*CAST<double*>(#{pointerExpr})" 228 end 229end 230 231class BaseIndex 232 def clDump 233 "[#{base.clDump}, #{offset.clDump}, #{index.clDump} << #{scaleShift}]" 234 end 235 def clValue(type=:int) 236 case type 237 when :int8; int8MemRef 238 when :int32; int32MemRef 239 when :int64; int64MemRef 240 when :int; intMemRef 241 when :uint8; uint8MemRef 242 when :uint32; uint32MemRef 243 when :uint64; uint64MemRef 244 when :uint; uintMemRef 245 when :opcode; opcodeMemRef 246 else 247 raise "Unexpected BaseIndex type: #{type}" 248 end 249 end 250 def pointerExpr 251 if base.is_a? RegisterID and base.name == "sp" 252 offsetValue = "(#{index.clValue} << #{scaleShift}) + #{offset.clValue})" 253 "(ASSERT(#{offsetValue} == offsetof(JITStackFrame, vm)), &sp->vm)" 254 else 255 "#{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift}) + #{offset.clValue}" 256 end 257 end 258 def int8MemRef 259 "*CAST<int8_t*>(#{pointerExpr})" 260 end 261 def int16MemRef 262 "*CAST<int16_t*>(#{pointerExpr})" 263 end 264 def int32MemRef 265 "*CAST<int32_t*>(#{pointerExpr})" 266 end 267 def int64MemRef 268 "*CAST<int64_t*>(#{pointerExpr})" 269 end 270 def intMemRef 271 "*CAST<intptr_t*>(#{pointerExpr})" 272 end 273 def uint8MemRef 274 "*CAST<uint8_t*>(#{pointerExpr})" 275 end 276 def uint16MemRef 277 "*CAST<uint16_t*>(#{pointerExpr})" 278 end 279 def uint32MemRef 280 "*CAST<uint32_t*>(#{pointerExpr})" 281 end 282 def uint64MemRef 283 "*CAST<uint64_t*>(#{pointerExpr})" 284 end 285 def uintMemRef 286 "*CAST<uintptr_t*>(#{pointerExpr})" 287 end 288 def opcodeMemRef 289 "*CAST<Opcode*>(#{pointerExpr})" 290 end 291 def dblMemRef 292 "*CAST<double*>(#{pointerExpr})" 293 end 294end 295 296class AbsoluteAddress 297 def clDump 298 "#{codeOriginString}" 299 end 300 def clValue 301 clDump 302 end 303end 304 305 306# 307# Lea support. 308# 309 310class Address 311 def cloopEmitLea(destination, type) 312 if destination == base 313 $asm.putc "#{destination.clValue(:int8Ptr)} += #{offset.clValue(type)};" 314 else 315 $asm.putc "#{destination.clValue(:int8Ptr)} = #{base.clValue(:int8Ptr)} + #{offset.clValue(type)};" 316 end 317 end 318end 319 320class BaseIndex 321 def cloopEmitLea(destination, type) 322 raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0 323 $asm.putc "#{destination.clValue(:int8Ptr)} = #{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift});" 324 end 325end 326 327# 328# Actual lowering code follows. 329# 330 331class Sequence 332 def getModifiedListC_LOOP 333 myList = @list 334 335 # Verify that we will only see instructions and labels. 336 myList.each { 337 | node | 338 unless node.is_a? Instruction or 339 node.is_a? Label or 340 node.is_a? LocalLabel or 341 node.is_a? Skip 342 raise "Unexpected #{node.inspect} at #{node.codeOrigin}" 343 end 344 } 345 346 return myList 347 end 348end 349 350def clOperands(operands) 351 operands.map{|v| v.clDump}.join(", ") 352end 353 354 355def cloopEmitOperation(operands, type, operator) 356 raise unless type == :int || type == :uint || type == :int32 || type == :uint32 || \ 357 type == :int64 || type == :uint64 || type == :double 358 if operands.size == 3 359 $asm.putc "#{operands[2].clValue(type)} = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};" 360 if operands[2].is_a? RegisterID and (type == :int32 or type == :uint32) 361 $asm.putc "#{operands[2].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port. 362 end 363 else 364 raise unless operands.size == 2 365 raise unless not operands[1].is_a? Immediate 366 $asm.putc "#{operands[1].clValue(type)} = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};" 367 if operands[1].is_a? RegisterID and (type == :int32 or type == :uint32) 368 $asm.putc "#{operands[1].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port. 369 end 370 end 371end 372 373def cloopEmitShiftOperation(operands, type, operator) 374 raise unless type == :int || type == :uint || type == :int32 || type == :uint32 || type == :int64 || type == :uint64 375 if operands.size == 3 376 $asm.putc "#{operands[2].clValue(type)} = #{operands[1].clValue(type)} #{operator} (#{operands[0].clValue(:int)} & 0x1f);" 377 if operands[2].is_a? RegisterID and (type == :int32 or type == :uint32) 378 $asm.putc "#{operands[2].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port. 379 end 380 else 381 raise unless operands.size == 2 382 raise unless not operands[1].is_a? Immediate 383 $asm.putc "#{operands[1].clValue(type)} = #{operands[1].clValue(type)} #{operator} (#{operands[0].clValue(:int)} & 0x1f);" 384 if operands[1].is_a? RegisterID and (type == :int32 or type == :uint32) 385 $asm.putc "#{operands[1].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port. 386 end 387 end 388end 389 390def cloopEmitUnaryOperation(operands, type, operator) 391 raise unless type == :int || type == :uint || type == :int32 || type == :uint32 || type == :int64 || type == :uint64 392 raise unless operands.size == 1 393 raise unless not operands[0].is_a? Immediate 394 $asm.putc "#{operands[0].clValue(type)} = #{operator}#{operands[0].clValue(type)};" 395 if operands[0].is_a? RegisterID and (type == :int32 or type == :uint32) 396 $asm.putc "#{operands[0].clDump}.clearHighWord();" # Just clear it. It does nothing on the 32-bit port. 397 end 398end 399 400def cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, condition) 401 $asm.putc "if (std::isnan(#{operands[0].clValue(:double)}) || isnan(#{operands[1].clValue(:double)})" 402 $asm.putc " || (#{operands[0].clValue(:double)} #{condition} #{operands[1].clValue(:double)}))" 403 $asm.putc " goto #{operands[2].cLabel};" 404end 405 406 407def cloopEmitCompareAndSet(operands, type, comparator) 408 # The result is a boolean. Hence, it doesn't need to be based on the type 409 # of the arguments being compared. 410 $asm.putc "#{operands[2].clValue} = (#{operands[0].clValue(type)} #{comparator} #{op2 = operands[1].clValue(type)});" 411end 412 413 414def cloopEmitCompareAndBranch(operands, type, comparator) 415 $asm.putc "if (#{operands[0].clValue(type)} #{comparator} #{operands[1].clValue(type)})" 416 $asm.putc " goto #{operands[2].cLabel};" 417end 418 419 420# conditionTest should contain a string that provides a comparator and a RHS 421# value e.g. "< 0". 422def cloopGenerateConditionExpression(operands, type, conditionTest) 423 op1 = operands[0].clValue(type) 424 425 # The operands must consist of 2 or 3 values. 426 case operands.size 427 when 2 # Just test op1 against the conditionTest. 428 lhs = op1 429 when 3 # Mask op1 with op2 before testing against the conditionTest. 430 lhs = "(#{op1} & #{operands[1].clValue(type)})" 431 else 432 raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}" 433 end 434 435 "#{lhs} #{conditionTest}" 436end 437 438# conditionTest should contain a string that provides a comparator and a RHS 439# value e.g. "< 0". 440def cloopEmitTestAndBranchIf(operands, type, conditionTest, branchTarget) 441 conditionExpr = cloopGenerateConditionExpression(operands, type, conditionTest) 442 $asm.putc "if (#{conditionExpr})" 443 $asm.putc " goto #{branchTarget};" 444end 445 446def cloopEmitTestSet(operands, type, conditionTest) 447 # The result is a boolean condition. Hence, the result type is always an 448 # int. The passed in type is only used for the values being tested in 449 # the condition test. 450 conditionExpr = cloopGenerateConditionExpression(operands, type, conditionTest) 451 $asm.putc "#{operands[-1].clValue} = (#{conditionExpr});" 452end 453 454def cloopEmitOpAndBranch(operands, operator, type, conditionTest) 455 case type 456 when :int; tempType = "intptr_t" 457 when :int32; tempType = "int32_t" 458 when :int64; tempType = "int64_t" 459 else 460 raise "Unimplemented type" 461 end 462 463 op1 = operands[0].clValue(type) 464 op2 = operands[1].clValue(type) 465 466 $asm.putc "{" 467 $asm.putc " #{tempType} temp = #{op2} #{operator} #{op1};" 468 $asm.putc " #{op2} = temp;" 469 $asm.putc " if (temp #{conditionTest})" 470 $asm.putc " goto #{operands[2].cLabel};" 471 $asm.putc "}" 472end 473 474def cloopAddOverflowTest(operands, type) 475 case type 476 when :int32 477 tempType = "int32_t" 478 signBit = "SIGN_BIT32" 479 else 480 raise "Unimplemented type" 481 end 482 483 $asm.putc " #{tempType} a = #{operands[0].clValue(type)};" 484 $asm.putc " #{tempType} b = #{operands[1].clValue(type)};" 485 $asm.putc " // sign(b) sign(a) | Overflows if:" 486 $asm.putc " // 0 0 | sign(b+a) = 1 (pos + pos != neg)" 487 $asm.putc " // 0 1 | never" 488 $asm.putc " // 1 0 | never" 489 $asm.putc " // 1 1 | sign(b+a) = 0 (neg + neg != pos)" 490 "((#{signBit}(b) == #{signBit}(a)) && (#{signBit}(b+a) != #{signBit}(a)))" 491end 492 493def cloopSubOverflowTest(operands, type) 494 case type 495 when :int32 496 tempType = "int32_t" 497 signBit = "SIGN_BIT32" 498 else 499 raise "Unimplemented type" 500 end 501 502 $asm.putc " #{tempType} a = #{operands[0].clValue(type)};" 503 $asm.putc " #{tempType} b = #{operands[1].clValue(type)};" 504 $asm.putc " // sign(b) sign(a) | Overflows if:" 505 $asm.putc " // 0 0 | never" 506 $asm.putc " // 0 1 | sign(b-a) = 1 (pos - neg != pos)" 507 $asm.putc " // 1 0 | sign(b-a) = 0 (neg - pos != pos)" 508 $asm.putc " // 1 1 | never" 509 "((#{signBit}(b) != #{signBit}(a)) && (#{signBit}(b-a) == #{signBit}(a)))" 510end 511 512def cloopMulOverflowTest(operands, type) 513 case type 514 when :int32 515 tempType = "uint32_t" 516 else 517 raise "Unimplemented type" 518 end 519 $asm.putc " #{tempType} a = #{operands[0].clValue(type)};" 520 $asm.putc " #{tempType} b = #{operands[1].clValue(type)};" 521 "((b | a) >> 15)" 522end 523 524def cloopEmitOpAndBranchIfOverflow(operands, operator, type) 525 $asm.putc "{" 526 527 # Emit the overflow test based on the operands and the type: 528 case operator 529 when "+"; overflowTest = cloopAddOverflowTest(operands, type) 530 when "-"; overflowTest = cloopSubOverflowTest(operands, type) 531 when "*"; overflowTest = cloopMulOverflowTest(operands, type) 532 else 533 raise "Unimplemented opeartor" 534 end 535 536 $asm.putc " bool didOverflow = #{overflowTest};" 537 $asm.putc " #{operands[1].clValue(type)} = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};" 538 $asm.putc " if (didOverflow)" 539 $asm.putc " goto #{operands[2].cLabel};" 540 $asm.putc "}" 541end 542 543# operands: callTarget, currentFrame, currentPC 544def cloopEmitCallSlowPath(operands) 545 $asm.putc "{" 546 $asm.putc " ExecState* exec = CAST<ExecState*>(#{operands[1].clValue(:voidPtr)});" 547 $asm.putc " Instruction* pc = CAST<Instruction*>(#{operands[2].clValue(:voidPtr)});" 548 $asm.putc " SlowPathReturnType result = #{operands[0].cLabel}(exec, pc);" 549 $asm.putc " LLInt::decodeResult(result, t0.instruction, t1.execState);" 550 $asm.putc "}" 551end 552 553class Instruction 554 def lowerC_LOOP 555 $asm.codeOrigin codeOriginString if $enableCodeOriginComments 556 $asm.annotation annotation if $enableInstrAnnotations && (opcode != "cloopDo") 557 558 case opcode 559 when "addi" 560 cloopEmitOperation(operands, :int32, "+") 561 when "addq" 562 cloopEmitOperation(operands, :int64, "+") 563 when "addp" 564 cloopEmitOperation(operands, :int, "+") 565 566 when "andi" 567 cloopEmitOperation(operands, :int32, "&") 568 when "andq" 569 cloopEmitOperation(operands, :int64, "&") 570 when "andp" 571 cloopEmitOperation(operands, :int, "&") 572 573 when "ori" 574 cloopEmitOperation(operands, :int32, "|") 575 when "orq" 576 cloopEmitOperation(operands, :int64, "|") 577 when "orp" 578 cloopEmitOperation(operands, :int, "|") 579 580 when "xori" 581 cloopEmitOperation(operands, :int32, "^") 582 when "xorq" 583 cloopEmitOperation(operands, :int64, "^") 584 when "xorp" 585 cloopEmitOperation(operands, :int, "^") 586 587 when "lshifti" 588 cloopEmitShiftOperation(operands, :int32, "<<") 589 when "lshiftq" 590 cloopEmitShiftOperation(operands, :int64, "<<") 591 when "lshiftp" 592 cloopEmitShiftOperation(operands, :int, "<<") 593 594 when "rshifti" 595 cloopEmitShiftOperation(operands, :int32, ">>") 596 when "rshiftq" 597 cloopEmitShiftOperation(operands, :int64, ">>") 598 when "rshiftp" 599 cloopEmitShiftOperation(operands, :int, ">>") 600 601 when "urshifti" 602 cloopEmitShiftOperation(operands, :uint32, ">>") 603 when "urshiftq" 604 cloopEmitShiftOperation(operands, :uint64, ">>") 605 when "urshiftp" 606 cloopEmitShiftOperation(operands, :uint, ">>") 607 608 when "muli" 609 cloopEmitOperation(operands, :int32, "*") 610 when "mulq" 611 cloopEmitOperation(operands, :int64, "*") 612 when "mulp" 613 cloopEmitOperation(operands, :int, "*") 614 615 when "subi" 616 cloopEmitOperation(operands, :int32, "-") 617 when "subq" 618 cloopEmitOperation(operands, :int64, "-") 619 when "subp" 620 cloopEmitOperation(operands, :int, "-") 621 622 when "negi" 623 cloopEmitUnaryOperation(operands, :int32, "-") 624 when "negq" 625 cloopEmitUnaryOperation(operands, :int64, "-") 626 when "negp" 627 cloopEmitUnaryOperation(operands, :int, "-") 628 629 when "noti" 630 cloopEmitUnaryOperation(operands, :int32, "!") 631 632 when "loadi" 633 $asm.putc "#{operands[1].clValue(:uint)} = #{operands[0].uint32MemRef};" 634 # There's no need to call clearHighWord() here because the above will 635 # automatically take care of 0 extension. 636 when "loadis" 637 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].int32MemRef};" 638 when "loadq" 639 $asm.putc "#{operands[1].clValue(:int64)} = #{operands[0].int64MemRef};" 640 when "loadp" 641 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].intMemRef};" 642 when "storei" 643 $asm.putc "#{operands[1].int32MemRef} = #{operands[0].clValue(:int32)};" 644 when "storeq" 645 $asm.putc "#{operands[1].int64MemRef} = #{operands[0].clValue(:int64)};" 646 when "storep" 647 $asm.putc "#{operands[1].intMemRef} = #{operands[0].clValue(:int)};" 648 when "loadb" 649 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].uint8MemRef};" 650 when "loadbs" 651 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].int8MemRef};" 652 when "storeb" 653 $asm.putc "#{operands[1].uint8MemRef} = #{operands[0].clValue(:int8)};" 654 when "loadh" 655 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].uint16MemRef};" 656 when "loadhs" 657 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].int16MemRef};" 658 when "storeh" 659 $asm.putc "*#{operands[1].uint16MemRef} = #{operands[0].clValue(:int16)};" 660 when "loadd" 661 $asm.putc "#{operands[1].clValue(:double)} = #{operands[0].dblMemRef};" 662 when "stored" 663 $asm.putc "#{operands[1].dblMemRef} = #{operands[0].clValue(:double)};" 664 665 when "addd" 666 cloopEmitOperation(operands, :double, "+") 667 when "divd" 668 cloopEmitOperation(operands, :double, "/") 669 when "subd" 670 cloopEmitOperation(operands, :double, "-") 671 when "muld" 672 cloopEmitOperation(operands, :double, "*") 673 674 # Convert an int value to its double equivalent, and store it in a double register. 675 when "ci2d" 676 $asm.putc "#{operands[1].clValue(:double)} = #{operands[0].clValue(:int32)};" 677 678 when "bdeq" 679 cloopEmitCompareAndBranch(operands, :double, "==") 680 when "bdneq" 681 cloopEmitCompareAndBranch(operands, :double, "!=") 682 when "bdgt" 683 cloopEmitCompareAndBranch(operands, :double, ">"); 684 when "bdgteq" 685 cloopEmitCompareAndBranch(operands, :double, ">="); 686 when "bdlt" 687 cloopEmitCompareAndBranch(operands, :double, "<"); 688 when "bdlteq" 689 cloopEmitCompareAndBranch(operands, :double, "<="); 690 691 when "bdequn" 692 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "==") 693 when "bdnequn" 694 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "!=") 695 when "bdgtun" 696 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, ">") 697 when "bdgtequn" 698 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, ">=") 699 when "bdltun" 700 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "<") 701 when "bdltequn" 702 cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "<=") 703 704 when "td2i" 705 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].clValue(:double)};" 706 $asm.putc "#{operands[1].clDump}.clearHighWord();" 707 708 when "bcd2i" # operands: srcDbl dstInt slowPath 709 $asm.putc "{" 710 $asm.putc " double d = #{operands[0].clValue(:double)};" 711 $asm.putc " const int32_t asInt32 = int32_t(d);" 712 $asm.putc " if (asInt32 != d || (!asInt32 && std::signbit(d))) // true for -0.0" 713 $asm.putc " goto #{operands[2].cLabel};" 714 $asm.putc " #{operands[1].clValue} = asInt32;" 715 $asm.putc " #{operands[1].clDump}.clearHighWord();" 716 $asm.putc "}" 717 718 when "move" 719 $asm.putc "#{operands[1].clValue(:int)} = #{operands[0].clValue(:int)};" 720 when "sxi2q" 721 $asm.putc "#{operands[1].clValue(:int64)} = #{operands[0].clValue(:int32)};" 722 when "zxi2q" 723 $asm.putc "#{operands[1].clValue(:uint64)} = #{operands[0].clValue(:uint32)};" 724 when "nop" 725 $asm.putc "// nop" 726 when "bbeq" 727 cloopEmitCompareAndBranch(operands, :int8, "==") 728 when "bieq" 729 cloopEmitCompareAndBranch(operands, :int32, "==") 730 when "bqeq" 731 cloopEmitCompareAndBranch(operands, :int64, "==") 732 when "bpeq" 733 cloopEmitCompareAndBranch(operands, :int, "==") 734 735 when "bbneq" 736 cloopEmitCompareAndBranch(operands, :int8, "!=") 737 when "bineq" 738 cloopEmitCompareAndBranch(operands, :int32, "!=") 739 when "bqneq" 740 cloopEmitCompareAndBranch(operands, :int64, "!=") 741 when "bpneq" 742 cloopEmitCompareAndBranch(operands, :int, "!=") 743 744 when "bba" 745 cloopEmitCompareAndBranch(operands, :uint8, ">") 746 when "bia" 747 cloopEmitCompareAndBranch(operands, :uint32, ">") 748 when "bqa" 749 cloopEmitCompareAndBranch(operands, :uint64, ">") 750 when "bpa" 751 cloopEmitCompareAndBranch(operands, :uint, ">") 752 753 when "bbaeq" 754 cloopEmitCompareAndBranch(operands, :uint8, ">=") 755 when "biaeq" 756 cloopEmitCompareAndBranch(operands, :uint32, ">=") 757 when "bqaeq" 758 cloopEmitCompareAndBranch(operands, :uint64, ">=") 759 when "bpaeq" 760 cloopEmitCompareAndBranch(operands, :uint, ">=") 761 762 when "bbb" 763 cloopEmitCompareAndBranch(operands, :uint8, "<") 764 when "bib" 765 cloopEmitCompareAndBranch(operands, :uint32, "<") 766 when "bqb" 767 cloopEmitCompareAndBranch(operands, :uint64, "<") 768 when "bpb" 769 cloopEmitCompareAndBranch(operands, :uint, "<") 770 771 when "bbbeq" 772 cloopEmitCompareAndBranch(operands, :uint8, "<=") 773 when "bibeq" 774 cloopEmitCompareAndBranch(operands, :uint32, "<=") 775 when "bqbeq" 776 cloopEmitCompareAndBranch(operands, :uint64, "<=") 777 when "bpbeq" 778 cloopEmitCompareAndBranch(operands, :uint, "<=") 779 780 when "bbgt" 781 cloopEmitCompareAndBranch(operands, :int8, ">") 782 when "bigt" 783 cloopEmitCompareAndBranch(operands, :int32, ">") 784 when "bqgt" 785 cloopEmitCompareAndBranch(operands, :int64, ">") 786 when "bpgt" 787 cloopEmitCompareAndBranch(operands, :int, ">") 788 789 when "bbgteq" 790 cloopEmitCompareAndBranch(operands, :int8, ">=") 791 when "bigteq" 792 cloopEmitCompareAndBranch(operands, :int32, ">=") 793 when "bqgteq" 794 cloopEmitCompareAndBranch(operands, :int64, ">=") 795 when "bpgteq" 796 cloopEmitCompareAndBranch(operands, :int, ">=") 797 798 when "bblt" 799 cloopEmitCompareAndBranch(operands, :int8, "<") 800 when "bilt" 801 cloopEmitCompareAndBranch(operands, :int32, "<") 802 when "bqlt" 803 cloopEmitCompareAndBranch(operands, :int64, "<") 804 when "bplt" 805 cloopEmitCompareAndBranch(operands, :int, "<") 806 807 when "bblteq" 808 cloopEmitCompareAndBranch(operands, :int8, "<=") 809 when "bilteq" 810 cloopEmitCompareAndBranch(operands, :int32, "<=") 811 when "bqlteq" 812 cloopEmitCompareAndBranch(operands, :int64, "<=") 813 when "bplteq" 814 cloopEmitCompareAndBranch(operands, :int, "<=") 815 816 when "btbz" 817 cloopEmitTestAndBranchIf(operands, :int8, "== 0", operands[-1].cLabel) 818 when "btiz" 819 cloopEmitTestAndBranchIf(operands, :int32, "== 0", operands[-1].cLabel) 820 when "btqz" 821 cloopEmitTestAndBranchIf(operands, :int64, "== 0", operands[-1].cLabel) 822 when "btpz" 823 cloopEmitTestAndBranchIf(operands, :int, "== 0", operands[-1].cLabel) 824 825 when "btbnz" 826 cloopEmitTestAndBranchIf(operands, :int8, "!= 0", operands[-1].cLabel) 827 when "btinz" 828 cloopEmitTestAndBranchIf(operands, :int32, "!= 0", operands[-1].cLabel) 829 when "btqnz" 830 cloopEmitTestAndBranchIf(operands, :int64, "!= 0", operands[-1].cLabel) 831 when "btpnz" 832 cloopEmitTestAndBranchIf(operands, :int, "!= 0", operands[-1].cLabel) 833 834 when "btbs" 835 cloopEmitTestAndBranchIf(operands, :int8, "< 0", operands[-1].cLabel) 836 when "btis" 837 cloopEmitTestAndBranchIf(operands, :int32, "< 0", operands[-1].cLabel) 838 when "btqs" 839 cloopEmitTestAndBranchIf(operands, :int64, "< 0", operands[-1].cLabel) 840 when "btps" 841 cloopEmitTestAndBranchIf(operands, :int, "< 0", operands[-1].cLabel) 842 843 # For jmp, we do not want to assume that we have COMPUTED_GOTO support. 844 # Fortunately, the only times we should ever encounter indirect jmps is 845 # when the jmp target is a CLoop opcode (by design). 846 # 847 # Hence, we check if the jmp target is a known label reference. If so, 848 # we can emit a goto directly. If it is not a known target, then we set 849 # the target in the opcode, and dispatch to it via whatever dispatch 850 # mechanism is in used. 851 when "jmp" 852 if operands[0].is_a? LocalLabelReference or operands[0].is_a? LabelReference 853 # Handles jumps local or global labels. 854 $asm.putc "goto #{operands[0].cLabel};" 855 else 856 # Handles jumps to some computed target. 857 # NOTE: must be an opcode handler or a llint glue helper. 858 $asm.putc "opcode = #{operands[0].clValue(:opcode)};" 859 $asm.putc "DISPATCH_OPCODE();" 860 end 861 862 when "call" 863 $asm.putc "CRASH(); // generic call instruction not supported by design!" 864 when "break" 865 $asm.putc "CRASH(); // break instruction not implemented." 866 when "ret" 867 $asm.putc "goto doReturnHelper;" 868 869 when "cbeq" 870 cloopEmitCompareAndSet(operands, :uint8, "==") 871 when "cieq" 872 cloopEmitCompareAndSet(operands, :uint32, "==") 873 when "cqeq" 874 cloopEmitCompareAndSet(operands, :uint64, "==") 875 when "cpeq" 876 cloopEmitCompareAndSet(operands, :uint, "==") 877 878 when "cbneq" 879 cloopEmitCompareAndSet(operands, :uint8, "!=") 880 when "cineq" 881 cloopEmitCompareAndSet(operands, :uint32, "!=") 882 when "cqneq" 883 cloopEmitCompareAndSet(operands, :uint64, "!=") 884 when "cpneq" 885 cloopEmitCompareAndSet(operands, :uint, "!=") 886 887 when "cba" 888 cloopEmitCompareAndSet(operands, :uint8, ">") 889 when "cia" 890 cloopEmitCompareAndSet(operands, :uint32, ">") 891 when "cqa" 892 cloopEmitCompareAndSet(operands, :uint64, ">") 893 when "cpa" 894 cloopEmitCompareAndSet(operands, :uint, ">") 895 896 when "cbaeq" 897 cloopEmitCompareAndSet(operands, :uint8, ">=") 898 when "ciaeq" 899 cloopEmitCompareAndSet(operands, :uint32, ">=") 900 when "cqaeq" 901 cloopEmitCompareAndSet(operands, :uint64, ">=") 902 when "cpaeq" 903 cloopEmitCompareAndSet(operands, :uint, ">=") 904 905 when "cbb" 906 cloopEmitCompareAndSet(operands, :uint8, "<") 907 when "cib" 908 cloopEmitCompareAndSet(operands, :uint32, "<") 909 when "cqb" 910 cloopEmitCompareAndSet(operands, :uint64, "<") 911 when "cpb" 912 cloopEmitCompareAndSet(operands, :uint, "<") 913 914 when "cbbeq" 915 cloopEmitCompareAndSet(operands, :uint8, "<=") 916 when "cibeq" 917 cloopEmitCompareAndSet(operands, :uint32, "<=") 918 when "cqbeq" 919 cloopEmitCompareAndSet(operands, :uint64, "<=") 920 when "cpbeq" 921 cloopEmitCompareAndSet(operands, :uint, "<=") 922 923 when "cbgt" 924 cloopEmitCompareAndSet(operands, :int8, ">") 925 when "cigt" 926 cloopEmitCompareAndSet(operands, :int32, ">") 927 when "cqgt" 928 cloopEmitCompareAndSet(operands, :int64, ">") 929 when "cpgt" 930 cloopEmitCompareAndSet(operands, :int, ">") 931 932 when "cbgteq" 933 cloopEmitCompareAndSet(operands, :int8, ">=") 934 when "cigteq" 935 cloopEmitCompareAndSet(operands, :int32, ">=") 936 when "cqgteq" 937 cloopEmitCompareAndSet(operands, :int64, ">=") 938 when "cpgteq" 939 cloopEmitCompareAndSet(operands, :int, ">=") 940 941 when "cblt" 942 cloopEmitCompareAndSet(operands, :int8, "<") 943 when "cilt" 944 cloopEmitCompareAndSet(operands, :int32, "<") 945 when "cqlt" 946 cloopEmitCompareAndSet(operands, :int64, "<") 947 when "cplt" 948 cloopEmitCompareAndSet(operands, :int, "<") 949 950 when "cblteq" 951 cloopEmitCompareAndSet(operands, :int8, "<=") 952 when "cilteq" 953 cloopEmitCompareAndSet(operands, :int32, "<=") 954 when "cqlteq" 955 cloopEmitCompareAndSet(operands, :int64, "<=") 956 when "cplteq" 957 cloopEmitCompareAndSet(operands, :int, "<=") 958 959 when "tbs" 960 cloopEmitTestSet(operands, :int8, "< 0") 961 when "tis" 962 cloopEmitTestSet(operands, :int32, "< 0") 963 when "tqs" 964 cloopEmitTestSet(operands, :int64, "< 0") 965 when "tps" 966 cloopEmitTestSet(operands, :int, "< 0") 967 968 when "tbz" 969 cloopEmitTestSet(operands, :int8, "== 0") 970 when "tiz" 971 cloopEmitTestSet(operands, :int32, "== 0") 972 when "tqz" 973 cloopEmitTestSet(operands, :int64, "== 0") 974 when "tpz" 975 cloopEmitTestSet(operands, :int, "== 0") 976 977 when "tbnz" 978 cloopEmitTestSet(operands, :int8, "!= 0") 979 when "tinz" 980 cloopEmitTestSet(operands, :int32, "!= 0") 981 when "tqnz" 982 cloopEmitTestSet(operands, :int64, "!= 0") 983 when "tpnz" 984 cloopEmitTestSet(operands, :int, "!= 0") 985 986 # 64-bit instruction: cdqi (based on X64) 987 # Sign extends the lower 32 bits of t0, but put the sign extension into 988 # the lower 32 bits of t1. Leave the upper 32 bits of t0 and t1 unchanged. 989 when "cdqi" 990 $asm.putc "{" 991 $asm.putc " int64_t temp = t0.i32; // sign extend the low 32bit" 992 $asm.putc " t0.i32 = temp; // low word" 993 $asm.putc " t0.clearHighWord();" 994 $asm.putc " t1.i32 = uint64_t(temp) >> 32; // high word" 995 $asm.putc " t1.clearHighWord();" 996 $asm.putc "}" 997 998 # 64-bit instruction: idivi op1 (based on X64) 999 # Divide a 64-bit integer numerator by the specified denominator. 1000 # The numerator is specified in t0 and t1 as follows: 1001 # 1. low 32 bits of the numerator is in the low 32 bits of t0. 1002 # 2. high 32 bits of the numerator is in the low 32 bits of t1. 1003 # 1004 # The resultant quotient is a signed 32-bit int, and is to be stored 1005 # in the lower 32 bits of t0. 1006 # The resultant remainder is a signed 32-bit int, and is to be stored 1007 # in the lower 32 bits of t1. 1008 when "idivi" 1009 # Divide t1,t0 (EDX,EAX) by the specified arg, and store the remainder in t1, 1010 # and quotient in t0: 1011 $asm.putc "{" 1012 $asm.putc " int64_t dividend = (int64_t(t1.u32) << 32) | t0.u32;" 1013 $asm.putc " int64_t divisor = #{operands[0].clValue(:int)};" 1014 $asm.putc " t1.i32 = dividend % divisor; // remainder" 1015 $asm.putc " t1.clearHighWord();" 1016 $asm.putc " t0.i32 = dividend / divisor; // quotient" 1017 $asm.putc " t0.clearHighWord();" 1018 $asm.putc "}" 1019 1020 # 32-bit instruction: fii2d int32LoOp int32HiOp dblOp (based on ARMv7) 1021 # Decode 2 32-bit ints (low and high) into a 64-bit double. 1022 when "fii2d" 1023 $asm.putc "#{operands[2].clValue(:double)} = Ints2Double(#{operands[0].clValue(:uint32)}, #{operands[1].clValue(:uint32)});" 1024 1025 # 32-bit instruction: f2dii dblOp int32LoOp int32HiOp (based on ARMv7) 1026 # Encode a 64-bit double into 2 32-bit ints (low and high). 1027 when "fd2ii" 1028 $asm.putc "Double2Ints(#{operands[0].clValue(:double)}, #{operands[1].clValue(:uint32)}, #{operands[2].clValue(:uint32)});" 1029 1030 # 64-bit instruction: fq2d int64Op dblOp (based on X64) 1031 # Copy a bit-encoded double in a 64-bit int register to a double register. 1032 when "fq2d" 1033 $asm.putc "#{operands[1].clValue(:double)} = #{operands[0].clValue(:castToDouble)};" 1034 1035 # 64-bit instruction: fd2q dblOp int64Op (based on X64 instruction set) 1036 # Copy a double as a bit-encoded double into a 64-bit int register. 1037 when "fd2q" 1038 $asm.putc "#{operands[1].clValue(:int64)} = #{operands[0].clValue(:castToInt64)};" 1039 1040 when "leai" 1041 operands[0].cloopEmitLea(operands[1], :int32) 1042 when "leap" 1043 operands[0].cloopEmitLea(operands[1], :int) 1044 1045 when "baddio" 1046 cloopEmitOpAndBranchIfOverflow(operands, "+", :int32) 1047 when "bsubio" 1048 cloopEmitOpAndBranchIfOverflow(operands, "-", :int32) 1049 when "bmulio" 1050 cloopEmitOpAndBranchIfOverflow(operands, "*", :int32) 1051 1052 when "baddis" 1053 cloopEmitOpAndBranch(operands, "+", :int32, "< 0") 1054 when "baddiz" 1055 cloopEmitOpAndBranch(operands, "+", :int32, "== 0") 1056 when "baddinz" 1057 cloopEmitOpAndBranch(operands, "+", :int32, "!= 0") 1058 1059 when "baddqs" 1060 cloopEmitOpAndBranch(operands, "+", :int64, "< 0") 1061 when "baddqz" 1062 cloopEmitOpAndBranch(operands, "+", :int64, "== 0") 1063 when "baddqnz" 1064 cloopEmitOpAndBranch(operands, "+", :int64, "!= 0") 1065 1066 when "baddps" 1067 cloopEmitOpAndBranch(operands, "+", :int, "< 0") 1068 when "baddpz" 1069 cloopEmitOpAndBranch(operands, "+", :int, "== 0") 1070 when "baddpnz" 1071 cloopEmitOpAndBranch(operands, "+", :int, "!= 0") 1072 1073 when "bsubis" 1074 cloopEmitOpAndBranch(operands, "-", :int32, "< 0") 1075 when "bsubiz" 1076 cloopEmitOpAndBranch(operands, "-", :int32, "== 0") 1077 when "bsubinz" 1078 cloopEmitOpAndBranch(operands, "-", :int32, "!= 0") 1079 1080 when "borris" 1081 cloopEmitOpAndBranch(operands, "|", :int32, "< 0") 1082 when "borriz" 1083 cloopEmitOpAndBranch(operands, "|", :int32, "== 0") 1084 when "borrinz" 1085 cloopEmitOpAndBranch(operands, "|", :int32, "!= 0") 1086 1087 # A convenience and compact call to crash because we don't want to use 1088 # the generic llint crash mechanism which relies on the availability 1089 # of the call instruction (which cannot be implemented in a generic 1090 # way, and can be abused if we made it just work for this special case). 1091 # Using a special cloopCrash instruction is cleaner. 1092 when "cloopCrash" 1093 $asm.putc "CRASH();" 1094 1095 # We can't rely on the llint JS call mechanism which actually makes 1096 # use of the call instruction. Instead, we just implement JS calls 1097 # as an opcode dispatch. 1098 when "cloopCallJSFunction" 1099 $asm.putc "opcode = #{operands[0].clValue(:opcode)};" 1100 $asm.putc "DISPATCH_OPCODE();" 1101 1102 # We can't do generic function calls with an arbitrary set of args, but 1103 # fortunately we don't have to here. All native function calls always 1104 # have a fixed prototype of 1 args: the passed ExecState. 1105 when "cloopCallNative" 1106 $asm.putc "nativeFunc = #{operands[0].clValue(:nativeFunc)};" 1107 $asm.putc "functionReturnValue = JSValue::decode(nativeFunc(t0.execState));" 1108 $asm.putc "#if USE(JSVALUE32_64)" 1109 $asm.putc " t1.i = functionReturnValue.tag();" 1110 $asm.putc " t0.i = functionReturnValue.payload();" 1111 $asm.putc "#else // USE_JSVALUE64)" 1112 $asm.putc " t0.encodedJSValue = JSValue::encode(functionReturnValue);" 1113 $asm.putc "#endif // USE_JSVALUE64)" 1114 1115 # We can't do generic function calls with an arbitrary set of args, but 1116 # fortunately we don't have to here. All slow path function calls always 1117 # have a fixed prototype too. See cloopEmitCallSlowPath() for details. 1118 when "cloopCallSlowPath" 1119 cloopEmitCallSlowPath(operands) 1120 1121 # For debugging only. This is used to insert instrumentation into the 1122 # generated LLIntAssembly.h during llint development only. Do not use 1123 # for production code. 1124 when "cloopDo" 1125 $asm.putc "#{annotation}" 1126 1127 else 1128 lowerDefault 1129 end 1130 end 1131end 1132