# Copyright (C) 2012 Apple Inc. All rights reserved. # Copyright (C) 2015 The Qt Company Ltd # # 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" def isX64 case $activeBackend when "X86" false when "X86_64" true else raise "bad value for $activeBackend: #{$activeBackend}" end end def useX87 case $activeBackend when "X86" true when "X86_64" false else raise "bad value for $activeBackend: #{$activeBackend}" end end class SpecialRegister < NoChildren def x86Operand(kind) raise unless @name =~ /^r/ raise unless isX64 case kind when :half "%" + @name + "w" when :int "%" + @name + "d" when :ptr "%" + @name when :quad "%" + @name else raise end end def x86CallOperand(kind) # Call operands are not allowed to be partial registers. "*#{x86Operand(:quad)}" end end X64_SCRATCH_REGISTER = SpecialRegister.new("r11") class RegisterID def supports8BitOnX86 case name when "t0", "a0", "r0", "t1", "a1", "r1", "t2", "t3" true when "cfr", "ttnr", "tmr" false when "t4", "t5" isX64 else raise end end def x86Operand(kind) case name when "t0", "a0", "r0" case kind when :byte "%al" when :half "%ax" when :int "%eax" when :ptr isX64 ? "%rax" : "%eax" when :quad isX64 ? "%rax" : raise else raise end when "t1", "a1", "r1" case kind when :byte "%dl" when :half "%dx" when :int "%edx" when :ptr isX64 ? "%rdx" : "%edx" when :quad isX64 ? "%rdx" : raise else raise end when "t2" case kind when :byte "%cl" when :half "%cx" when :int "%ecx" when :ptr isX64 ? "%rcx" : "%ecx" when :quad isX64 ? "%rcx" : raise else raise end when "t3" case kind when :byte "%bl" when :half "%bx" when :int "%ebx" when :ptr isX64 ? "%rbx" : "%ebx" when :quad isX64 ? "%rbx" : raise else raise end when "t4" case kind when :byte "%sil" when :half "%si" when :int "%esi" when :ptr isX64 ? "%rsi" : "%esi" when :quad isX64 ? "%rsi" : raise else raise end when "cfr" if isX64 case kind when :half "%r13w" when :int "%r13d" when :ptr "%r13" when :quad "%r13" else raise end else case kind when :byte "%dil" when :half "%di" when :int "%edi" when :ptr "%edi" else raise end end when "sp" case kind when :byte "%spl" when :half "%sp" when :int "%esp" when :ptr isX64 ? "%rsp" : "%esp" when :quad isX64 ? "%rsp" : raise else raise end when "t5" raise "Cannot use #{name} in 32-bit X86 at #{codeOriginString}" unless isX64 case kind when :byte "%dil" when :half "%di" when :int "%edi" when :ptr "%rdi" when :quad "%rdi" end when "t6" raise "Cannot use #{name} in 32-bit X86 at #{codeOriginString}" unless isX64 case kind when :half "%r10w" when :int "%r10d" when :ptr "%r10" when :quad "%r10" end when "csr1" raise "Cannot use #{name} in 32-bit X86 at #{codeOriginString}" unless isX64 case kind when :half "%r14w" when :int "%r14d" when :ptr "%r14" when :quad "%r14" end when "csr2" raise "Cannot use #{name} in 32-bit X86 at #{codeOriginString}" unless isX64 case kind when :half "%r15w" when :int "%r15d" when :ptr "%r15" when :quad "%r15" end else raise "Bad register #{name} for X86 at #{codeOriginString}" end end def x86CallOperand(kind) isX64 ? "*#{x86Operand(:quad)}" : "*#{x86Operand(:ptr)}" end end class FPRegisterID def x86Operand(kind) raise unless kind == :double raise if useX87 case name when "ft0", "fa0", "fr" "%xmm0" when "ft1", "fa1" "%xmm1" when "ft2", "fa2" "%xmm2" when "ft3", "fa3" "%xmm3" when "ft4" "%xmm4" when "ft5" "%xmm5" else raise "Bad register #{name} for X86 at #{codeOriginString}" end end def x87DefaultStackPosition case name when "ft0", "fr" 0 when "ft1" 1 when "ft2", "ft3", "ft4", "ft5" raise "Unimplemented register #{name} for X86 at #{codeOriginString}" else raise "Bad register #{name} for X86 at #{codeOriginString}" end end def x87Operand(offset) raise unless useX87 raise unless offset == 0 or offset == 1 "%st(#{x87DefaultStackPosition + offset})" end def x86CallOperand(kind) "*#{x86Operand(kind)}" end end class Immediate def validX86Immediate? if isX64 value >= -0x80000000 and value <= 0x7fffffff else true end end def x86Operand(kind) "$#{value}" end def x86CallOperand(kind) "#{value}" end end class Address def supports8BitOnX86 true end def x86AddressOperand(addressKind) "#{offset.value}(#{base.x86Operand(addressKind)})" end def x86Operand(kind) x86AddressOperand(:ptr) end def x86CallOperand(kind) "*#{x86Operand(kind)}" end end class BaseIndex def supports8BitOnX86 true end def x86AddressOperand(addressKind) "#{offset.value}(#{base.x86Operand(addressKind)}, #{index.x86Operand(addressKind)}, #{scale})" end def x86Operand(kind) x86AddressOperand(:ptr) end def x86CallOperand(kind) "*#{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) "*#{address.value}" end end class LabelReference def x86CallOperand(kind) asmLabel end end class LocalLabelReference 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 end class Instruction def x86Operands(*kinds) raise unless kinds.size == operands.size result = [] kinds.size.times { | idx | result << operands[idx].x86Operand(kinds[idx]) } result.join(", ") end def x86Suffix(kind) case kind when :byte "b" when :half "w" when :int "l" when :ptr isX64 ? "q" : "l" when :quad isX64 ? "q" : raise when :double not useX87 ? "sd" : raise 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 :double 8 else raise end end def handleX86OpWithNumOperands(opcode, kind, numOperands) if numOperands == 3 if operands[0] == operands[2] $asm.puts "#{opcode} #{operands[1].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" elsif operands[1] == operands[2] $asm.puts "#{opcode} #{operands[0].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" else $asm.puts "mov#{x86Suffix(kind)} #{operands[0].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" $asm.puts "#{opcode} #{operands[1].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" end else $asm.puts "#{opcode} #{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] == RegisterID.forName(nil, "t2") $asm.puts "#{opcode} #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(kind)}" else cx = RegisterID.forName(nil, "t2") $asm.puts "xchg#{x86Suffix(:ptr)} #{operands[0].x86Operand(:ptr)}, #{cx.x86Operand(:ptr)}" $asm.puts "#{opcode} %cl, #{operands[1].x86Operand(kind)}" $asm.puts "xchg#{x86Suffix(:ptr)} #{operands[0].x86Operand(:ptr)}, #{cx.x86Operand(:ptr)}" end end def handleX86DoubleBranch(branchOpcode, mode) if useX87 handleX87Compare(mode) else case mode when :normal $asm.puts "ucomisd #{operands[1].x86Operand(:double)}, #{operands[0].x86Operand(:double)}" when :reverse $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}" else raise mode.inspect end 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)} #{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)} #{operands[0].x86Operand(kind)}" else $asm.puts "cmp#{x86Suffix(kind)} #{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)}" $asm.puts "movzbl #{operand.x86Operand(:byte)}, #{operand.x86Operand(:int)}" else ax = RegisterID.new(nil, "t0") $asm.puts "xchg#{x86Suffix(:ptr)} #{operand.x86Operand(:ptr)}, #{ax.x86Operand(:ptr)}" $asm.puts "#{setOpcode} %al" $asm.puts "movzbl %al, %eax" $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 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)} $0, #{value.x86Operand(kind)}" end else $asm.puts "test#{x86Suffix(kind)} #{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)} #{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)} #{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 unless operands[1] == operands[2] $asm.puts "mov#{x86Suffix(kind)} #{operands[1].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" end else $asm.puts "lea#{x86Suffix(kind)} #{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)} #{operands[1].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" else $asm.puts "lea#{x86Suffix(kind)} (#{operands[0].x86Operand(kind)}, #{operands[1].x86Operand(kind)}), #{operands[2].x86Operand(kind)}" 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 and operands[1] == operands[2] $asm.puts "neg#{x86Suffix(kind)} #{operands[2].x86Operand(kind)}" $asm.puts "add#{x86Suffix(kind)} #{operands[0].x86Operand(kind)}, #{operands[2].x86Operand(kind)}" else handleX86Op("sub#{x86Suffix(kind)}", kind) end end def handleX86Mul(kind) if operands.size == 3 and operands[0].is_a? Immediate $asm.puts "imul#{x86Suffix(kind)} #{x86Operands(kind, kind, kind)}" else # FIXME: could do some peephole in case the left operand is immediate and it's # a power of two. handleX86Op("imul#{x86Suffix(kind)}", kind) end 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 handleX87Compare(mode) case mode when :normal if (operands[0].x87DefaultStackPosition == 0) $asm.puts "fucomi #{operands[1].x87Operand(0)}" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fucomip #{operands[1].x87Operand(1)}" end when :reverse if (operands[1].x87DefaultStackPosition == 0) $asm.puts "fucomi #{operands[0].x87Operand(0)}" else $asm.puts "fld #{operands[1].x87Operand(0)}" $asm.puts "fucomip #{operands[0].x87Operand(1)}" end else raise mode.inspect end end def handleX87BinOp(opcode, opcodereverse) if (operands[1].x87DefaultStackPosition == 0) $asm.puts "#{opcode} #{operands[0].x87Operand(0)}, %st" elsif (operands[0].x87DefaultStackPosition == 0) $asm.puts "#{opcodereverse} %st, #{operands[1].x87Operand(0)}" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "#{opcodereverse}p %st, #{operands[1].x87Operand(1)}" end end def lowerX86 raise unless $activeBackend == "X86" lowerX86Common end def lowerX86_64 raise unless $activeBackend == "X86_64" lowerX86Common end def lowerX86Common $asm.codeOrigin codeOriginString if $enableCodeOriginComments $asm.annotation annotation if $enableInstrAnnotations case opcode when "addi" handleX86Add(:int) when "addp" handleX86Add(:ptr) when "addq" handleX86Add(:quad) when "andi" handleX86Op("andl", :int) when "andp" handleX86Op("and#{x86Suffix(:ptr)}", :ptr) when "andq" handleX86Op("and#{x86Suffix(:quad)}", :quad) when "lshifti" handleX86Shift("sall", :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 "negl #{x86Operands(:int)}" when "negp" $asm.puts "neg#{x86Suffix(:ptr)} #{x86Operands(:ptr)}" when "negq" $asm.puts "neg#{x86Suffix(:quad)} #{x86Operands(:quad)}" when "noti" $asm.puts "notl #{x86Operands(:int)}" when "ori" handleX86Op("orl", :int) when "orp" handleX86Op("or#{x86Suffix(:ptr)}", :ptr) when "orq" handleX86Op("or#{x86Suffix(:quad)}", :quad) when "rshifti" handleX86Shift("sarl", :int) when "rshiftp" handleX86Shift("sar#{x86Suffix(:ptr)}", :ptr) when "rshiftq" handleX86Shift("sar#{x86Suffix(:quad)}", :quad) when "urshifti" handleX86Shift("shrl", :int) when "urshiftp" handleX86Shift("shr#{x86Suffix(:ptr)}", :ptr) when "urshiftq" handleX86Shift("shr#{x86Suffix(:quad)}", :quad) when "subi" handleX86Sub(:int) when "subp" handleX86Sub(:ptr) when "subq" handleX86Sub(:quad) when "xori" handleX86Op("xorl", :int) when "xorp" handleX86Op("xor#{x86Suffix(:ptr)}", :ptr) when "xorq" handleX86Op("xor#{x86Suffix(:quad)}", :quad) when "loadi", "storei" $asm.puts "movl #{x86Operands(:int, :int)}" when "loadis" if isX64 $asm.puts "movslq #{x86Operands(:int, :quad)}" else $asm.puts "movl #{x86Operands(:int, :int)}" end when "loadp", "storep" $asm.puts "mov#{x86Suffix(:ptr)} #{x86Operands(:ptr, :ptr)}" when "loadq", "storeq" $asm.puts "mov#{x86Suffix(:quad)} #{x86Operands(:quad, :quad)}" when "loadb" $asm.puts "movzbl #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(:int)}" when "loadbs" $asm.puts "movsbl #{operands[0].x86Operand(:byte)}, #{operands[1].x86Operand(:int)}" when "loadh" $asm.puts "movzwl #{operands[0].x86Operand(:half)}, #{operands[1].x86Operand(:int)}" when "loadhs" $asm.puts "movswl #{operands[0].x86Operand(:half)}, #{operands[1].x86Operand(:int)}" when "storeb" $asm.puts "movb #{x86Operands(:byte, :byte)}" when "loadd" if useX87 $asm.puts "fldl #{operands[0].x86Operand(:double)}" $asm.puts "fstp #{operands[1].x87Operand(1)}" else $asm.puts "movsd #{x86Operands(:double, :double)}" end when "moved" if useX87 if (operands[0].x87DefaultStackPosition == 0) $asm.puts "fst #{operands[1].x87Operand(0)}" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fstp #{operands[1].x87Operand(1)}" end else $asm.puts "movsd #{x86Operands(:double, :double)}" end when "stored" if useX87 if (operands[0].x87DefaultStackPosition == 0) $asm.puts "fstl #{operands[1].x86Operand(:double)}" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fstpl #{operands[1].x86Operand(:double)}" end else $asm.puts "movsd #{x86Operands(:double, :double)}" end when "addd" if useX87 handleX87BinOp("fadd", "fadd") else $asm.puts "addsd #{x86Operands(:double, :double)}" end when "muld" if useX87 handleX87BinOp("fmul", "fmul") else $asm.puts "mulsd #{x86Operands(:double, :double)}" end when "subd" if useX87 handleX87BinOp("fsub", "fsubr") else $asm.puts "subsd #{x86Operands(:double, :double)}" end when "divd" if useX87 handleX87BinOp("fdiv", "fdivr") else $asm.puts "divsd #{x86Operands(:double, :double)}" end when "sqrtd" if useX87 $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fsqrtl" $asm.puts "fstp #{operands[1].x87Operand(1)}" else $asm.puts "sqrtsd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}" end when "ci2d" if useX87 sp = RegisterID.new(nil, "sp") $asm.puts "movl #{operands[0].x86Operand(:int)}, -4(#{sp.x86Operand(:ptr)})" $asm.puts "fildl -4(#{sp.x86Operand(:ptr)})" $asm.puts "fstp #{operands[1].x87Operand(1)}" else $asm.puts "cvtsi2sd #{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:double)}" end when "bdeq" if useX87 handleX87Compare(:normal) else $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}" end 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 #{LabelReference.new(codeOrigin, isUnordered).asmLabel}" $asm.puts "je #{LabelReference.new(codeOrigin, operands[2]).asmLabel}" isUnordered.lower("X86") end when "bdneq" handleX86DoubleBranch("jne", :normal) when "bdgt" handleX86DoubleBranch("ja", :normal) when "bdgteq" handleX86DoubleBranch("jae", :normal) when "bdlt" handleX86DoubleBranch("ja", :reverse) when "bdlteq" handleX86DoubleBranch("jae", :reverse) when "bdequn" handleX86DoubleBranch("je", :normal) when "bdnequn" if useX87 handleX87Compare(:normal) else $asm.puts "ucomisd #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:double)}" end 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 #{LabelReference.new(codeOrigin, isUnordered).asmLabel}" $asm.puts "je #{LabelReference.new(codeOrigin, isEqual).asmLabel}" isUnordered.lower("X86") $asm.puts "jmp #{operands[2].asmLabel}" isEqual.lower("X86") end when "bdgtun" handleX86DoubleBranch("jb", :reverse) when "bdgtequn" handleX86DoubleBranch("jbe", :reverse) when "bdltun" handleX86DoubleBranch("jb", :normal) when "bdltequn" handleX86DoubleBranch("jbe", :normal) when "btd2i" # FIXME: unused and unimplemented for x87 raise if useX87 $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" # FIXME: unused and unimplemented for x87 raise if useX87 $asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}" when "bcd2i" if useX87 sp = RegisterID.new(nil, "sp") if (operands[0].x87DefaultStackPosition == 0) $asm.puts "fistl -4(#{sp.x86Operand(:ptr)})" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fistpl -4(#{sp.x86Operand(:ptr)})" end $asm.puts "movl -4(#{sp.x86Operand(:ptr)}), #{operands[1].x86Operand(:int)}" $asm.puts "testl #{operands[1].x86Operand(:int)}, #{operands[1].x86Operand(:int)}" $asm.puts "je #{operands[2].asmLabel}" $asm.puts "fildl -4(#{sp.x86Operand(:ptr)})" $asm.puts "fucomip #{operands[0].x87Operand(1)}" $asm.puts "jp #{operands[2].asmLabel}" $asm.puts "jne #{operands[2].asmLabel}" else $asm.puts "cvttsd2si #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:int)}" $asm.puts "testl #{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}" end when "movdz" if useX87 $asm.puts "fldzl" $asm.puts "fstp #{operands[0].x87Operand(1)}" else $asm.puts "xorpd #{operands[0].x86Operand(:double)}, #{operands[0].x86Operand(:double)}" end when "pop" $asm.puts "pop #{operands[0].x86Operand(:ptr)}" when "push" $asm.puts "push #{operands[0].x86Operand(:ptr)}" when "move" handleMove when "sxi2q" $asm.puts "movslq #{operands[0].x86Operand(:int)}, #{operands[1].x86Operand(:quad)}" when "zxi2q" $asm.puts "movl #{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("addl", "jo", :int) when "baddpo" handleX86OpBranch("add#{x86Suffix(:ptr)}", "jo", :ptr) when "baddqo" handleX86OpBranch("add#{x86Suffix(:quad)}", "jo", :quad) when "baddis" handleX86OpBranch("addl", "js", :int) when "baddps" handleX86OpBranch("add#{x86Suffix(:ptr)}", "js", :ptr) when "baddqs" handleX86OpBranch("add#{x86Suffix(:quad)}", "js", :quad) when "baddiz" handleX86OpBranch("addl", "jz", :int) when "baddpz" handleX86OpBranch("add#{x86Suffix(:ptr)}", "jz", :ptr) when "baddqz" handleX86OpBranch("add#{x86Suffix(:quad)}", "jz", :quad) when "baddinz" handleX86OpBranch("addl", "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("imull", "jo", :int) when "bmulis" handleX86OpBranch("imull", "js", :int) when "bmuliz" handleX86OpBranch("imull", "jz", :int) when "bmulinz" handleX86OpBranch("imull", "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 $3" when "call" if useX87 2.times { | offset | $asm.puts "ffree %st(#{offset})" } end $asm.puts "call #{operands[0].x86CallOperand(:ptr)}" 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 "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" sp = RegisterID.new(nil, "sp") $asm.puts "mov#{x86Suffix(:ptr)} #{operands[0].value * x86Bytes(:ptr)}(#{sp.x86Operand(:ptr)}), #{operands[1].x86Operand(:ptr)}" when "peekq" sp = RegisterID.new(nil, "sp") $asm.puts "mov#{x86Suffix(:quad)} #{operands[0].value * x86Bytes(:quad)}(#{sp.x86Operand(:ptr)}), #{operands[1].x86Operand(:quad)}" when "poke" sp = RegisterID.new(nil, "sp") $asm.puts "mov#{x86Suffix(:ptr)} #{operands[0].x86Operand(:ptr)}, #{operands[1].value * x86Bytes(:ptr)}(#{sp.x86Operand(:ptr)})" when "pokeq" sp = RegisterID.new(nil, "sp") $asm.puts "mov#{x86Suffix(:quad)} #{operands[0].x86Operand(:quad)}, #{operands[1].value * x86Bytes(:quad)}(#{sp.x86Operand(:ptr)})" when "cdqi" $asm.puts "cdq" when "idivi" $asm.puts "idivl #{operands[0].x86Operand(:int)}" when "fii2d" if useX87 sp = RegisterID.new(nil, "sp") $asm.puts "movl #{operands[0].x86Operand(:int)}, -8(#{sp.x86Operand(:ptr)})" $asm.puts "movl #{operands[1].x86Operand(:int)}, -4(#{sp.x86Operand(:ptr)})" $asm.puts "fldl -8(#{sp.x86Operand(:ptr)})" $asm.puts "fstp #{operands[2].x87Operand(1)}" else $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)}" end when "fd2ii" if useX87 sp = RegisterID.new(nil, "sp") if (operands[0].x87DefaultStackPosition == 0) $asm.puts "fstl -8(#{sp.x86Operand(:ptr)})" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fstpl -8(#{sp.x86Operand(:ptr)})" end $asm.puts "movl -8(#{sp.x86Operand(:ptr)}), #{operands[1].x86Operand(:int)}" $asm.puts "movl -4(#{sp.x86Operand(:ptr)}), #{operands[2].x86Operand(:int)}" else $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)}" end when "fq2d" if useX87 sp = RegisterID.new(nil, "sp") $asm.puts "movq #{operands[0].x86Operand(:quad)}, -8(#{sp.x86Operand(:ptr)})" $asm.puts "fldl -8(#{sp.x86Operand(:ptr)})" $asm.puts "fstp #{operands[1].x87Operand(1)}" else $asm.puts "movq #{operands[0].x86Operand(:quad)}, #{operands[1].x86Operand(:double)}" end when "fd2q" if useX87 sp = RegisterID.new(nil, "sp") if (operands[0].x87DefaultStackPosition == 0) $asm.puts "fstl -8(#{sp.x86Operand(:ptr)})" else $asm.puts "fld #{operands[0].x87Operand(0)}" $asm.puts "fstpl -8(#{sp.x86Operand(:ptr)})" end $asm.puts "movq -8(#{sp.x86Operand(:ptr)}), #{operands[1].x86Operand(:quad)}" else $asm.puts "movq #{operands[0].x86Operand(:double)}, #{operands[1].x86Operand(:quad)}" end 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 "leal #{operands[0].x86AddressOperand(:int)}, #{operands[1].x86Operand(:int)}" when "leap" $asm.puts "lea#{x86Suffix(:ptr)} #{operands[0].x86AddressOperand(:ptr)}, #{operands[1].x86Operand(:ptr)}" else lowerDefault end end end