# 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
        offsetRegister(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

