blob: 112eed7b5c108825b439d1096146f85cdb029134 [file] [log] [blame]
# Copyright (C) 2012-2018 Apple Inc. All rights reserved.
# Copyright (C) 2013 Digia Plc. and/or its subsidiary(-ies)
#
# 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"
# GPR conventions, to match the baseline JIT:
#
#
# On x86-32 bits (windows and non-windows)
# a0, a1, a2, a3 are only there for ease-of-use of offlineasm; they are not
# actually considered as such by the ABI and we need to push/pop our arguments
# on the stack. a0 and a1 are ecx and edx to follow fastcall.
#
# eax => t0, a2, r0
# edx => t1, a1, r1
# ecx => t2, a0
# ebx => t3, a3 (callee-save)
# esi => t4 (callee-save)
# edi => t5 (callee-save)
# ebp => cfr
# esp => sp
#
# On x86-64 non-windows
#
# rax => t0, r0
# rdi => a0
# rsi => t1, a1
# rdx => t2, a2, r1
# rcx => t3, a3
# r8 => t4
# r9 => t5
# r10 => t6
# rbx => csr0 (callee-save, wasmInstance)
# r12 => csr1 (callee-save, metadataTable)
# r13 => csr2 (callee-save, PB)
# r14 => csr3 (callee-save, tagTypeNumber)
# r15 => csr4 (callee-save, tagMask)
# rsp => sp
# rbp => cfr
# r11 => (scratch)
#
# On x86-64 windows
# Arguments need to be push/pop'd on the stack in addition to being stored in
# the registers. Also, >8 return types are returned in a weird way.
#
# rax => t0, r0
# rcx => t5, a0
# rdx => t1, a1, r1
# r8 => t2, a2
# r9 => t3, a3
# r10 => t4
# rbx => csr0 (callee-save, PB, unused in baseline)
# rsi => csr1 (callee-save)
# rdi => csr2 (callee-save)
# r12 => csr3 (callee-save)
# r13 => csr4 (callee-save)
# r14 => csr5 (callee-save, numberTag)
# r15 => csr6 (callee-save, notCellMask)
# rsp => sp
# rbp => cfr
# r11 => (scratch)
def isX64
case $activeBackend
when "X86"
false
when "X86_WIN"
false
when "X86_64"
true
when "X86_64_WIN"
true
else
raise "bad value for $activeBackend: #{$activeBackend}"
end
end
def isWin
case $activeBackend
when "X86"
false
when "X86_WIN"
true
when "X86_64"
false
when "X86_64_WIN"
true
else
raise "bad value for $activeBackend: #{$activeBackend}"
end
end
def isMSVC
$options.has_key?(:assembler) && $options[:assembler] == "MASM"
end
def isIntelSyntax
$options.has_key?(:assembler) && $options[:assembler] == "MASM"
end
def register(name)
isIntelSyntax ? name : "%" + name
end
def offsetRegister(off, register)
isIntelSyntax ? "[#{off} + #{register}]" : "#{off}(#{register})"
end
def callPrefix
isIntelSyntax ? "" : "*"
end
def orderOperands(*operands)
(isIntelSyntax ? operands.reverse : operands).join(", ")
end
def const(c)
isIntelSyntax ? "#{c}" : "$#{c}"
end
def getSizeString(kind)
if !isIntelSyntax
return ""
end
size = ""
case kind
when :byte
size = "byte"
when :half
size = "word"
when :int
size = "dword"
when :ptr
size = isX64 ? "qword" : "dword"
when :float
size = "dword"
when :double
size = "qword"
when :quad
size = "qword"
else
raise "Invalid kind #{kind}"
end
return size + " " + "ptr" + " ";
end
class SpecialRegister < NoChildren
def x86Operand(kind)
raise unless @name =~ /^r/
raise unless isX64
case kind
when :half
register(@name + "w")
when :int
register(@name + "d")
when :ptr
register(@name)
when :quad
register(@name)
else
raise codeOriginString
end
end
def x86CallOperand(kind)
# Call operands are not allowed to be partial registers.
"#{callPrefix}#{x86Operand(:quad)}"
end
end
X64_SCRATCH_REGISTER = SpecialRegister.new("r11")
def x86GPRName(name, kind)
case name
when "eax", "ebx", "ecx", "edx"
name8 = name[1] + 'l'
name16 = name[1..2]
when "esi", "edi", "ebp", "esp"
name16 = name[1..2]
name8 = name16 + 'l'
when "rax", "rbx", "rcx", "rdx"
raise "bad GPR name #{name} in 32-bit X86" unless isX64
name8 = name[1] + 'l'
name16 = name[1..2]
when "r8", "r9", "r10", "r12", "r13", "r14", "r15"
raise "bad GPR name #{name} in 32-bit X86" unless isX64
case kind
when :half
return register(name + "w")
when :int
return register(name + "d")
when :ptr
return register(name)
when :quad
return register(name)
end
else
raise "bad GPR name #{name}"
end
case kind
when :byte
register(name8)
when :half
register(name16)
when :int
register("e" + name16)
when :ptr
register((isX64 ? "r" : "e") + name16)
when :quad
isX64 ? register("r" + name16) : raise
else
raise "invalid kind #{kind} for GPR #{name} in X86"
end
end
class Node
def x86LoadOperand(type, dst)
x86Operand(type)
end
end
class RegisterID
def supports8BitOnX86
case x86GPR
when "eax", "ebx", "ecx", "edx", "edi", "esi", "ebp", "esp"
true
when "r8", "r9", "r10", "r12", "r13", "r14", "r15"
false
else
raise
end
end
def x86GPR
if isX64
case name
when "t0", "r0", "ws0"
"eax"
when "r1"
"edx" # t1 = a1 when isWin, t2 = a2 otherwise
when "a0", "wa0"
isWin ? "ecx" : "edi"
when "t1", "a1", "wa1"
isWin ? "edx" : "esi"
when "t2", "a2", "wa2"
isWin ? "r8" : "edx"
when "t3", "a3", "wa3"
isWin ? "r9" : "ecx"
when "t4", "wa4"
isWin ? "r10" : "r8"
when "t5", "wa5"
isWin ? "ecx" : "r9"
when "t6", "ws1"
raise "cannot use register #{name} on X86-64 Windows" if isWin
"r10"
when "csr0"
"ebx"
when "csr1"
isWin ? "esi" : "r12"
when "csr2"
isWin ? "edi" : "r13"
when "csr3"
isWin ? "r12" : "r14"
when "csr4"
isWin ? "r13" : "r15"
when "csr5"
raise "cannot use register #{name} on X86-64" unless isWin
"r14"
when "csr6"
raise "cannot use register #{name} on X86-64" unless isWin
"r15"
when "cfr"
"ebp"
when "sp"
"esp"
else
raise "cannot use register #{name} on X86"
end
else
case name
when "t0", "r0", "a2"
"eax"
when "t1", "r1", "a1"
"edx"
when "t2", "a0"
"ecx"
when "t3", "a3"
"ebx"
when "t4"
"esi"
when "t5"
"edi"
when "cfr"
"ebp"
when "sp"
"esp"
end
end
end
def x86Operand(kind)
x86GPRName(x86GPR, kind)
end
def x86CallOperand(kind)
"#{callPrefix}#{x86Operand(:ptr)}"
end
end
class FPRegisterID
def x86Operand(kind)
raise unless [:float, :double].include? kind
case name
when "ft0", "fa0", "fr", "wfa0"
register("xmm0")
when "ft1", "fa1", "wfa1"
register("xmm1")
when "ft2", "fa2", "wfa2"
register("xmm2")
when "ft3", "fa3", "wfa3"
register("xmm3")
when "ft4", "wfa4"
register("xmm4")
when "ft5", "wfa5"
register("xmm5")
when "wfa6"
register("xmm6")
when "wfa7"
register("xmm7")
else
raise "Bad register #{name} for X86 at #{codeOriginString}"
end
end
def x86CallOperand(kind)
"#{callPrefix}#{x86Operand(kind)}"
end
end
class Immediate
def validX86Immediate?
if isX64
value >= -0x80000000 and value <= 0x7fffffff
else
true
end
end
def x86Operand(kind)
"#{const(value)}"
end
def x86CallOperand(kind)
"#{value}"
end
end
class Address
def supports8BitOnX86
true
end
def x86AddressOperand(addressKind)
"#{offsetRegister(offset.value, base.x86Operand(addressKind))}"
end
def x86Operand(kind)
"#{getSizeString(kind)}#{x86AddressOperand(:ptr)}"
end
def x86CallOperand(kind)
"#{callPrefix}#{x86Operand(kind)}"
end
end
class BaseIndex
def supports8BitOnX86
true
end
def x86AddressOperand(addressKind)
if !isIntelSyntax
"#{offset.value}(#{base.x86Operand(addressKind)}, #{index.x86Operand(addressKind)}, #{scaleValue})"
else
"#{getSizeString(addressKind)}[#{offset.value} + #{base.x86Operand(addressKind)} + #{index.x86Operand(addressKind)} * #{scaleValue}]"
end
end
def x86Operand(kind)
if !isIntelSyntax
x86AddressOperand(:ptr)
else
"#{getSizeString(kind)}[#{offset.value} + #{base.x86Operand(:ptr)} + #{index.x86Operand(:ptr)} * #{scaleValue}]"
end
end
def x86CallOperand(kind)
"#{callPrefix}#{x86Operand(kind)}"
end
end
class AbsoluteAddress
def supports8BitOnX86
true
end
def x86AddressOperand(addressKind)
"#{address.value}"
end
def x86Operand(kind)
"#{address.value}"
end
def x86CallOperand(kind)
"#{callPrefix}#{address.value}"
end
end
class LabelReference
def x86CallOperand(kind)
asmLabel
end
def x86LoadOperand(kind, dst)
# FIXME: Implement this on platforms that aren't Mach-O.
# https://bugs.webkit.org/show_bug.cgi?id=175104
used
if !isIntelSyntax
$asm.puts "movq #{asmLabel}@GOTPCREL(%rip), #{dst.x86Operand(:ptr)}"
else
$asm.puts "lea #{dst.x86Operand(:ptr)}, #{asmLabel}"
end
"#{offset}(#{dst.x86Operand(kind)})"
end
end
class LocalLabelReference
def x86Operand(kind)
asmLabel
end
def x86CallOperand(kind)
asmLabel
end
end
class Sequence
def getModifiedListX86_64
newList = []
@list.each {
| node |
newNode = node
if node.is_a? Instruction
unless node.opcode == "move"
usedScratch = false
newOperands = node.operands.map {
| operand |
if operand.immediate? and not operand.validX86Immediate?
if usedScratch
raise "Attempt to use scratch register twice at #{operand.codeOriginString}"
end
newList << Instruction.new(operand.codeOrigin, "move", [operand, X64_SCRATCH_REGISTER])
usedScratch = true
X64_SCRATCH_REGISTER
else
operand
end
}
newNode = Instruction.new(node.codeOrigin, node.opcode, newOperands, node.annotation)
end
else
unless node.is_a? Label or
node.is_a? LocalLabel or
node.is_a? Skip
raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
end
end
if newNode
newList << newNode
end
}
return newList
end
def getModifiedListX86_64_WIN
getModifiedListX86_64
end
end
class Instruction
def x86Operands(*kinds)
raise "Expected size of kinds to be #{operands.size}, but it was #{kinds.size}" unless kinds.size == operands.size
result = []
kinds.size.times {
| idx |
i = isIntelSyntax ? (kinds.size - idx - 1) : idx
result << operands[i].x86Operand(kinds[i])
}
result.join(", ")
end
def x86LoadOperands(srcKind, dstKind)
orderOperands(operands[0].x86LoadOperand(srcKind, operands[1]), operands[1].x86Operand(dstKind))
end
def x86Suffix(kind)
if isIntelSyntax and not [:float, :double].include? kind
return ""
end
case kind
when :byte
"b"
when :half
"w"
when :int
"l"
when :ptr
isX64 ? "q" : "l"
when :quad
isX64 ? "q" : raise
when :float
"ss"
when :double
"sd"
else
raise
end
end
def x86Bytes(kind)
case kind
when :byte
1
when :half
2
when :int
4
when :ptr
isX64 ? 8 : 4
when :quad
isX64 ? 8 : raise
when :float
4
when :double
8
else
raise
end
end
def emitX86Lea(src, dst, kind)
if src.is_a? LabelReference
src.used
if !isIntelSyntax
$asm.puts "movq #{src.asmLabel}@GOTPCREL(%rip), #{dst.x86Operand(:ptr)}"
else
$asm.puts "lea #{dst.x86Operand(:ptr)}, #{src.asmLabel}"
end
else
$asm.puts "lea#{x86Suffix(kind)} #{orderOperands(src.x86AddressOperand(kind), dst.x86Operand(kind))}"
end
end
def getImplicitOperandString
isIntelSyntax ? "st(0), " : ""
end
def handleX86OpWithNumOperands(opcode, kind, numOperands)
if numOperands == 3
if operands[0] == operands[2]
$asm.puts "#{opcode} #{orderOperands(operands[1].x86Operand(kind), operands[2].x86Operand(kind))}"
elsif operands[1] == operands[2]
$asm.puts "#{opcode} #{orderOperands(operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
else
$asm.puts "mov#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
$asm.puts "#{opcode} #{orderOperands(operands[1].x86Operand(kind), operands[2].x86Operand(kind))}"
end
else
$asm.puts "#{opcode} #{orderOperands(operands[0].x86Operand(kind), operands[1].x86Operand(kind))}"
end
end
def handleX86Op(opcode, kind)
handleX86OpWithNumOperands(opcode, kind, operands.size)
end
def handleX86Shift(opcode, kind)
if operands[0].is_a? Immediate or operands[0].x86GPR == "ecx"
$asm.puts "#{opcode} #{orderOperands(operands[0].x86Operand(:byte), operands[1].x86Operand(kind))}"
else
$asm.puts "xchg#{x86Suffix(:ptr)} #{operands[0].x86Operand(:ptr)}, #{x86GPRName("ecx", :ptr)}"
$asm.puts "#{opcode} #{orderOperands(register("cl"), operands[1].x86Operand(kind))}"
$asm.puts "xchg#{x86Suffix(:ptr)} #{operands[0].x86Operand(:ptr)}, #{x86GPRName("ecx", :ptr)}"
end
end
def handleX86FPBranch(kind, branchOpcode, mode)
case mode
when :normal
$asm.puts "ucomi#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(:double), operands[0].x86Operand(:double))}"
when :reverse
$asm.puts "ucomi#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(:double), operands[1].x86Operand(:double))}"
else
raise mode.inspect
end
$asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
end
def handleX86IntCompare(opcodeSuffix, kind)
if operands[0].is_a? Immediate and operands[0].value == 0 and operands[1].is_a? RegisterID and (opcodeSuffix == "e" or opcodeSuffix == "ne")
$asm.puts "test#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[1].x86Operand(kind))}"
elsif operands[1].is_a? Immediate and operands[1].value == 0 and operands[0].is_a? RegisterID and (opcodeSuffix == "e" or opcodeSuffix == "ne")
$asm.puts "test#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[0].x86Operand(kind))}"
else
$asm.puts "cmp#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[0].x86Operand(kind))}"
end
end
def handleX86IntCompare(opcodeSuffix, kind)
if operands[0].is_a? Immediate and operands[0].value == 0 and operands[1].is_a? RegisterID and (opcodeSuffix == "e" or opcodeSuffix == "ne")
$asm.puts "test#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[1].x86Operand(kind))}"
elsif operands[1].is_a? Immediate and operands[1].value == 0 and operands[0].is_a? RegisterID and (opcodeSuffix == "e" or opcodeSuffix == "ne")
$asm.puts "test#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[0].x86Operand(kind))}"
else
$asm.puts "cmp#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[0].x86Operand(kind))}"
end
end
def handleX86IntBranch(branchOpcode, kind)
handleX86IntCompare(branchOpcode[1..-1], kind)
$asm.puts "#{branchOpcode} #{operands[2].asmLabel}"
end
def handleX86Set(setOpcode, operand)
if operand.supports8BitOnX86
$asm.puts "#{setOpcode} #{operand.x86Operand(:byte)}"
if !isIntelSyntax
$asm.puts "movzbl #{orderOperands(operand.x86Operand(:byte), operand.x86Operand(:int))}"
else
$asm.puts "movzx #{orderOperands(operand.x86Operand(:byte), operand.x86Operand(:int))}"
end
else
ax = RegisterID.new(nil, "r0")
$asm.puts "xchg#{x86Suffix(:ptr)} #{operand.x86Operand(:ptr)}, #{ax.x86Operand(:ptr)}"
$asm.puts "#{setOpcode} #{ax.x86Operand(:byte)}"
if !isIntelSyntax
$asm.puts "movzbl #{ax.x86Operand(:byte)}, #{ax.x86Operand(:int)}"
else
$asm.puts "movzx #{ax.x86Operand(:int)}, #{ax.x86Operand(:byte)}"
end
$asm.puts "xchg#{x86Suffix(:ptr)} #{operand.x86Operand(:ptr)}, #{ax.x86Operand(:ptr)}"
end
end
def handleX86IntCompareSet(setOpcode, kind)
handleX86IntCompare(setOpcode[3..-1], kind)
handleX86Set(setOpcode, operands[2])
end
def handleX86FPCompareSet(kind, setOpcode, order = :normal)
is_special = setOpcode.is_a? Symbol
left = operands[0]
right = operands[1]
target = operands[2]
compare = lambda do |lhs, rhs|
$asm.puts "ucomi#{x86Suffix(kind)} #{orderOperands(lhs.x86Operand(:double), rhs.x86Operand(:double))}"
end
if is_special
case setOpcode
when :eq
if left == right
compare.call(right, left)
handleX86Set("setnp", operands[2])
return
end
isUnordered = LocalLabel.unique("isUnordered")
$asm.puts "movq $0, #{target.x86Operand(:quad)}"
compare.call(right, left)
$asm.puts "jp #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
handleX86Set("sete", target)
isUnordered.lower($activeBackend)
return
when :nequn
if left == right
compare.call(right, left)
handleX86Set("setp", target)
return
end
isUnordered = LocalLabel.unique("isUnordered")
$asm.puts "movq $1, #{target.x86Operand(:quad)}"
compare.call(right, left);
$asm.puts "jp #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
handleX86Set("setne", target)
isUnordered.lower($activeBackend)
return
else
raise "Uhandled special opcode: #{setOpcode}"
end
end
if order == :normal
compare.call(right, left)
else
compare.call(left, right)
end
handleX86Set(setOpcode, target)
end
def handleX86Test(kind)
value = operands[0]
case operands.size
when 2
mask = Immediate.new(codeOrigin, -1)
when 3
mask = operands[1]
else
raise "Expected 2 or 3 operands, but got #{operands.size} at #{codeOriginString}"
end
if mask.is_a? Immediate and mask.value == -1
if value.is_a? RegisterID
$asm.puts "test#{x86Suffix(kind)} #{value.x86Operand(kind)}, #{value.x86Operand(kind)}"
else
$asm.puts "cmp#{x86Suffix(kind)} #{orderOperands(const(0), value.x86Operand(kind))}"
end
else
$asm.puts "test#{x86Suffix(kind)} #{orderOperands(mask.x86Operand(kind), value.x86Operand(kind))}"
end
end
def handleX86BranchTest(branchOpcode, kind)
handleX86Test(kind)
$asm.puts "#{branchOpcode} #{operands.last.asmLabel}"
end
def handleX86SetTest(setOpcode, kind)
handleX86Test(kind)
handleX86Set(setOpcode, operands.last)
end
def handleX86OpBranch(opcode, branchOpcode, kind)
handleX86OpWithNumOperands(opcode, kind, operands.size - 1)
case operands.size
when 4
jumpTarget = operands[3]
when 3
jumpTarget = operands[2]
else
raise self.inspect
end
$asm.puts "#{branchOpcode} #{jumpTarget.asmLabel}"
end
def handleX86SubBranch(branchOpcode, kind)
if operands.size == 4 and operands[1] == operands[2]
$asm.puts "neg#{x86Suffix(kind)} #{operands[2].x86Operand(kind)}"
$asm.puts "add#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
else
handleX86OpWithNumOperands("sub#{x86Suffix(kind)}", kind, operands.size - 1)
end
case operands.size
when 4
jumpTarget = operands[3]
when 3
jumpTarget = operands[2]
else
raise self.inspect
end
$asm.puts "#{branchOpcode} #{jumpTarget.asmLabel}"
end
def handleX86Add(kind)
if operands.size == 3 and operands[1] == operands[2]
unless Immediate.new(nil, 0) == operands[0]
$asm.puts "add#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
end
elsif operands.size == 3 and operands[0].is_a? Immediate
raise unless operands[1].is_a? RegisterID
raise unless operands[2].is_a? RegisterID
if operands[0].value == 0
if operands[1] != operands[2]
$asm.puts "mov#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[2].x86Operand(kind))}"
end
else
$asm.puts "lea#{x86Suffix(kind)} #{orderOperands(offsetRegister(operands[0].value, operands[1].x86Operand(kind)), operands[2].x86Operand(kind))}"
end
elsif operands.size == 3 and operands[0].is_a? RegisterID
raise unless operands[1].is_a? RegisterID
raise unless operands[2].is_a? RegisterID
if operands[0] == operands[2]
$asm.puts "add#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[2].x86Operand(kind))}"
else
if !isIntelSyntax
$asm.puts "lea#{x86Suffix(kind)} (#{operands[0].x86Operand(kind)}, #{operands[1].x86Operand(kind)}), #{operands[2].x86Operand(kind)}"
else
$asm.puts "lea#{x86Suffix(kind)} #{operands[2].x86Operand(kind)}, [#{operands[0].x86Operand(kind)} + #{operands[1].x86Operand(kind)}]"
end
end
else
unless Immediate.new(nil, 0) == operands[0]
$asm.puts "add#{x86Suffix(kind)} #{x86Operands(kind, kind)}"
end
end
end
def handleX86Sub(kind)
if operands.size == 3
if Immediate.new(nil, 0) == operands[1]
raise unless operands[0].is_a? RegisterID
raise unless operands[2].is_a? RegisterID
if operands[0] != operands[2]
$asm.puts "mov#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
end
return
end
if operands[1] == operands[2]
$asm.puts "neg#{x86Suffix(kind)} #{operands[2].x86Operand(kind)}"
if Immediate.new(nil, 0) != operands[0]
$asm.puts "add#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
end
return
end
end
if operands.size == 2
if Immediate.new(nil, 0) == operands[0]
return
end
end
handleX86Op("sub#{x86Suffix(kind)}", kind)
end
def handleX86Mul(kind)
if operands.size == 3 and operands[0].is_a? Immediate
$asm.puts "imul#{x86Suffix(kind)} #{x86Operands(kind, kind, kind)}"
return
end
if operands.size == 2 and operands[0].is_a? Immediate
imm = operands[0].value
if imm > 0 and isPowerOfTwo(imm)
$asm.puts "sal#{x86Suffix(kind)} #{orderOperands(Immediate.new(nil, Math.log2(imm).to_i).x86Operand(kind), operands[1].x86Operand(kind))}"
return
end
end
handleX86Op("imul#{x86Suffix(kind)}", kind)
end
def handleX86AddFP(kind)
if operands.size == 2
$asm.puts "add#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[1].x86Operand(kind))}"
elsif operands.size == 3
$asm.puts "vadd#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
else
raise "Unexpected number of operands for floating point addition: #{operands.size}"
end
end
def handleX86SubFP(kind)
if operands.size == 2
$asm.puts "sub#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[1].x86Operand(kind))}"
elsif operands.size == 3
$asm.puts "vsub#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
else
raise "Unexpected number of operands for floating point addition: #{operands.size}"
end
end
def handleX86MulFP(kind)
if operands.size == 2
$asm.puts "mul#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[1].x86Operand(kind))}"
elsif operands.size == 3
$asm.puts "vmul#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
else
raise "Unexpected number of operands for floating point addition: #{operands.size}"
end
end
def handleX86DivFP(kind)
if operands.size == 2
$asm.puts "div#{x86Suffix(kind)} #{orderOperands(operands[0].x86Operand(kind), operands[1].x86Operand(kind))}"
elsif operands.size == 3
$asm.puts "vdiv#{x86Suffix(kind)} #{orderOperands(operands[1].x86Operand(kind), operands[0].x86Operand(kind), operands[2].x86Operand(kind))}"
else
raise "Unexpected number of operands for floating point addition: #{operands.size}"
end
end
def handleX86Peek()
sp = RegisterID.new(nil, "sp")
opA = offsetRegister(operands[0].value * x86Bytes(:ptr), sp.x86Operand(:ptr))
opB = operands[1].x86Operand(:ptr)
$asm.puts "mov#{x86Suffix(:ptr)} #{orderOperands(opA, opB)}"
end
def handleX86Poke()
sp = RegisterID.new(nil, "sp")
opA = operands[0].x86Operand(:ptr)
opB = offsetRegister(operands[1].value * x86Bytes(:ptr), sp.x86Operand(:ptr))
$asm.puts "mov#{x86Suffix(:ptr)} #{orderOperands(opA, opB)}"
end
def handleMove
if Immediate.new(nil, 0) == operands[0] and operands[1].is_a? RegisterID
if isX64
$asm.puts "xor#{x86Suffix(:quad)} #{operands[1].x86Operand(:quad)}, #{operands[1].x86Operand(:quad)}"
else
$asm.puts "xor#{x86Suffix(:ptr)} #{operands[1].x86Operand(:ptr)}, #{operands[1].x86Operand(:ptr)}"
end
elsif operands[0] != operands[1]
if isX64
$asm.puts "mov#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}"
else
$asm.puts "mov#{x86Suffix(:ptr)} #{x86Operands(:ptr, :ptr)}"
end
end
end
def countLeadingZeros(kind)
target = operands[1]
srcIsNonZero = LocalLabel.unique("srcIsNonZero")
skipNonZeroCase = LocalLabel.unique("skipNonZeroCase")
zeroValue = Immediate.new(codeOrigin, x86Bytes(kind) * 8)
xorValue = Immediate.new(codeOrigin, kind == :quad ? 0x3f : 0x1f)
xor = kind == :quad ? "xorq" : "xori"
$asm.puts "bsr#{x86Suffix(kind)} #{x86Operands(kind, kind)}"
Sequence.new(codeOrigin, [
Instruction.new(codeOrigin, "bnz", [LocalLabelReference.new(codeOrigin, srcIsNonZero)]),
Instruction.new(codeOrigin, "move", [zeroValue, target]),
Instruction.new(codeOrigin, "jmp", [LocalLabelReference.new(codeOrigin, skipNonZeroCase)]),
srcIsNonZero,
Instruction.new(codeOrigin, xor, [xorValue, target]),
skipNonZeroCase,
]).lower($activeBackend)
end
def countTrailingZeros(kind)
target = operands[1]
srcIsNonZero = LocalLabel.unique("srcIsNonZero")
zeroValue = Immediate.new(codeOrigin, x86Bytes(kind) * 8)
$asm.puts "bsf#{x86Suffix(kind)} #{x86Operands(kind, kind)}"
Sequence.new(codeOrigin, [
Instruction.new(codeOrigin, "bnz", [LocalLabelReference.new(codeOrigin, srcIsNonZero)]),
Instruction.new(codeOrigin, "move", [zeroValue, target]),
srcIsNonZero,
]).lower($activeBackend)
end
def truncateFloatingPointToQuad(kind)
src = operands[0]
dst = operands[1]
slow = LocalLabel.unique("slow")
done = LocalLabel.unique("done")
gprScratch = X64_SCRATCH_REGISTER
fprScratch = FPRegisterID.forName(codeOrigin, "wfa7")
int64SignBit = Immediate.new(codeOrigin, 0x8000000000000000)
case kind
when :float
int64Min = Immediate.new(codeOrigin, 0xdf000000)
negInt64Min = Immediate.new(codeOrigin, 0x5f000000)
integerSuffix = "i"
floatingSuffix = "f"
when :double
int64Min = Immediate.new(codeOrigin, 0xc3e0000000000000)
negInt64Min = Immediate.new(codeOrigin, 0x43e0000000000000)
integerSuffix = "q"
floatingSuffix = "d"
else
raise
end
Sequence.new(codeOrigin, [
Instruction.new(codeOrigin, "move", [negInt64Min, gprScratch]),
Instruction.new(codeOrigin, "f#{integerSuffix}2#{floatingSuffix}", [gprScratch, fprScratch]),
Instruction.new(codeOrigin, "b#{floatingSuffix}gteq", [src, fprScratch, LocalLabelReference.new(codeOrigin, slow)]),
Instruction.new(codeOrigin, "truncate#{floatingSuffix}2qs", [src, dst]),
Instruction.new(codeOrigin, "jmp", [LocalLabelReference.new(codeOrigin, done)]),
slow,
Instruction.new(codeOrigin, "move", [int64Min, gprScratch]),
Instruction.new(codeOrigin, "f#{integerSuffix}2#{floatingSuffix}", [gprScratch, fprScratch]),
Instruction.new(codeOrigin, "add#{floatingSuffix}", [src, fprScratch]),
Instruction.new(codeOrigin, "truncate#{floatingSuffix}2qs", [fprScratch, dst]),
Instruction.new(codeOrigin, "move", [int64SignBit, gprScratch]),
Instruction.new(codeOrigin, "orq", [gprScratch, dst]),
done,
]).lower($activeBackend)
end
def convertQuadToFloatingPoint(kind)
src = operands[0]
scratch1 = operands[1]
dst = operands[2]
slow = LocalLabel.unique("slow")
done = LocalLabel.unique("done")
scratch2 = X64_SCRATCH_REGISTER
one = Immediate.new(codeOrigin, 0x1)
case kind
when :float
floatingSuffix = "f"
when :double
floatingSuffix = "d"
else
raise
end
Sequence.new(codeOrigin, [
Instruction.new(codeOrigin, "btqs", [src, LocalLabelReference.new(codeOrigin, slow)]),
Instruction.new(codeOrigin, "cq2#{floatingSuffix}s", [src, dst]),
Instruction.new(codeOrigin, "jmp", [LocalLabelReference.new(codeOrigin, done)]),
slow,
Instruction.new(codeOrigin, "move", [src, scratch1]),
Instruction.new(codeOrigin, "move", [src, scratch2]),
Instruction.new(codeOrigin, "urshiftq", [one, scratch1]),
Instruction.new(codeOrigin, "andq", [one, scratch2]),
Instruction.new(codeOrigin, "orq", [scratch1, scratch2]),
Instruction.new(codeOrigin, "cq2#{floatingSuffix}s", [scratch2, dst]),
Instruction.new(codeOrigin, "add#{floatingSuffix}", [dst, dst]),
done,
]).lower($activeBackend)
end
def lowerX86
raise unless $activeBackend == "X86"
lowerX86Common
end
def lowerX86_WIN
raise unless $activeBackend == "X86_WIN"
lowerX86Common
end
def lowerX86_64
raise unless $activeBackend == "X86_64"
lowerX86Common
end
def lowerX86_64_WIN
raise unless $activeBackend == "X86_64_WIN"
lowerX86Common
end
def lowerX86Common
case opcode
when "addi"
handleX86Add(:int)
when "addp"
handleX86Add(:ptr)
when "addq"
handleX86Add(:quad)
when "andi"
handleX86Op("and#{x86Suffix(:int)}", :int)
when "andp"
handleX86Op("and#{x86Suffix(:ptr)}", :ptr)
when "andq"
handleX86Op("and#{x86Suffix(:quad)}", :quad)
when "andf"
handleX86Op("andps", :float)
when "andd"
handleX86Op("andpd", :double)
when "lshifti"
handleX86Shift("sal#{x86Suffix(:int)}", :int)
when "lshiftp"
handleX86Shift("sal#{x86Suffix(:ptr)}", :ptr)
when "lshiftq"
handleX86Shift("sal#{x86Suffix(:quad)}", :quad)
when "muli"
handleX86Mul(:int)
when "mulp"
handleX86Mul(:ptr)
when "mulq"
handleX86Mul(:quad)
when "negi"
$asm.puts "neg#{x86Suffix(:int)} #{x86Operands(:int)}"
when "negp"
$asm.puts "neg#{x86Suffix(:ptr)} #{x86Operands(:ptr)}"
when "negq"
$asm.puts "neg#{x86Suffix(:quad)} #{x86Operands(:quad)}"
when "noti"
$asm.puts "not#{x86Suffix(:int)} #{x86Operands(:int)}"
when "ori"
handleX86Op("or#{x86Suffix(:int)}", :int)
when "orp"
handleX86Op("or#{x86Suffix(:ptr)}", :ptr)
when "orq"
handleX86Op("or#{x86Suffix(:quad)}", :quad)
when "orf"
handleX86Op("orps", :float)
when "ord"
handleX86Op("orpd", :double)
when "rshifti"
handleX86Shift("sar#{x86Suffix(:int)}", :int)
when "rshiftp"
handleX86Shift("sar#{x86Suffix(:ptr)}", :ptr)
when "rshiftq"
handleX86Shift("sar#{x86Suffix(:quad)}", :quad)
when "urshifti"
handleX86Shift("shr#{x86Suffix(:int)}", :int)
when "urshiftp"
handleX86Shift("shr#{x86Suffix(:ptr)}", :ptr)
when "urshiftq"
handleX86Shift("shr#{x86Suffix(:quad)}", :quad)
when "rrotatei"
handleX86Shift("ror#{x86Suffix(:int)}", :int)
when "rrotateq"
handleX86Shift("ror#{x86Suffix(:quad)}", :quad)
when "lrotatei"
handleX86Shift("rol#{x86Suffix(:int)}", :int)
when "lrotateq"
handleX86Shift("rol#{x86Suffix(:quad)}", :quad)
when "subi"
handleX86Sub(:int)
when "subp"
handleX86Sub(:ptr)
when "subq"
handleX86Sub(:quad)
when "xori"
handleX86Op("xor#{x86Suffix(:int)}", :int)
when "xorp"
handleX86Op("xor#{x86Suffix(:ptr)}", :ptr)
when "xorq"
handleX86Op("xor#{x86Suffix(:quad)}", :quad)
when "leap"
emitX86Lea(operands[0], operands[1], :ptr)
when "loadi"
$asm.puts "mov#{x86Suffix(:int)} #{x86LoadOperands(:int, :int)}"
when "storei"
$asm.puts "mov#{x86Suffix(:int)} #{x86Operands(:int, :int)}"
when "loadis"
if isX64
if !isIntelSyntax
$asm.puts "movslq #{x86LoadOperands(:int, :quad)}"
else
$asm.puts "movsxd #{x86LoadOperands(:int, :quad)}"
end
else
$asm.puts "mov#{x86Suffix(:int)} #{x86LoadOperands(:int, :int)}"
end
when "loadp"
$asm.puts "mov#{x86Suffix(:ptr)} #{x86LoadOperands(:ptr, :ptr)}"
when "storep"
$asm.puts "mov#{x86Suffix(:ptr)} #{x86Operands(:ptr, :ptr)}"
when "loadq"
$asm.puts "mov#{x86Suffix(:quad)} #{x86LoadOperands(:quad, :quad)}"
when "storeq"
$asm.puts "mov#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}"
when "loadb"
if !isIntelSyntax
$asm.puts "movzbl #{x86LoadOperands(:byte, :int)}"
else
$asm.puts "movzx #{x86LoadOperands(:byte, :int)}"
end
when "loadbsi"
if !isIntelSyntax
$asm.puts "movsbl #{x86LoadOperands(:byte, :int)}"
else
$asm.puts "movsx #{x86LoadOperands(:byte, :int)}"
end
when "loadbsq"
if !isIntelSyntax
$asm.puts "movsbq #{x86LoadOperands(:byte, :quad)}"
else
$asm.puts "movsx #{x86LoadOperands(:byte, :quad)}"
end
when "loadh"
if !isIntelSyntax
$asm.puts "movzwl #{x86LoadOperands(:half, :int)}"
else
$asm.puts "movzx #{x86LoadOperands(:half, :int)}"
end
when "loadhsi"
if !isIntelSyntax
$asm.puts "movswl #{x86LoadOperands(:half, :int)}"
else
$asm.puts "movsx #{x86LoadOperands(:half, :int)}"
end
when "loadhsq"
if !isIntelSyntax
$asm.puts "movswq #{x86LoadOperands(:half, :quad)}"
else
$asm.puts "movsx #{x86LoadOperands(:half, :quad)}"
end
when "storeb"
$asm.puts "mov#{x86Suffix(:byte)} #{x86Operands(:byte, :byte)}"
when "storeh"
$asm.puts "mov#{x86Suffix(:half)} #{x86Operands(:half, :half)}"
when "loadf"
$asm.puts "movss #{x86Operands(:float, :float)}"
when "loadd"
$asm.puts "movsd #{x86Operands(:double, :double)}"
when "moved"
$asm.puts "movsd #{x86Operands(:double, :double)}"
when "storef"
$asm.puts "movss #{x86Operands(:float, :float)}"
when "stored"
$asm.puts "movsd #{x86Operands(:double, :double)}"
when "addf"
handleX86AddFP(:float)
when "addd"
handleX86AddFP(:double)
when "mulf"
handleX86MulFP(:float)
when "muld"
handleX86MulFP(:double)
when "subf"
handleX86SubFP(:float)
when "subd"
handleX86SubFP(:double)
when "divf"
handleX86DivFP(:float)
when "divd"
handleX86DivFP(:double)
when "sqrtf"
$asm.puts "sqrtss #{operands[0].x86Operand(:float)}, #{operands[1].x86Operand(:float)}"
when "sqrtd"
$asm.puts "sqrtsd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "roundf"
$asm.puts "roundss $0, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "roundd"
$asm.puts "roundsd $0, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "floorf"
$asm.puts "roundss $1, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "floord"
$asm.puts "roundsd $1, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "ceilf"
$asm.puts "roundss $2, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "ceild"
$asm.puts "roundsd $2, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "truncatef"
$asm.puts "roundss $3, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "truncated"
$asm.puts "roundsd $3, #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}"
when "truncatef2i"
$asm.puts "cvttss2si #{operands[0].x86Operand(:float)}, #{operands[1].x86Operand(:quad)}"
when "truncated2i"
$asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:quad)}"
when "truncatef2q"
truncateFloatingPointToQuad(:float)
when "truncated2q"
truncateFloatingPointToQuad(:double)
when "truncatef2is"
$asm.puts "cvttss2si #{operands[0].x86Operand(:float)}, #{operands[1].x86Operand(:int)}"
when "truncatef2qs"
$asm.puts "cvttss2si #{operands[0].x86Operand(:float)}, #{operands[1].x86Operand(:quad)}"
when "truncated2is"
$asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
when "truncated2qs"
$asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:quad)}"
when "ci2d"
$asm.puts "cvtsi2sd #{orderOperands(operands[0].x86Operand(:quad), operands[1].x86Operand(:double))}"
when "ci2ds"
$asm.puts "cvtsi2sd #{orderOperands(operands[0].x86Operand(:int), operands[1].x86Operand(:double))}"
when "ci2fs"
$asm.puts "cvtsi2ss #{orderOperands(operands[0].x86Operand(:int), operands[1].x86Operand(:float))}"
when "ci2f"
$asm.puts "cvtsi2ss #{orderOperands(operands[0].x86Operand(:quad), operands[1].x86Operand(:float))}"
when "cq2f"
convertQuadToFloatingPoint(:float)
when "cq2d"
convertQuadToFloatingPoint(:double)
when "cq2fs"
$asm.puts "cvtsi2ssq #{orderOperands(operands[0].x86Operand(:quad), operands[1].x86Operand(:float))}"
when "cq2ds"
$asm.puts "cvtsi2sdq #{orderOperands(operands[0].x86Operand(:quad), operands[1].x86Operand(:double))}"
when "cd2f"
$asm.puts "cvtsd2ss #{x86Operands(:double, :float)}"
when "cf2d"
$asm.puts "cvtss2sd #{x86Operands(:float, :double)}"
when "bdeq"
$asm.puts "ucomisd #{orderOperands(operands[0].x86Operand(:double), operands[1].x86Operand(:double))}"
if operands[0] == operands[1]
# This is just a jump ordered, which is a jnp.
$asm.puts "jnp #{operands[2].asmLabel}"
else
isUnordered = LocalLabel.unique("bdeq")
$asm.puts "jp #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
$asm.puts "je #{LocalLabelReference.new(codeOrigin, operands[2]).asmLabel}"
isUnordered.lower($activeBackend)
end
when "bdneq"
handleX86FPBranch(:double, "jne", :normal)
when "bdgt"
handleX86FPBranch(:double, "ja", :normal)
when "bdgteq"
handleX86FPBranch(:double, "jae", :normal)
when "bdlt"
handleX86FPBranch(:double, "ja", :reverse)
when "bdlteq"
handleX86FPBranch(:double, "jae", :reverse)
when "bdequn"
handleX86FPBranch(:double, "je", :normal)
when "bdnequn"
$asm.puts "ucomisd #{orderOperands(operands[0].x86Operand(:double), operands[1].x86Operand(:double))}"
if operands[0] == operands[1]
# This is just a jump unordered, which is a jp.
$asm.puts "jp #{operands[2].asmLabel}"
else
isUnordered = LocalLabel.unique("bdnequn")
isEqual = LocalLabel.unique("bdnequn")
$asm.puts "jp #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
$asm.puts "je #{LocalLabelReference.new(codeOrigin, isEqual).asmLabel}"
isUnordered.lower($activeBackend)
$asm.puts "jmp #{operands[2].asmLabel}"
isEqual.lower($activeBackend)
end
when "bdgtun"
handleX86FPBranch(:double, "jb", :reverse)
when "bdgtequn"
handleX86FPBranch(:double, "jbe", :reverse)
when "bdltun"
handleX86FPBranch(:double, "jb", :normal)
when "bdltequn"
handleX86FPBranch(:double, "jbe", :normal)
when "bfeq"
$asm.puts "ucomiss #{orderOperands(operands[0].x86Operand(:float), operands[1].x86Operand(:float))}"
if operands[0] == operands[1]
# This is just a jump ordered, which is a jnp.
$asm.puts "jnp #{operands[2].asmLabel}"
else
isUnordered = LocalLabel.unique("bfeq")
$asm.puts "jp #{LocalLabelReference.new(codeOrigin, isUnordered).asmLabel}"
$asm.puts "je #{LocalLabelReference.new(codeOrigin, operands[2]).asmLabel}"
isUnordered.lower($activeBackend)
end
when "bfgt"
handleX86FPBranch(:float, "ja", :normal)
when "bfgteq"
handleX86FPBranch(:float, "jae", :normal)
when "bflt"
handleX86FPBranch(:float, "ja", :reverse)
when "bfgtun"
handleX86FPBranch(:float, "jb", :reverse)
when "bfgtequn"
handleX86FPBranch(:float, "jbe", :reverse)
when "bfltun"
handleX86FPBranch(:float, "jb", :normal)
when "bfltequn"
handleX86FPBranch(:float, "jbe", :normal)
when "btd2i"
$asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
$asm.puts "cmpl $0x80000000 #{operands[1].x86Operand(:int)}"
$asm.puts "je #{operands[2].asmLabel}"
when "td2i"
$asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
when "bcd2i"
$asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
$asm.puts "test#{x86Suffix(:int)} #{operands[1].x86Operand(:int)}, #{operands[1].x86Operand(:int)}"
$asm.puts "je #{operands[2].asmLabel}"
$asm.puts "cvtsi2sd #{operands[1].x86Operand(:int)}, %xmm7"
$asm.puts "ucomisd #{operands[0].x86Operand(:double)}, %xmm7"
$asm.puts "jp #{operands[2].asmLabel}"
$asm.puts "jne #{operands[2].asmLabel}"
when "movdz"
$asm.puts "xorpd #{operands[0].x86Operand(:double)}, #{operands[0].x86Operand(:double)}"
when "pop"
operands.each {
| op |
$asm.puts "pop #{op.x86Operand(:ptr)}"
}
when "push"
operands.each {
| op |
$asm.puts "push #{op.x86Operand(:ptr)}"
}
when "move"
handleMove
when "sxi2q"
if !isIntelSyntax
$asm.puts "movslq #{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:quad)}"
else
$asm.puts "movsxd #{orderOperands(operands[0].x86Operand(:int), operands[1].x86Operand(:quad))}"
end
when "zxi2q"
$asm.puts "mov#{x86Suffix(:int)} #{orderOperands(operands[0].x86Operand(:int), operands[1].x86Operand(:int))}"
when "nop"
$asm.puts "nop"
when "bieq"
handleX86IntBranch("je", :int)
when "bpeq"
handleX86IntBranch("je", :ptr)
when "bqeq"
handleX86IntBranch("je", :quad)
when "bineq"
handleX86IntBranch("jne", :int)
when "bpneq"
handleX86IntBranch("jne", :ptr)
when "bqneq"
handleX86IntBranch("jne", :quad)
when "bia"
handleX86IntBranch("ja", :int)
when "bpa"
handleX86IntBranch("ja", :ptr)
when "bqa"
handleX86IntBranch("ja", :quad)
when "biaeq"
handleX86IntBranch("jae", :int)
when "bpaeq"
handleX86IntBranch("jae", :ptr)
when "bqaeq"
handleX86IntBranch("jae", :quad)
when "bib"
handleX86IntBranch("jb", :int)
when "bpb"
handleX86IntBranch("jb", :ptr)
when "bqb"
handleX86IntBranch("jb", :quad)
when "bibeq"
handleX86IntBranch("jbe", :int)
when "bpbeq"
handleX86IntBranch("jbe", :ptr)
when "bqbeq"
handleX86IntBranch("jbe", :quad)
when "bigt"
handleX86IntBranch("jg", :int)
when "bpgt"
handleX86IntBranch("jg", :ptr)
when "bqgt"
handleX86IntBranch("jg", :quad)
when "bigteq"
handleX86IntBranch("jge", :int)
when "bpgteq"
handleX86IntBranch("jge", :ptr)
when "bqgteq"
handleX86IntBranch("jge", :quad)
when "bilt"
handleX86IntBranch("jl", :int)
when "bplt"
handleX86IntBranch("jl", :ptr)
when "bqlt"
handleX86IntBranch("jl", :quad)
when "bilteq"
handleX86IntBranch("jle", :int)
when "bplteq"
handleX86IntBranch("jle", :ptr)
when "bqlteq"
handleX86IntBranch("jle", :quad)
when "bbeq"
handleX86IntBranch("je", :byte)
when "bbneq"
handleX86IntBranch("jne", :byte)
when "bba"
handleX86IntBranch("ja", :byte)
when "bbaeq"
handleX86IntBranch("jae", :byte)
when "bbb"
handleX86IntBranch("jb", :byte)
when "bbbeq"
handleX86IntBranch("jbe", :byte)
when "bbgt"
handleX86IntBranch("jg", :byte)
when "bbgteq"
handleX86IntBranch("jge", :byte)
when "bblt"
handleX86IntBranch("jl", :byte)
when "bblteq"
handleX86IntBranch("jlteq", :byte)
when "btis"
handleX86BranchTest("js", :int)
when "btps"
handleX86BranchTest("js", :ptr)
when "btqs"
handleX86BranchTest("js", :quad)
when "btiz"
handleX86BranchTest("jz", :int)
when "btpz"
handleX86BranchTest("jz", :ptr)
when "btqz"
handleX86BranchTest("jz", :quad)
when "btinz"
handleX86BranchTest("jnz", :int)
when "btpnz"
handleX86BranchTest("jnz", :ptr)
when "btqnz"
handleX86BranchTest("jnz", :quad)
when "btbs"
handleX86BranchTest("js", :byte)
when "btbz"
handleX86BranchTest("jz", :byte)
when "btbnz"
handleX86BranchTest("jnz", :byte)
when "jmp"
$asm.puts "jmp #{operands[0].x86CallOperand(:ptr)}"
when "baddio"
handleX86OpBranch("add#{x86Suffix(:int)}", "jo", :int)
when "baddpo"
handleX86OpBranch("add#{x86Suffix(:ptr)}", "jo", :ptr)
when "baddqo"
handleX86OpBranch("add#{x86Suffix(:quad)}", "jo", :quad)
when "baddis"
handleX86OpBranch("add#{x86Suffix(:int)}", "js", :int)
when "baddps"
handleX86OpBranch("add#{x86Suffix(:ptr)}", "js", :ptr)
when "baddqs"
handleX86OpBranch("add#{x86Suffix(:quad)}", "js", :quad)
when "baddiz"
handleX86OpBranch("add#{x86Suffix(:int)}", "jz", :int)
when "baddpz"
handleX86OpBranch("add#{x86Suffix(:ptr)}", "jz", :ptr)
when "baddqz"
handleX86OpBranch("add#{x86Suffix(:quad)}", "jz", :quad)
when "baddinz"
handleX86OpBranch("add#{x86Suffix(:int)}", "jnz", :int)
when "baddpnz"
handleX86OpBranch("add#{x86Suffix(:ptr)}", "jnz", :ptr)
when "baddqnz"
handleX86OpBranch("add#{x86Suffix(:quad)}", "jnz", :quad)
when "bsubio"
handleX86SubBranch("jo", :int)
when "bsubis"
handleX86SubBranch("js", :int)
when "bsubiz"
handleX86SubBranch("jz", :int)
when "bsubinz"
handleX86SubBranch("jnz", :int)
when "bmulio"
handleX86OpBranch("imul#{x86Suffix(:int)}", "jo", :int)
when "bmulis"
handleX86OpBranch("imul#{x86Suffix(:int)}", "js", :int)
when "bmuliz"
handleX86OpBranch("imul#{x86Suffix(:int)}", "jz", :int)
when "bmulinz"
handleX86OpBranch("imul#{x86Suffix(:int)}", "jnz", :int)
when "borio"
handleX86OpBranch("orl", "jo", :int)
when "boris"
handleX86OpBranch("orl", "js", :int)
when "boriz"
handleX86OpBranch("orl", "jz", :int)
when "borinz"
handleX86OpBranch("orl", "jnz", :int)
when "break"
$asm.puts "int #{const(3)}"
when "call"
op = operands[0].x86CallOperand(:ptr)
if operands[0].is_a? LabelReference
operands[0].used
end
$asm.puts "call #{op}"
when "ret"
$asm.puts "ret"
when "cieq"
handleX86IntCompareSet("sete", :int)
when "cbeq"
handleX86IntCompareSet("sete", :byte)
when "cpeq"
handleX86IntCompareSet("sete", :ptr)
when "cqeq"
handleX86IntCompareSet("sete", :quad)
when "cineq"
handleX86IntCompareSet("setne", :int)
when "cbneq"
handleX86IntCompareSet("setne", :byte)
when "cpneq"
handleX86IntCompareSet("setne", :ptr)
when "cqneq"
handleX86IntCompareSet("setne", :quad)
when "cia"
handleX86IntCompareSet("seta", :int)
when "cba"
handleX86IntCompareSet("seta", :byte)
when "cpa"
handleX86IntCompareSet("seta", :ptr)
when "cqa"
handleX86IntCompareSet("seta", :quad)
when "ciaeq"
handleX86IntCompareSet("setae", :int)
when "cbaeq"
handleX86IntCompareSet("setae", :byte)
when "cpaeq"
handleX86IntCompareSet("setae", :ptr)
when "cqaeq"
handleX86IntCompareSet("setae", :quad)
when "cib"
handleX86IntCompareSet("setb", :int)
when "cbb"
handleX86IntCompareSet("setb", :byte)
when "cpb"
handleX86IntCompareSet("setb", :ptr)
when "cqb"
handleX86IntCompareSet("setb", :quad)
when "cibeq"
handleX86IntCompareSet("setbe", :int)
when "cbbeq"
handleX86IntCompareSet("setbe", :byte)
when "cpbeq"
handleX86IntCompareSet("setbe", :ptr)
when "cqbeq"
handleX86IntCompareSet("setbe", :quad)
when "cigt"
handleX86IntCompareSet("setg", :int)
when "cbgt"
handleX86IntCompareSet("setg", :byte)
when "cpgt"
handleX86IntCompareSet("setg", :ptr)
when "cqgt"
handleX86IntCompareSet("setg", :quad)
when "cigteq"
handleX86IntCompareSet("setge", :int)
when "cbgteq"
handleX86IntCompareSet("setge", :byte)
when "cpgteq"
handleX86IntCompareSet("setge", :ptr)
when "cqgteq"
handleX86IntCompareSet("setge", :quad)
when "cilt"
handleX86IntCompareSet("setl", :int)
when "cblt"
handleX86IntCompareSet("setl", :byte)
when "cplt"
handleX86IntCompareSet("setl", :ptr)
when "cqlt"
handleX86IntCompareSet("setl", :quad)
when "cilteq"
handleX86IntCompareSet("setle", :int)
when "cblteq"
handleX86IntCompareSet("setle", :byte)
when "cplteq"
handleX86IntCompareSet("setle", :ptr)
when "cqlteq"
handleX86IntCompareSet("setle", :quad)
when "cfeq"
handleX86FPCompareSet(:float, :eq)
when "cdeq"
handleX86FPCompareSet(:double, :eq)
when "cfneq"
handleX86FPCompareSet(:float, "setne")
when "cdneq"
handleX86FPCompareSet(:double, "setne")
when "cfnequn"
handleX86FPCompareSet(:float, :nequn)
when "cdnequn"
handleX86FPCompareSet(:double, :nequn)
when "cfgt"
handleX86FPCompareSet(:float, "seta")
when "cdgt"
handleX86FPCompareSet(:double, "seta")
when "cfgteq"
handleX86FPCompareSet(:float, "setae")
when "cdgteq"
handleX86FPCompareSet(:double, "setae")
when "cflt"
handleX86FPCompareSet(:float, "seta", :reverse)
when "cdlt"
handleX86FPCompareSet(:double, "seta", :reverse)
when "cflteq"
handleX86FPCompareSet(:float, "setae", :reverse)
when "cdlteq"
handleX86FPCompareSet(:double, "setae", :reverse)
when "tis"
handleX86SetTest("sets", :int)
when "tiz"
handleX86SetTest("setz", :int)
when "tinz"
handleX86SetTest("setnz", :int)
when "tps"
handleX86SetTest("sets", :ptr)
when "tpz"
handleX86SetTest("setz", :ptr)
when "tpnz"
handleX86SetTest("setnz", :ptr)
when "tqs"
handleX86SetTest("sets", :quad)
when "tqz"
handleX86SetTest("setz", :quad)
when "tqnz"
handleX86SetTest("setnz", :quad)
when "tbs"
handleX86SetTest("sets", :byte)
when "tbz"
handleX86SetTest("setz", :byte)
when "tbnz"
handleX86SetTest("setnz", :byte)
when "peek"
handleX86Peek()
when "poke"
handleX86Poke()
when "cdqi"
$asm.puts "cdq"
when "cqoq"
$asm.puts "cqo"
when "idivi"
$asm.puts "idiv#{x86Suffix(:int)} #{operands[0].x86Operand(:int)}"
when "udivi"
$asm.puts "div#{x86Suffix(:int)} #{operands[0].x86Operand(:int)}"
when "idivq"
$asm.puts "idiv#{x86Suffix(:quad)} #{operands[0].x86Operand(:quad)}"
when "udivq"
$asm.puts "div#{x86Suffix(:quad)} #{operands[0].x86Operand(:quad)}"
when "popcnti"
$asm.puts "popcnt#{x86Suffix(:int)} #{x86Operands(:int, :int)}"
when "popcntq"
$asm.puts "popcnt#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}"
when "tzcnti"
countTrailingZeros(:int)
when "tzcntq"
countTrailingZeros(:quad)
when "lzcnti"
countLeadingZeros(:int)
when "lzcntq"
countLeadingZeros(:quad)
when "fii2d"
$asm.puts "movd #{operands[0].x86Operand(:int)}, #{operands[2].x86Operand(:double)}"
$asm.puts "movd #{operands[1].x86Operand(:int)}, %xmm7"
$asm.puts "psllq $32, %xmm7"
$asm.puts "por %xmm7, #{operands[2].x86Operand(:double)}"
when "fd2ii"
$asm.puts "movd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}"
$asm.puts "movsd #{operands[0].x86Operand(:double)}, %xmm7"
$asm.puts "psrlq $32, %xmm7"
$asm.puts "movd %xmm7, #{operands[2].x86Operand(:int)}"
when "fq2d"
if !isIntelSyntax
$asm.puts "movq #{operands[0].x86Operand(:quad)}, #{operands[1].x86Operand(:double)}"
else
# MASM does not accept register operands with movq.
# Debugging shows that movd actually moves a qword when using MASM.
$asm.puts "movd #{operands[1].x86Operand(:double)}, #{operands[0].x86Operand(:quad)}"
end
when "fd2q"
if !isIntelSyntax
$asm.puts "movq #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:quad)}"
else
# MASM does not accept register operands with movq.
# Debugging shows that movd actually moves a qword when using MASM.
$asm.puts "movd #{operands[1].x86Operand(:quad)}, #{operands[0].x86Operand(:double)}"
end
when "fi2f"
$asm.puts "movd #{x86Operands(:int, :float)}"
when "ff2i"
$asm.puts "movd #{x86Operands(:float, :int)}"
when "bo"
$asm.puts "jo #{operands[0].asmLabel}"
when "bs"
$asm.puts "js #{operands[0].asmLabel}"
when "bz"
$asm.puts "jz #{operands[0].asmLabel}"
when "bnz"
$asm.puts "jnz #{operands[0].asmLabel}"
when "leai"
$asm.puts "lea#{x86Suffix(:int)} #{orderOperands(operands[0].x86AddressOperand(:int), operands[1].x86Operand(:int))}"
when "leap"
$asm.puts "lea#{x86Suffix(:ptr)} #{orderOperands(operands[0].x86AddressOperand(:ptr), operands[1].x86Operand(:ptr))}"
when "memfence"
sp = RegisterID.new(nil, "sp")
if isIntelSyntax
$asm.puts "mfence"
else
$asm.puts "lock; orl $0, (#{sp.x86Operand(:ptr)})"
end
when "absf"
$asm.puts "movl #{orderOperands("$0x80000000", X64_SCRATCH_REGISTER.x86Operand(:int))}"
$asm.puts "movd #{orderOperands(X64_SCRATCH_REGISTER.x86Operand(:int), operands[1].x86Operand(:float))}"
$asm.puts "andnps #{orderOperands(operands[0].x86Operand(:float), operands[1].x86Operand(:float))}"
when "absd"
$asm.puts "movq #{orderOperands("$0x8000000000000000", X64_SCRATCH_REGISTER.x86Operand(:quad))}"
$asm.puts "movd #{orderOperands(X64_SCRATCH_REGISTER.x86Operand(:quad), operands[1].x86Operand(:double))}"
$asm.puts "andnps #{orderOperands(operands[0].x86Operand(:double), operands[1].x86Operand(:double))}"
when "negf"
$asm.puts "movl #{orderOperands("$0x80000000", X64_SCRATCH_REGISTER.x86Operand(:int))}"
$asm.puts "movd #{orderOperands(X64_SCRATCH_REGISTER.x86Operand(:int), operands[1].x86Operand(:float))}"
$asm.puts "xorps #{orderOperands(operands[0].x86Operand(:float), operands[1].x86Operand(:float))}"
when "negd"
$asm.puts "movq #{orderOperands("$0x8000000000000000", X64_SCRATCH_REGISTER.x86Operand(:quad))}"
$asm.puts "movd #{orderOperands(X64_SCRATCH_REGISTER.x86Operand(:quad), operands[1].x86Operand(:double))}"
$asm.puts "xorpd #{orderOperands(operands[0].x86Operand(:double), operands[1].x86Operand(:double))}"
when "tls_loadp"
raise "tls_loadp is only supported on x64" unless isX64
if operands[0].immediate?
mem = "%gs:#{operands[0].value * 8}"
else
mem = BaseIndex.new(codeOrigin, nil, operands[0], 8, "%gs:").x86AddressOperand(:quad)
end
$asm.puts "movq #{orderOperands(mem, operands[1].x86Operand(:quad))}"
when "tls_loadp"
raise "tls_loadp is only supported on x64" unless isX64
if operands[0].immediate?
mem = "%gs:#{operands[0].value * x86Bytes(:ptr)}"
else
mem = BaseIndex.new(codeOrigin, nil, operands[0], x86Bytes(:ptr), "%gs:").x86AddressOperand(:quad)
end
$asm.puts "mov#{x86Suffix(:ptr)} #{orderOperands(mem, operands[1].x86Operand(:quad))}"
when "tls_storep"
raise "tls_loadp is only supported on x64" unless isX64
if operands[1].immediate?
mem = "%gs:#{operands[1].value * x86Bytes(:ptr)}"
else
mem = BaseIndex.new(codeOrigin, nil, operands[1], x86Bytes(:ptr), "%gs:").x86AddressOperand(:ptr)
end
$asm.puts "mov#{x86Suffix(:ptr)} #{orderOperands(operands[0].x86Operand(:ptr), mem)}"
else
lowerDefault
end
end
end