mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-14 12:59:56 +00:00
1189 lines
42 KiB
Ruby
1189 lines
42 KiB
Ruby
# Copyright (C) 2012-2020 Apple Inc. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. 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.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
|
|
|
|
require "config"
|
|
require "ast"
|
|
require "opt"
|
|
|
|
# The CLoop llint backend is initially based on the ARMv7 backend, and
|
|
# then further enhanced with a few instructions from the x86 backend to
|
|
# support building for X64 targets. Hence, the shape of the generated
|
|
# code and the usage convention of registers will look a lot like the
|
|
# ARMv7 backend's.
|
|
|
|
def cloopMapType(type)
|
|
case type
|
|
when :intptr; ".i()"
|
|
when :uintptr; ".u()"
|
|
when :int32; ".i32()"
|
|
when :uint32; ".u32()"
|
|
when :int64; ".i64()"
|
|
when :uint64; ".u64()"
|
|
when :int8; ".i8()"
|
|
when :uint8; ".u8()"
|
|
when :int8Ptr; ".i8p()"
|
|
when :voidPtr; ".vp()"
|
|
when :nativeFunc; ".nativeFunc()"
|
|
when :double; ".d()"
|
|
when :bitsAsDouble; ".bitsAsDouble()"
|
|
when :bitsAsInt64; ".bitsAsInt64()"
|
|
when :opcode; ".opcode()"
|
|
else;
|
|
raise "Unsupported type"
|
|
end
|
|
end
|
|
|
|
|
|
class SpecialRegister < NoChildren
|
|
def clLValue(type=:intptr)
|
|
clDump
|
|
end
|
|
def clDump
|
|
@name
|
|
end
|
|
def clValue(type=:intptr)
|
|
@name + cloopMapType(type)
|
|
end
|
|
end
|
|
|
|
C_LOOP_SCRATCH_FPR = SpecialRegister.new("d6")
|
|
|
|
class RegisterID
|
|
def clDump
|
|
case name
|
|
# The cloop is modelled on the ARM implementation. Hence, the a0-a3
|
|
# registers are aliases for r0-r3 i.e. t0-t3 in our case.
|
|
when "t0", "a0", "r0"
|
|
"t0"
|
|
when "t1", "a1", "r1"
|
|
"t1"
|
|
when "t2", "a2"
|
|
"t2"
|
|
when "t3", "a3"
|
|
"t3"
|
|
when "t4"
|
|
"pc"
|
|
when "t5"
|
|
"t5"
|
|
when "csr0"
|
|
"pcBase"
|
|
when "csr1"
|
|
"numberTag"
|
|
when "csr2"
|
|
"notCellMask"
|
|
when "csr3"
|
|
"metadataTable"
|
|
when "cfr"
|
|
"cfr"
|
|
when "lr"
|
|
"lr"
|
|
when "sp"
|
|
"sp"
|
|
else
|
|
raise "Bad register #{name} for C_LOOP at #{codeOriginString}"
|
|
end
|
|
end
|
|
def clLValue(type=:intptr)
|
|
clDump
|
|
end
|
|
def clValue(type=:intptr)
|
|
clDump + cloopMapType(type)
|
|
end
|
|
end
|
|
|
|
class FPRegisterID
|
|
def clDump
|
|
case name
|
|
when "ft0", "fr"
|
|
"d0"
|
|
when "ft1"
|
|
"d1"
|
|
when "ft2"
|
|
"d2"
|
|
when "ft3"
|
|
"d3"
|
|
when "ft4"
|
|
"d4"
|
|
when "ft5"
|
|
"d5"
|
|
else
|
|
raise "Bad register #{name} for C_LOOP at #{codeOriginString}"
|
|
end
|
|
end
|
|
def clLValue(type=:intptr)
|
|
clDump
|
|
end
|
|
def clValue(type=:intptr)
|
|
clDump + cloopMapType(type)
|
|
end
|
|
end
|
|
|
|
class Immediate
|
|
def clDump
|
|
"#{value}"
|
|
end
|
|
def clLValue(type=:intptr)
|
|
raise "Immediate cannot be used as an LValue"
|
|
end
|
|
def clValue(type=:intptr)
|
|
# There is a case of a very large unsigned number (0x8000000000000000)
|
|
# which we wish to encode. Unfortunately, the C/C++ compiler
|
|
# complains if we express that number as a positive decimal integer.
|
|
# Hence, for positive values, we just convert the number into hex form
|
|
# to keep the compiler happy.
|
|
#
|
|
# However, for negative values, the to_s(16) hex conversion method does
|
|
# not strip the "-" sign resulting in a meaningless "0x-..." valueStr.
|
|
# To workaround this, we simply don't encode negative numbers as hex.
|
|
|
|
valueStr = (value < 0) ? "#{value}" : "0x#{value.to_s(16)}"
|
|
|
|
case type
|
|
when :int8; "int8_t(#{valueStr})"
|
|
when :int16; "int16_t(#{valueStr})"
|
|
when :int32; "int32_t(#{valueStr})"
|
|
when :int64; "int64_t(#{valueStr})"
|
|
when :intptr; "intptr_t(#{valueStr})"
|
|
when :uint8; "uint8_t(#{valueStr})"
|
|
when :uint32; "uint32_t(#{valueStr})"
|
|
when :uint64; "uint64_t(#{valueStr})"
|
|
when :uintptr; "uintptr_t(#{valueStr})"
|
|
else
|
|
raise "Not implemented immediate of type: #{type}"
|
|
end
|
|
end
|
|
end
|
|
|
|
class Address
|
|
def clDump
|
|
"[#{base.clDump}, #{offset.value}]"
|
|
end
|
|
def clLValue(type=:intptr)
|
|
clValue(type)
|
|
end
|
|
def clValue(type=:intptr)
|
|
case type
|
|
when :int8; int8MemRef
|
|
when :int16; int16MemRef
|
|
when :int32; int32MemRef
|
|
when :int64; int64MemRef
|
|
when :intptr; intptrMemRef
|
|
when :uint8; uint8MemRef
|
|
when :uint32; uint32MemRef
|
|
when :uint64; uint64MemRef
|
|
when :uintptr; uintptrMemRef
|
|
when :opcode; opcodeMemRef
|
|
when :nativeFunc; nativeFuncMemRef
|
|
else
|
|
raise "Unexpected Address type: #{type}"
|
|
end
|
|
end
|
|
def pointerExpr
|
|
if offset.value == 0
|
|
"#{base.clValue(:int8Ptr)}"
|
|
elsif offset.value > 0
|
|
"#{base.clValue(:int8Ptr)} + #{offset.value}"
|
|
else
|
|
"#{base.clValue(:int8Ptr)} - #{-offset.value}"
|
|
end
|
|
end
|
|
def int8MemRef
|
|
"*CAST<int8_t*>(#{pointerExpr})"
|
|
end
|
|
def int16MemRef
|
|
"*CAST<int16_t*>(#{pointerExpr})"
|
|
end
|
|
def int32MemRef
|
|
"*CAST<int32_t*>(#{pointerExpr})"
|
|
end
|
|
def int64MemRef
|
|
"*CAST<int64_t*>(#{pointerExpr})"
|
|
end
|
|
def intptrMemRef
|
|
"*CAST<intptr_t*>(#{pointerExpr})"
|
|
end
|
|
def uint8MemRef
|
|
"*CAST<uint8_t*>(#{pointerExpr})"
|
|
end
|
|
def uint16MemRef
|
|
"*CAST<uint16_t*>(#{pointerExpr})"
|
|
end
|
|
def uint32MemRef
|
|
"*CAST<uint32_t*>(#{pointerExpr})"
|
|
end
|
|
def uint64MemRef
|
|
"*CAST<uint64_t*>(#{pointerExpr})"
|
|
end
|
|
def uintptrMemRef
|
|
"*CAST<uintptr_t*>(#{pointerExpr})"
|
|
end
|
|
def nativeFuncMemRef
|
|
"*CAST<NativeFunction*>(#{pointerExpr})"
|
|
end
|
|
def opcodeMemRef
|
|
"*CAST<Opcode*>(#{pointerExpr})"
|
|
end
|
|
def dblMemRef
|
|
"*CAST<double*>(#{pointerExpr})"
|
|
end
|
|
end
|
|
|
|
class BaseIndex
|
|
def clDump
|
|
"[#{base.clDump}, #{offset.clDump}, #{index.clDump} << #{scaleShift}]"
|
|
end
|
|
def clLValue(type=:intptr)
|
|
clValue(type)
|
|
end
|
|
def clValue(type=:intptr)
|
|
case type
|
|
when :int8; int8MemRef
|
|
when :int32; int32MemRef
|
|
when :int64; int64MemRef
|
|
when :intptr; intptrMemRef
|
|
when :uint8; uint8MemRef
|
|
when :uint32; uint32MemRef
|
|
when :uint64; uint64MemRef
|
|
when :uintptr; uintptrMemRef
|
|
when :opcode; opcodeMemRef
|
|
else
|
|
raise "Unexpected BaseIndex type: #{type}"
|
|
end
|
|
end
|
|
def pointerExpr
|
|
if offset.value == 0
|
|
"#{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift})"
|
|
else
|
|
"#{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift}) + #{offset.clValue}"
|
|
end
|
|
end
|
|
def int8MemRef
|
|
"*CAST<int8_t*>(#{pointerExpr})"
|
|
end
|
|
def int16MemRef
|
|
"*CAST<int16_t*>(#{pointerExpr})"
|
|
end
|
|
def int32MemRef
|
|
"*CAST<int32_t*>(#{pointerExpr})"
|
|
end
|
|
def int64MemRef
|
|
"*CAST<int64_t*>(#{pointerExpr})"
|
|
end
|
|
def intptrMemRef
|
|
"*CAST<intptr_t*>(#{pointerExpr})"
|
|
end
|
|
def uint8MemRef
|
|
"*CAST<uint8_t*>(#{pointerExpr})"
|
|
end
|
|
def uint16MemRef
|
|
"*CAST<uint16_t*>(#{pointerExpr})"
|
|
end
|
|
def uint32MemRef
|
|
"*CAST<uint32_t*>(#{pointerExpr})"
|
|
end
|
|
def uint64MemRef
|
|
"*CAST<uint64_t*>(#{pointerExpr})"
|
|
end
|
|
def uintptrMemRef
|
|
"*CAST<uintptr_t*>(#{pointerExpr})"
|
|
end
|
|
def opcodeMemRef
|
|
"*CAST<Opcode*>(#{pointerExpr})"
|
|
end
|
|
def dblMemRef
|
|
"*CAST<double*>(#{pointerExpr})"
|
|
end
|
|
end
|
|
|
|
class AbsoluteAddress
|
|
def clDump
|
|
"#{codeOriginString}"
|
|
end
|
|
def clLValue(type=:intptr)
|
|
clValue(type)
|
|
end
|
|
def clValue
|
|
clDump
|
|
end
|
|
end
|
|
|
|
class LabelReference
|
|
def intptrMemRef
|
|
"*CAST<intptr_t*>(&#{cLabel})"
|
|
end
|
|
def cloopEmitLea(destination, type)
|
|
$asm.putc "#{destination.clLValue(:voidPtr)} = CAST<void*>(&#{cLabel});"
|
|
if offset != 0
|
|
$asm.putc "#{destination.clLValue(:int8Ptr)} = #{destination.clValue(:int8Ptr)} + #{offset};"
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
#
|
|
# Lea support.
|
|
#
|
|
|
|
class Address
|
|
def cloopEmitLea(destination, type)
|
|
if destination == base
|
|
$asm.putc "#{destination.clLValue(:int8Ptr)} = #{destination.clValue(:int8Ptr)} + #{offset.clValue(type)};"
|
|
else
|
|
$asm.putc "#{destination.clLValue(:int8Ptr)} = #{base.clValue(:int8Ptr)} + #{offset.clValue(type)};"
|
|
end
|
|
end
|
|
end
|
|
|
|
class BaseIndex
|
|
def cloopEmitLea(destination, type)
|
|
raise "Malformed BaseIndex, offset should be zero at #{codeOriginString}" unless offset.value == 0
|
|
$asm.putc "#{destination.clLValue(:int8Ptr)} = #{base.clValue(:int8Ptr)} + (#{index.clValue} << #{scaleShift});"
|
|
end
|
|
end
|
|
|
|
#
|
|
# Actual lowering code follows.
|
|
#
|
|
|
|
class Sequence
|
|
def getModifiedListC_LOOP
|
|
myList = @list
|
|
|
|
# Verify that we will only see instructions and labels.
|
|
myList.each {
|
|
| node |
|
|
unless node.is_a? Instruction or
|
|
node.is_a? Label or
|
|
node.is_a? LocalLabel or
|
|
node.is_a? Skip
|
|
raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
|
|
end
|
|
}
|
|
|
|
return myList
|
|
end
|
|
end
|
|
|
|
def clOperands(operands)
|
|
operands.map{|v| v.clDump}.join(", ")
|
|
end
|
|
|
|
|
|
def cloopEmitOperation(operands, type, operator)
|
|
raise unless type == :intptr || type == :uintptr || type == :int32 || type == :uint32 || \
|
|
type == :int64 || type == :uint64 || type == :double || type == :int16
|
|
if operands.size == 3
|
|
op1 = operands[0]
|
|
op2 = operands[1]
|
|
dst = operands[2]
|
|
else
|
|
raise unless operands.size == 2
|
|
op1 = operands[1]
|
|
op2 = operands[0]
|
|
dst = operands[1]
|
|
end
|
|
raise unless not dst.is_a? Immediate
|
|
if dst.is_a? RegisterID and (type == :int32 or type == :uint32)
|
|
truncationHeader = "(uint32_t)("
|
|
truncationFooter = ")"
|
|
elsif dst.is_a? RegisterID and (type == :int16)
|
|
truncationHeader = "(uint16_t)("
|
|
truncationFooter = ")"
|
|
else
|
|
truncationHeader = ""
|
|
truncationFooter = ""
|
|
end
|
|
$asm.putc "#{dst.clLValue(type)} = #{truncationHeader}#{op1.clValue(type)} #{operator} #{op2.clValue(type)}#{truncationFooter};"
|
|
end
|
|
|
|
def cloopEmitShiftOperation(operands, type, operator)
|
|
raise unless type == :intptr || type == :uintptr || type == :int32 || type == :uint32 || type == :int64 || type == :uint64
|
|
if operands.size == 3
|
|
op1 = operands[0]
|
|
op2 = operands[1]
|
|
dst = operands[2]
|
|
else
|
|
op1 = operands[1]
|
|
op2 = operands[0]
|
|
dst = operands[1]
|
|
end
|
|
if dst.is_a? RegisterID and (type == :int32 or type == :uint32)
|
|
truncationHeader = "(uint32_t)("
|
|
truncationFooter = ")"
|
|
else
|
|
truncationHeader = ""
|
|
truncationFooter = ""
|
|
end
|
|
shiftMask = "((sizeof(uintptr_t) == 8) ? 0x3f : 0x1f)" if type == :intptr || type == :uintptr
|
|
shiftMask = "0x3f" if type == :int64 || type == :uint64
|
|
shiftMask = "0x1f" if type == :int32 || type == :uint32
|
|
$asm.putc "#{dst.clLValue(type)} = #{truncationHeader}#{operands[1].clValue(type)} #{operator} (#{operands[0].clValue(:intptr)} & #{shiftMask})#{truncationFooter};"
|
|
end
|
|
|
|
def cloopEmitUnaryOperation(operands, type, operator)
|
|
raise unless type == :intptr || type == :uintptr || type == :int32 || type == :uint32 || type == :int64 || type == :uint64
|
|
raise unless operands.size == 1
|
|
raise unless not operands[0].is_a? Immediate
|
|
op = operands[0]
|
|
dst = operands[0]
|
|
if dst.is_a? RegisterID and (type == :int32 or type == :uint32)
|
|
truncationHeader = "(uint32_t)("
|
|
truncationFooter = ")"
|
|
else
|
|
truncationHeader = ""
|
|
truncationFooter = ""
|
|
end
|
|
$asm.putc "#{dst.clLValue(type)} = #{truncationHeader}#{operator}#{op.clValue(type)}#{truncationFooter};"
|
|
end
|
|
|
|
def cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, condition)
|
|
$asm.putc "if (std::isnan(#{operands[0].clValue(:double)}) || std::isnan(#{operands[1].clValue(:double)})"
|
|
$asm.putc " || (#{operands[0].clValue(:double)} #{condition} #{operands[1].clValue(:double)}))"
|
|
$asm.putc " goto #{operands[2].cLabel};"
|
|
end
|
|
|
|
|
|
def cloopEmitCompareAndSet(operands, type, comparator)
|
|
# The result is a boolean. Hence, it doesn't need to be based on the type
|
|
# of the arguments being compared.
|
|
$asm.putc "#{operands[2].clLValue(type)} = (#{operands[0].clValue(type)} #{comparator} #{operands[1].clValue(type)});"
|
|
end
|
|
|
|
|
|
def cloopEmitCompareAndBranch(operands, type, comparator)
|
|
$asm.putc "if (#{operands[0].clValue(type)} #{comparator} #{operands[1].clValue(type)})"
|
|
$asm.putc " goto #{operands[2].cLabel};"
|
|
end
|
|
|
|
|
|
# conditionTest should contain a string that provides a comparator and a RHS
|
|
# value e.g. "< 0".
|
|
def cloopGenerateConditionExpression(operands, type, conditionTest)
|
|
op1 = operands[0].clValue(type)
|
|
|
|
# The operands must consist of 2 or 3 values.
|
|
case operands.size
|
|
when 2 # Just test op1 against the conditionTest.
|
|
lhs = op1
|
|
when 3 # Mask op1 with op2 before testing against the conditionTest.
|
|
lhs = "(#{op1} & #{operands[1].clValue(type)})"
|
|
else
|
|
raise "Expected 2 or 3 operands but got #{operands.size} at #{codeOriginString}"
|
|
end
|
|
|
|
"#{lhs} #{conditionTest}"
|
|
end
|
|
|
|
# conditionTest should contain a string that provides a comparator and a RHS
|
|
# value e.g. "< 0".
|
|
def cloopEmitTestAndBranchIf(operands, type, conditionTest, branchTarget)
|
|
conditionExpr = cloopGenerateConditionExpression(operands, type, conditionTest)
|
|
$asm.putc "if (#{conditionExpr})"
|
|
$asm.putc " goto #{branchTarget};"
|
|
end
|
|
|
|
def cloopEmitTestSet(operands, type, conditionTest)
|
|
# The result is a boolean condition. Hence, the result type is always an
|
|
# int. The passed in type is only used for the values being tested in
|
|
# the condition test.
|
|
conditionExpr = cloopGenerateConditionExpression(operands, type, conditionTest)
|
|
$asm.putc "#{operands[-1].clLValue} = (#{conditionExpr});"
|
|
end
|
|
|
|
def cloopEmitOpAndBranch(operands, operator, type, conditionTest)
|
|
case type
|
|
when :intptr; tempType = "intptr_t"
|
|
when :int32; tempType = "int32_t"
|
|
when :int64; tempType = "int64_t"
|
|
else
|
|
raise "Unimplemented type"
|
|
end
|
|
|
|
$asm.putc "{"
|
|
$asm.putc " #{tempType} temp = #{operands[1].clValue(type)} #{operator} #{operands[0].clValue(type)};"
|
|
$asm.putc " #{operands[1].clLValue(type)} = temp;"
|
|
$asm.putc " if (temp #{conditionTest})"
|
|
$asm.putc " goto #{operands[2].cLabel};"
|
|
$asm.putc "}"
|
|
end
|
|
|
|
def cloopEmitOpAndBranchIfOverflow(operands, operator, type)
|
|
case type
|
|
when :int32
|
|
tempType = "int32_t"
|
|
truncationHeader = "(uint32_t)("
|
|
truncationFooter = ")"
|
|
else
|
|
raise "Unimplemented type"
|
|
end
|
|
|
|
$asm.putc "{"
|
|
|
|
# Emit the overflow test based on the operands and the type:
|
|
case operator
|
|
when "+"; operation = "add"
|
|
when "-"; operation = "sub"
|
|
when "*"; operation = "multiply"
|
|
else
|
|
raise "Unimplemented opeartor"
|
|
end
|
|
|
|
$asm.putc " #{tempType} result;"
|
|
$asm.putc " bool success = WTF::ArithmeticOperations<#{tempType}, #{tempType}, #{tempType}>::#{operation}(#{operands[1].clValue(type)}, #{operands[0].clValue(type)}, result);"
|
|
$asm.putc " #{operands[1].clLValue(type)} = #{truncationHeader}result#{truncationFooter};"
|
|
$asm.putc " if (!success)"
|
|
$asm.putc " goto #{operands[2].cLabel};"
|
|
$asm.putc "}"
|
|
end
|
|
|
|
# operands: callTarget, currentFrame, currentPC
|
|
def cloopEmitCallSlowPath(operands)
|
|
$asm.putc "{"
|
|
$asm.putc " cloopStack.setCurrentStackPointer(sp.vp());"
|
|
$asm.putc " SlowPathReturnType result = #{operands[0].cLabel}(#{operands[1].clDump}, #{operands[2].clDump});"
|
|
$asm.putc " decodeResult(result, t0, t1);"
|
|
$asm.putc "}"
|
|
end
|
|
|
|
def cloopEmitCallSlowPathVoid(operands)
|
|
$asm.putc "cloopStack.setCurrentStackPointer(sp.vp());"
|
|
$asm.putc "#{operands[0].cLabel}(#{operands[1].clDump}, #{operands[2].clDump});"
|
|
end
|
|
|
|
class Instruction
|
|
def lowerC_LOOP
|
|
case opcode
|
|
when "addi"
|
|
cloopEmitOperation(operands, :int32, "+")
|
|
when "addq"
|
|
cloopEmitOperation(operands, :int64, "+")
|
|
when "addp"
|
|
cloopEmitOperation(operands, :intptr, "+")
|
|
|
|
when "andi"
|
|
cloopEmitOperation(operands, :int32, "&")
|
|
when "andq"
|
|
cloopEmitOperation(operands, :int64, "&")
|
|
when "andp"
|
|
cloopEmitOperation(operands, :intptr, "&")
|
|
|
|
when "ori"
|
|
cloopEmitOperation(operands, :int32, "|")
|
|
when "orq"
|
|
cloopEmitOperation(operands, :int64, "|")
|
|
when "orp"
|
|
cloopEmitOperation(operands, :intptr, "|")
|
|
when "orh"
|
|
cloopEmitOperation(operands, :int16, "|")
|
|
|
|
when "xori"
|
|
cloopEmitOperation(operands, :int32, "^")
|
|
when "xorq"
|
|
cloopEmitOperation(operands, :int64, "^")
|
|
when "xorp"
|
|
cloopEmitOperation(operands, :intptr, "^")
|
|
|
|
when "lshifti"
|
|
cloopEmitShiftOperation(operands, :int32, "<<")
|
|
when "lshiftq"
|
|
cloopEmitShiftOperation(operands, :int64, "<<")
|
|
when "lshiftp"
|
|
cloopEmitShiftOperation(operands, :intptr, "<<")
|
|
|
|
when "rshifti"
|
|
cloopEmitShiftOperation(operands, :int32, ">>")
|
|
when "rshiftq"
|
|
cloopEmitShiftOperation(operands, :int64, ">>")
|
|
when "rshiftp"
|
|
cloopEmitShiftOperation(operands, :intptr, ">>")
|
|
|
|
when "urshifti"
|
|
cloopEmitShiftOperation(operands, :uint32, ">>")
|
|
when "urshiftq"
|
|
cloopEmitShiftOperation(operands, :uint64, ">>")
|
|
when "urshiftp"
|
|
cloopEmitShiftOperation(operands, :uintptr, ">>")
|
|
|
|
when "muli"
|
|
cloopEmitOperation(operands, :int32, "*")
|
|
when "mulq"
|
|
cloopEmitOperation(operands, :int64, "*")
|
|
when "mulp"
|
|
cloopEmitOperation(operands, :intptr, "*")
|
|
|
|
when "subi"
|
|
cloopEmitOperation(operands, :int32, "-")
|
|
when "subq"
|
|
cloopEmitOperation(operands, :int64, "-")
|
|
when "subp"
|
|
cloopEmitOperation(operands, :intptr, "-")
|
|
|
|
when "negi"
|
|
cloopEmitUnaryOperation(operands, :int32, "-")
|
|
when "negq"
|
|
cloopEmitUnaryOperation(operands, :int64, "-")
|
|
when "negp"
|
|
cloopEmitUnaryOperation(operands, :intptr, "-")
|
|
|
|
when "noti"
|
|
cloopEmitUnaryOperation(operands, :int32, "~")
|
|
|
|
when "loadi"
|
|
$asm.putc "#{operands[1].clLValue(:uint32)} = #{operands[0].uint32MemRef};"
|
|
# There's no need to call clearHighWord() here because the above will
|
|
# automatically take care of 0 extension.
|
|
when "loadis"
|
|
$asm.putc "#{operands[1].clLValue(:int32)} = #{operands[0].int32MemRef};"
|
|
when "loadq"
|
|
$asm.putc "#{operands[1].clLValue(:int64)} = #{operands[0].int64MemRef};"
|
|
when "loadp"
|
|
$asm.putc "#{operands[1].clLValue} = #{operands[0].intptrMemRef};"
|
|
when "storei"
|
|
$asm.putc "#{operands[1].int32MemRef} = #{operands[0].clValue(:int32)};"
|
|
when "storeq"
|
|
$asm.putc "#{operands[1].int64MemRef} = #{operands[0].clValue(:int64)};"
|
|
when "storep"
|
|
$asm.putc "#{operands[1].intptrMemRef} = #{operands[0].clValue(:intptr)};"
|
|
when "loadb"
|
|
$asm.putc "#{operands[1].clLValue(:intptr)} = #{operands[0].uint8MemRef};"
|
|
when "loadbsi"
|
|
$asm.putc "#{operands[1].clLValue(:uint32)} = (uint32_t)((int32_t)#{operands[0].int8MemRef});"
|
|
when "loadbsq"
|
|
$asm.putc "#{operands[1].clLValue(:uint64)} = (int64_t)#{operands[0].int8MemRef};"
|
|
when "storeb"
|
|
$asm.putc "#{operands[1].uint8MemRef} = #{operands[0].clValue(:int8)};"
|
|
when "loadh"
|
|
$asm.putc "#{operands[1].clLValue(:intptr)} = #{operands[0].uint16MemRef};"
|
|
when "loadhsi"
|
|
$asm.putc "#{operands[1].clLValue(:uint32)} = (uint32_t)((int32_t)#{operands[0].int16MemRef});"
|
|
when "loadhsq"
|
|
$asm.putc "#{operands[1].clLValue(:uint64)} = (int64_t)#{operands[0].int16MemRef};"
|
|
when "storeh"
|
|
$asm.putc "*#{operands[1].uint16MemRef} = #{operands[0].clValue(:int16)};"
|
|
when "loadd"
|
|
$asm.putc "#{operands[1].clLValue(:double)} = #{operands[0].dblMemRef};"
|
|
when "stored"
|
|
$asm.putc "#{operands[1].dblMemRef} = #{operands[0].clValue(:double)};"
|
|
|
|
when "addd"
|
|
cloopEmitOperation(operands, :double, "+")
|
|
when "divd"
|
|
cloopEmitOperation(operands, :double, "/")
|
|
when "subd"
|
|
cloopEmitOperation(operands, :double, "-")
|
|
when "muld"
|
|
cloopEmitOperation(operands, :double, "*")
|
|
|
|
# Convert an int value to its double equivalent, and store it in a double register.
|
|
when "ci2ds"
|
|
$asm.putc "#{operands[1].clLValue(:double)} = (double)#{operands[0].clValue(:int32)}; // ci2ds"
|
|
|
|
when "bdeq"
|
|
cloopEmitCompareAndBranch(operands, :double, "==")
|
|
when "bdneq"
|
|
cloopEmitCompareAndBranch(operands, :double, "!=")
|
|
when "bdgt"
|
|
cloopEmitCompareAndBranch(operands, :double, ">");
|
|
when "bdgteq"
|
|
cloopEmitCompareAndBranch(operands, :double, ">=");
|
|
when "bdlt"
|
|
cloopEmitCompareAndBranch(operands, :double, "<");
|
|
when "bdlteq"
|
|
cloopEmitCompareAndBranch(operands, :double, "<=");
|
|
|
|
when "bdequn"
|
|
cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "==")
|
|
when "bdnequn"
|
|
cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "!=")
|
|
when "bdgtun"
|
|
cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, ">")
|
|
when "bdgtequn"
|
|
cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, ">=")
|
|
when "bdltun"
|
|
cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "<")
|
|
when "bdltequn"
|
|
cloopEmitCompareDoubleWithNaNCheckAndBranch(operands, "<=")
|
|
|
|
when "td2i"
|
|
$asm.putc "#{operands[1].clLValue(:intptr)} = (uint32_t)(intptr_t)#{operands[0].clValue(:double)}; // td2i"
|
|
|
|
when "bcd2i" # operands: srcDbl dstInt slowPath
|
|
$asm.putc "{ // bcd2i"
|
|
$asm.putc " double d = #{operands[0].clValue(:double)};"
|
|
$asm.putc " const int32_t asInt32 = int32_t(d);"
|
|
$asm.putc " if (asInt32 != d || (!asInt32 && std::signbit(d))) // true for -0.0"
|
|
$asm.putc " goto #{operands[2].cLabel};"
|
|
$asm.putc " #{operands[1].clLValue} = (uint32_t)asInt32;"
|
|
$asm.putc "}"
|
|
|
|
when "move"
|
|
$asm.putc "#{operands[1].clLValue(:intptr)} = #{operands[0].clValue(:intptr)};"
|
|
when "sxi2q"
|
|
$asm.putc "#{operands[1].clLValue(:int64)} = #{operands[0].clValue(:int32)};"
|
|
when "zxi2q"
|
|
$asm.putc "#{operands[1].clLValue(:uint64)} = #{operands[0].clValue(:uint32)};"
|
|
when "sxb2i"
|
|
$asm.putc "#{operands[1].clLValue(:int32)} = #{operands[0].clValue(:int8)};"
|
|
when "sxh2i"
|
|
$asm.putc "#{operands[1].clLValue(:int32)} = #{operands[0].clValue(:int16)};"
|
|
when "sxb2q"
|
|
$asm.putc "#{operands[1].clLValue(:int64)} = #{operands[0].clValue(:int8)};"
|
|
when "sxh2q"
|
|
$asm.putc "#{operands[1].clLValue(:int64)} = #{operands[0].clValue(:int16)};"
|
|
when "nop"
|
|
$asm.putc "// nop"
|
|
when "bbeq"
|
|
cloopEmitCompareAndBranch(operands, :int8, "==")
|
|
when "bieq"
|
|
cloopEmitCompareAndBranch(operands, :int32, "==")
|
|
when "bqeq"
|
|
cloopEmitCompareAndBranch(operands, :int64, "==")
|
|
when "bpeq"
|
|
cloopEmitCompareAndBranch(operands, :intptr, "==")
|
|
|
|
when "bbneq"
|
|
cloopEmitCompareAndBranch(operands, :int8, "!=")
|
|
when "bineq"
|
|
cloopEmitCompareAndBranch(operands, :int32, "!=")
|
|
when "bqneq"
|
|
cloopEmitCompareAndBranch(operands, :int64, "!=")
|
|
when "bpneq"
|
|
cloopEmitCompareAndBranch(operands, :intptr, "!=")
|
|
|
|
when "bba"
|
|
cloopEmitCompareAndBranch(operands, :uint8, ">")
|
|
when "bia"
|
|
cloopEmitCompareAndBranch(operands, :uint32, ">")
|
|
when "bqa"
|
|
cloopEmitCompareAndBranch(operands, :uint64, ">")
|
|
when "bpa"
|
|
cloopEmitCompareAndBranch(operands, :uintptr, ">")
|
|
|
|
when "bbaeq"
|
|
cloopEmitCompareAndBranch(operands, :uint8, ">=")
|
|
when "biaeq"
|
|
cloopEmitCompareAndBranch(operands, :uint32, ">=")
|
|
when "bqaeq"
|
|
cloopEmitCompareAndBranch(operands, :uint64, ">=")
|
|
when "bpaeq"
|
|
cloopEmitCompareAndBranch(operands, :uintptr, ">=")
|
|
|
|
when "bbb"
|
|
cloopEmitCompareAndBranch(operands, :uint8, "<")
|
|
when "bib"
|
|
cloopEmitCompareAndBranch(operands, :uint32, "<")
|
|
when "bqb"
|
|
cloopEmitCompareAndBranch(operands, :uint64, "<")
|
|
when "bpb"
|
|
cloopEmitCompareAndBranch(operands, :uintptr, "<")
|
|
|
|
when "bbbeq"
|
|
cloopEmitCompareAndBranch(operands, :uint8, "<=")
|
|
when "bibeq"
|
|
cloopEmitCompareAndBranch(operands, :uint32, "<=")
|
|
when "bqbeq"
|
|
cloopEmitCompareAndBranch(operands, :uint64, "<=")
|
|
when "bpbeq"
|
|
cloopEmitCompareAndBranch(operands, :uintptr, "<=")
|
|
|
|
when "bbgt"
|
|
cloopEmitCompareAndBranch(operands, :int8, ">")
|
|
when "bigt"
|
|
cloopEmitCompareAndBranch(operands, :int32, ">")
|
|
when "bqgt"
|
|
cloopEmitCompareAndBranch(operands, :int64, ">")
|
|
when "bpgt"
|
|
cloopEmitCompareAndBranch(operands, :intptr, ">")
|
|
|
|
when "bbgteq"
|
|
cloopEmitCompareAndBranch(operands, :int8, ">=")
|
|
when "bigteq"
|
|
cloopEmitCompareAndBranch(operands, :int32, ">=")
|
|
when "bqgteq"
|
|
cloopEmitCompareAndBranch(operands, :int64, ">=")
|
|
when "bpgteq"
|
|
cloopEmitCompareAndBranch(operands, :intptr, ">=")
|
|
|
|
when "bblt"
|
|
cloopEmitCompareAndBranch(operands, :int8, "<")
|
|
when "bilt"
|
|
cloopEmitCompareAndBranch(operands, :int32, "<")
|
|
when "bqlt"
|
|
cloopEmitCompareAndBranch(operands, :int64, "<")
|
|
when "bplt"
|
|
cloopEmitCompareAndBranch(operands, :intptr, "<")
|
|
|
|
when "bblteq"
|
|
cloopEmitCompareAndBranch(operands, :int8, "<=")
|
|
when "bilteq"
|
|
cloopEmitCompareAndBranch(operands, :int32, "<=")
|
|
when "bqlteq"
|
|
cloopEmitCompareAndBranch(operands, :int64, "<=")
|
|
when "bplteq"
|
|
cloopEmitCompareAndBranch(operands, :intptr, "<=")
|
|
|
|
when "btbz"
|
|
cloopEmitTestAndBranchIf(operands, :int8, "== 0", operands[-1].cLabel)
|
|
when "btiz"
|
|
cloopEmitTestAndBranchIf(operands, :int32, "== 0", operands[-1].cLabel)
|
|
when "btqz"
|
|
cloopEmitTestAndBranchIf(operands, :int64, "== 0", operands[-1].cLabel)
|
|
when "btpz"
|
|
cloopEmitTestAndBranchIf(operands, :intptr, "== 0", operands[-1].cLabel)
|
|
|
|
when "btbnz"
|
|
cloopEmitTestAndBranchIf(operands, :int8, "!= 0", operands[-1].cLabel)
|
|
when "btinz"
|
|
cloopEmitTestAndBranchIf(operands, :int32, "!= 0", operands[-1].cLabel)
|
|
when "btqnz"
|
|
cloopEmitTestAndBranchIf(operands, :int64, "!= 0", operands[-1].cLabel)
|
|
when "btpnz"
|
|
cloopEmitTestAndBranchIf(operands, :intptr, "!= 0", operands[-1].cLabel)
|
|
|
|
when "btbs"
|
|
cloopEmitTestAndBranchIf(operands, :int8, "< 0", operands[-1].cLabel)
|
|
when "btis"
|
|
cloopEmitTestAndBranchIf(operands, :int32, "< 0", operands[-1].cLabel)
|
|
when "btqs"
|
|
cloopEmitTestAndBranchIf(operands, :int64, "< 0", operands[-1].cLabel)
|
|
when "btps"
|
|
cloopEmitTestAndBranchIf(operands, :intptr, "< 0", operands[-1].cLabel)
|
|
|
|
# For jmp, we do not want to assume that we have COMPUTED_GOTO support.
|
|
# Fortunately, the only times we should ever encounter indirect jmps is
|
|
# when the jmp target is a CLoop opcode (by design).
|
|
#
|
|
# Hence, we check if the jmp target is a known label reference. If so,
|
|
# we can emit a goto directly. If it is not a known target, then we set
|
|
# the target in the opcode, and dispatch to it via whatever dispatch
|
|
# mechanism is in used.
|
|
when "jmp"
|
|
if operands[0].is_a? LocalLabelReference or operands[0].is_a? LabelReference
|
|
# Handles jumps local or global labels.
|
|
$asm.putc "goto #{operands[0].cLabel};"
|
|
else
|
|
# Handles jumps to some computed target.
|
|
# NOTE: must be an opcode handler or a llint glue helper.
|
|
$asm.putc "opcode = #{operands[0].clValue(:opcode)};"
|
|
$asm.putc "DISPATCH_OPCODE();"
|
|
end
|
|
|
|
when "call"
|
|
$asm.putc "CRASH(); // generic call instruction not supported by design!"
|
|
when "break"
|
|
$asm.putc "CRASH(); // break instruction not implemented."
|
|
when "ret"
|
|
$asm.putc "opcode = lr.opcode();"
|
|
$asm.putc "DISPATCH_OPCODE();"
|
|
|
|
when "cbeq"
|
|
cloopEmitCompareAndSet(operands, :uint8, "==")
|
|
when "cieq"
|
|
cloopEmitCompareAndSet(operands, :uint32, "==")
|
|
when "cqeq"
|
|
cloopEmitCompareAndSet(operands, :uint64, "==")
|
|
when "cpeq"
|
|
cloopEmitCompareAndSet(operands, :uintptr, "==")
|
|
|
|
when "cbneq"
|
|
cloopEmitCompareAndSet(operands, :uint8, "!=")
|
|
when "cineq"
|
|
cloopEmitCompareAndSet(operands, :uint32, "!=")
|
|
when "cqneq"
|
|
cloopEmitCompareAndSet(operands, :uint64, "!=")
|
|
when "cpneq"
|
|
cloopEmitCompareAndSet(operands, :uintptr, "!=")
|
|
|
|
when "cba"
|
|
cloopEmitCompareAndSet(operands, :uint8, ">")
|
|
when "cia"
|
|
cloopEmitCompareAndSet(operands, :uint32, ">")
|
|
when "cqa"
|
|
cloopEmitCompareAndSet(operands, :uint64, ">")
|
|
when "cpa"
|
|
cloopEmitCompareAndSet(operands, :uintptr, ">")
|
|
|
|
when "cbaeq"
|
|
cloopEmitCompareAndSet(operands, :uint8, ">=")
|
|
when "ciaeq"
|
|
cloopEmitCompareAndSet(operands, :uint32, ">=")
|
|
when "cqaeq"
|
|
cloopEmitCompareAndSet(operands, :uint64, ">=")
|
|
when "cpaeq"
|
|
cloopEmitCompareAndSet(operands, :uintptr, ">=")
|
|
|
|
when "cbb"
|
|
cloopEmitCompareAndSet(operands, :uint8, "<")
|
|
when "cib"
|
|
cloopEmitCompareAndSet(operands, :uint32, "<")
|
|
when "cqb"
|
|
cloopEmitCompareAndSet(operands, :uint64, "<")
|
|
when "cpb"
|
|
cloopEmitCompareAndSet(operands, :uintptr, "<")
|
|
|
|
when "cbbeq"
|
|
cloopEmitCompareAndSet(operands, :uint8, "<=")
|
|
when "cibeq"
|
|
cloopEmitCompareAndSet(operands, :uint32, "<=")
|
|
when "cqbeq"
|
|
cloopEmitCompareAndSet(operands, :uint64, "<=")
|
|
when "cpbeq"
|
|
cloopEmitCompareAndSet(operands, :uintptr, "<=")
|
|
|
|
when "cbgt"
|
|
cloopEmitCompareAndSet(operands, :int8, ">")
|
|
when "cigt"
|
|
cloopEmitCompareAndSet(operands, :int32, ">")
|
|
when "cqgt"
|
|
cloopEmitCompareAndSet(operands, :int64, ">")
|
|
when "cpgt"
|
|
cloopEmitCompareAndSet(operands, :intptr, ">")
|
|
|
|
when "cbgteq"
|
|
cloopEmitCompareAndSet(operands, :int8, ">=")
|
|
when "cigteq"
|
|
cloopEmitCompareAndSet(operands, :int32, ">=")
|
|
when "cqgteq"
|
|
cloopEmitCompareAndSet(operands, :int64, ">=")
|
|
when "cpgteq"
|
|
cloopEmitCompareAndSet(operands, :intptr, ">=")
|
|
|
|
when "cblt"
|
|
cloopEmitCompareAndSet(operands, :int8, "<")
|
|
when "cilt"
|
|
cloopEmitCompareAndSet(operands, :int32, "<")
|
|
when "cqlt"
|
|
cloopEmitCompareAndSet(operands, :int64, "<")
|
|
when "cplt"
|
|
cloopEmitCompareAndSet(operands, :intptr, "<")
|
|
|
|
when "cblteq"
|
|
cloopEmitCompareAndSet(operands, :int8, "<=")
|
|
when "cilteq"
|
|
cloopEmitCompareAndSet(operands, :int32, "<=")
|
|
when "cqlteq"
|
|
cloopEmitCompareAndSet(operands, :int64, "<=")
|
|
when "cplteq"
|
|
cloopEmitCompareAndSet(operands, :intptr, "<=")
|
|
|
|
when "tbs"
|
|
cloopEmitTestSet(operands, :int8, "< 0")
|
|
when "tis"
|
|
cloopEmitTestSet(operands, :int32, "< 0")
|
|
when "tqs"
|
|
cloopEmitTestSet(operands, :int64, "< 0")
|
|
when "tps"
|
|
cloopEmitTestSet(operands, :intptr, "< 0")
|
|
|
|
when "tbz"
|
|
cloopEmitTestSet(operands, :int8, "== 0")
|
|
when "tiz"
|
|
cloopEmitTestSet(operands, :int32, "== 0")
|
|
when "tqz"
|
|
cloopEmitTestSet(operands, :int64, "== 0")
|
|
when "tpz"
|
|
cloopEmitTestSet(operands, :intptr, "== 0")
|
|
|
|
when "tbnz"
|
|
cloopEmitTestSet(operands, :int8, "!= 0")
|
|
when "tinz"
|
|
cloopEmitTestSet(operands, :int32, "!= 0")
|
|
when "tqnz"
|
|
cloopEmitTestSet(operands, :int64, "!= 0")
|
|
when "tpnz"
|
|
cloopEmitTestSet(operands, :intptr, "!= 0")
|
|
|
|
# 64-bit instruction: cdqi (based on X64)
|
|
# Sign extends the lower 32 bits of t0, but put the sign extension into
|
|
# the lower 32 bits of t1. Leave the upper 32 bits of t0 and t1 unchanged.
|
|
when "cdqi"
|
|
$asm.putc "{ // cdqi"
|
|
$asm.putc " int64_t temp = t0.i32(); // sign extend the low 32bit"
|
|
$asm.putc " t0 = (uint32_t)temp; // low word"
|
|
$asm.putc " t1 = (uint32_t)(temp >> 32); // high word"
|
|
$asm.putc "}"
|
|
|
|
# 64-bit instruction: idivi op1 (based on X64)
|
|
# Divide a 64-bit integer numerator by the specified denominator.
|
|
# The numerator is specified in t0 and t1 as follows:
|
|
# 1. low 32 bits of the numerator is in the low 32 bits of t0.
|
|
# 2. high 32 bits of the numerator is in the low 32 bits of t1.
|
|
#
|
|
# The resultant quotient is a signed 32-bit int, and is to be stored
|
|
# in the lower 32 bits of t0.
|
|
# The resultant remainder is a signed 32-bit int, and is to be stored
|
|
# in the lower 32 bits of t1.
|
|
when "idivi"
|
|
# Divide t1,t0 (EDX,EAX) by the specified arg, and store the remainder in t1,
|
|
# and quotient in t0:
|
|
$asm.putc "{ // idivi"
|
|
$asm.putc " int64_t dividend = (int64_t(t1.u32()) << 32) | t0.u32();"
|
|
$asm.putc " int64_t divisor = #{operands[0].clValue(:intptr)};"
|
|
$asm.putc " t1 = (uint32_t)(dividend % divisor); // remainder"
|
|
$asm.putc " t0 = (uint32_t)(dividend / divisor); // quotient"
|
|
$asm.putc "}"
|
|
|
|
# 32-bit instruction: fii2d int32LoOp int32HiOp dblOp (based on ARMv7)
|
|
# Decode 2 32-bit ints (low and high) into a 64-bit double.
|
|
when "fii2d"
|
|
$asm.putc "#{operands[2].clLValue(:double)} = ints2Double(#{operands[0].clValue(:uint32)}, #{operands[1].clValue(:uint32)}); // fii2d"
|
|
|
|
# 32-bit instruction: f2dii dblOp int32LoOp int32HiOp (based on ARMv7)
|
|
# Encode a 64-bit double into 2 32-bit ints (low and high).
|
|
when "fd2ii"
|
|
$asm.putc "double2Ints(#{operands[0].clValue(:double)}, #{operands[1].clDump}, #{operands[2].clDump}); // fd2ii"
|
|
|
|
# 64-bit instruction: fq2d int64Op dblOp (based on X64)
|
|
# Copy a bit-encoded double in a 64-bit int register to a double register.
|
|
when "fq2d"
|
|
$asm.putc "#{operands[1].clLValue(:double)} = #{operands[0].clValue(:bitsAsDouble)}; // fq2d"
|
|
|
|
# 64-bit instruction: fd2q dblOp int64Op (based on X64 instruction set)
|
|
# Copy a double as a bit-encoded double into a 64-bit int register.
|
|
when "fd2q"
|
|
$asm.putc "#{operands[1].clLValue(:int64)} = #{operands[0].clValue(:bitsAsInt64)}; // fd2q"
|
|
|
|
when "leai"
|
|
operands[0].cloopEmitLea(operands[1], :int32)
|
|
when "leap"
|
|
operands[0].cloopEmitLea(operands[1], :intptr)
|
|
|
|
when "baddio"
|
|
cloopEmitOpAndBranchIfOverflow(operands, "+", :int32)
|
|
when "bsubio"
|
|
cloopEmitOpAndBranchIfOverflow(operands, "-", :int32)
|
|
when "bmulio"
|
|
cloopEmitOpAndBranchIfOverflow(operands, "*", :int32)
|
|
|
|
when "baddis"
|
|
cloopEmitOpAndBranch(operands, "+", :int32, "< 0")
|
|
when "baddiz"
|
|
cloopEmitOpAndBranch(operands, "+", :int32, "== 0")
|
|
when "baddinz"
|
|
cloopEmitOpAndBranch(operands, "+", :int32, "!= 0")
|
|
|
|
when "baddqs"
|
|
cloopEmitOpAndBranch(operands, "+", :int64, "< 0")
|
|
when "baddqz"
|
|
cloopEmitOpAndBranch(operands, "+", :int64, "== 0")
|
|
when "baddqnz"
|
|
cloopEmitOpAndBranch(operands, "+", :int64, "!= 0")
|
|
|
|
when "baddps"
|
|
cloopEmitOpAndBranch(operands, "+", :intptr, "< 0")
|
|
when "baddpz"
|
|
cloopEmitOpAndBranch(operands, "+", :intptr, "== 0")
|
|
when "baddpnz"
|
|
cloopEmitOpAndBranch(operands, "+", :intptr, "!= 0")
|
|
|
|
when "bsubis"
|
|
cloopEmitOpAndBranch(operands, "-", :int32, "< 0")
|
|
when "bsubiz"
|
|
cloopEmitOpAndBranch(operands, "-", :int32, "== 0")
|
|
when "bsubinz"
|
|
cloopEmitOpAndBranch(operands, "-", :int32, "!= 0")
|
|
|
|
when "borris"
|
|
cloopEmitOpAndBranch(operands, "|", :int32, "< 0")
|
|
when "borriz"
|
|
cloopEmitOpAndBranch(operands, "|", :int32, "== 0")
|
|
when "borrinz"
|
|
cloopEmitOpAndBranch(operands, "|", :int32, "!= 0")
|
|
|
|
when "memfence"
|
|
|
|
when "push"
|
|
operands.each {
|
|
| op |
|
|
$asm.putc "PUSH(#{op.clDump});"
|
|
}
|
|
when "pop"
|
|
operands.each {
|
|
| op |
|
|
$asm.putc "POP(#{op.clDump});"
|
|
}
|
|
|
|
|
|
# A convenience and compact call to crash because we don't want to use
|
|
# the generic llint crash mechanism which relies on the availability
|
|
# of the call instruction (which cannot be implemented in a generic
|
|
# way, and can be abused if we made it just work for this special case).
|
|
# Using a special cloopCrash instruction is cleaner.
|
|
when "cloopCrash"
|
|
$asm.putc "CRASH();"
|
|
|
|
# We can't rely on the llint JS call mechanism which actually makes
|
|
# use of the call instruction. Instead, we just implement JS calls
|
|
# as an opcode dispatch.
|
|
when "cloopCallJSFunction"
|
|
uid = $asm.newUID
|
|
$asm.putc "lr = getOpcode(llint_cloop_did_return_from_js_#{uid});"
|
|
$asm.putc "opcode = #{operands[0].clValue(:opcode)};"
|
|
$asm.putc "DISPATCH_OPCODE();"
|
|
$asm.putsLabel("llint_cloop_did_return_from_js_#{uid}", false)
|
|
|
|
# We can't do generic function calls with an arbitrary set of args, but
|
|
# fortunately we don't have to here. All native function calls always
|
|
# have a fixed prototype of 2 args: the passed JSGlobalObject* and CallFrame*.
|
|
when "cloopCallNative"
|
|
$asm.putc "cloopStack.setCurrentStackPointer(sp.vp());"
|
|
$asm.putc "nativeFunc = #{operands[0].clValue(:nativeFunc)};"
|
|
$asm.putc "functionReturnValue = JSValue::decode(nativeFunc(jsCast<JSGlobalObject*>(t0.cell()), t1.callFrame()));"
|
|
$asm.putc "#if USE(JSVALUE32_64)"
|
|
$asm.putc " t1 = functionReturnValue.tag();"
|
|
$asm.putc " t0 = functionReturnValue.payload();"
|
|
$asm.putc "#else // USE_JSVALUE64)"
|
|
$asm.putc " t0 = JSValue::encode(functionReturnValue);"
|
|
$asm.putc "#endif // USE_JSVALUE64)"
|
|
|
|
# We can't do generic function calls with an arbitrary set of args, but
|
|
# fortunately we don't have to here. All slow path function calls always
|
|
# have a fixed prototype too. See cloopEmitCallSlowPath() for details.
|
|
when "cloopCallSlowPath"
|
|
cloopEmitCallSlowPath(operands)
|
|
|
|
when "cloopCallSlowPathVoid"
|
|
cloopEmitCallSlowPathVoid(operands)
|
|
|
|
# For debugging only. This is used to insert instrumentation into the
|
|
# generated LLIntAssembly.h during llint development only. Do not use
|
|
# for production code.
|
|
when "cloopDo"
|
|
$asm.putc "#{annotation}"
|
|
|
|
else
|
|
lowerDefault
|
|
end
|
|
end
|
|
|
|
def lowerC_LOOP_WIN
|
|
lowerC_LOOP
|
|
end
|
|
|
|
def recordMetaDataC_LOOP
|
|
$asm.codeOrigin codeOriginString if $enableCodeOriginComments
|
|
$asm.annotation annotation if $enableInstrAnnotations && (opcode != "cloopDo")
|
|
$asm.debugAnnotation codeOrigin.debugDirective if $enableDebugAnnotations
|
|
end
|
|
end
|