# Copyright (C) 2012-2020 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(:ptr)})" 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 if src.offset != 0 $asm.puts "add#{x86Suffix(kind)} #{orderOperands(const(src.offset), dst.x86Operand(kind))}" 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 "notq" $asm.puts "not#{x86Suffix(:quad)} #{x86Operands(:quad)}" 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", "atomicloadi" $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", "atomicloadq" $asm.puts "mov#{x86Suffix(:quad)} #{x86LoadOperands(:quad, :quad)}" when "storeq" $asm.puts "mov#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}" when "loadb", "atomicloadb" 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", "atomicloadh" 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 "sxb2i" if !isIntelSyntax $asm.puts "movsbl #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(:int)}" else $asm.puts "movsx #{orderOperands(operands[0].x86Operand(:byte), operands[1].x86Operand(:int))}" end when "sxh2i" if !isIntelSyntax $asm.puts "movswl #{operands[0].x86Operand(:half)}, #{operands[1].x86Operand(:int)}" else $asm.puts "movsx #{orderOperands(operands[0].x86Operand(:half), operands[1].x86Operand(:int))}" end when "sxb2q" if !isIntelSyntax $asm.puts "movsbq #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(:quad)}" else $asm.puts "movsxd #{orderOperands(operands[0].x86Operand(:byte), operands[1].x86Operand(:quad))}" end when "sxh2q" if !isIntelSyntax $asm.puts "movswq #{operands[0].x86Operand(:half)}, #{operands[1].x86Operand(:quad)}" else $asm.puts "movsxd #{orderOperands(operands[0].x86Operand(:half), operands[1].x86Operand(:quad))}" end 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", "fence" 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)}" when "atomicxchgaddb" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:byte)} #{x86Operands(:byte, :byte)}" when "atomicxchgaddh" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:half)} #{x86Operands(:half, :half)}" when "atomicxchgaddi" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:int)} #{x86Operands(:int, :int)}" when "atomicxchgaddq" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}" when "atomicxchgsubb" $asm.puts "neg#{x86Suffix(:byte)} #{operands[0].x86Operand(:byte)}" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:byte)} #{x86Operands(:byte, :byte)}" when "atomicxchgsubh" $asm.puts "neg#{x86Suffix(:half)} #{operands[0].x86Operand(:half)}" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:half)} #{x86Operands(:half, :half)}" when "atomicxchgsubi" $asm.puts "neg#{x86Suffix(:int)} #{operands[0].x86Operand(:int)}" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:int)} #{x86Operands(:int, :int)}" when "atomicxchgsubq" $asm.puts "neg#{x86Suffix(:quad)} #{operands[0].x86Operand(:quad)}" $asm.puts "lock" $asm.puts "xadd#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}" when "atomicxchgb" $asm.puts "xchg#{x86Suffix(:byte)} #{x86Operands(:byte, :byte)}" when "atomicxchgh" $asm.puts "xchg#{x86Suffix(:half)} #{x86Operands(:half, :half)}" when "atomicxchgi" $asm.puts "xchg#{x86Suffix(:int)} #{x86Operands(:int, :int)}" when "atomicxchgq" $asm.puts "xchg#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}" when "batomicweakcasb" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:byte)} #{orderOperands(operands[1].x86Operand(:byte), operands[2].x86Operand(:byte))}" $asm.puts "jne #{operands.last.asmLabel}" when "batomicweakcash" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:half)} #{orderOperands(operands[1].x86Operand(:half), operands[2].x86Operand(:half))}" $asm.puts "jne #{operands.last.asmLabel}" when "batomicweakcasi" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:int)} #{orderOperands(operands[1].x86Operand(:int), operands[2].x86Operand(:int))}" $asm.puts "jne #{operands.last.asmLabel}" when "batomicweakcasq" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:quad)} #{orderOperands(operands[1].x86Operand(:quad), operands[2].x86Operand(:quad))}" $asm.puts "jne #{operands.last.asmLabel}" when "atomicweakcasb" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:byte)} #{orderOperands(operands[1].x86Operand(:byte), operands[2].x86Operand(:byte))}" when "atomicweakcash" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:half)} #{orderOperands(operands[1].x86Operand(:half), operands[2].x86Operand(:half))}" when "atomicweakcasi" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:int)} #{orderOperands(operands[1].x86Operand(:int), operands[2].x86Operand(:int))}" when "atomicweakcasq" raise "first operand must be t0" unless operands[0].is_a? RegisterID and operands[0].name == 't0' $asm.puts "lock" $asm.puts "cmpxchg#{x86Suffix(:quad)} #{orderOperands(operands[1].x86Operand(:quad), operands[2].x86Operand(:quad))}" else lowerDefault end end end