| # 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 "orh" |
| handleX86Op("or#{x86Suffix(:half)}", :half) |
| 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 |
| |