""" SSE2 instructions """

import struct
from ..isa import Isa
from ..encoding import Operand, Syntax, Instruction, Constructor
from .instructions import rm64_modes, rm32_modes, bits64  # , bits32
from .instructions import OpcodeToken
from .instructions import PrefixToken
from .instructions import RexToken, ModRmToken, SibToken
from .instructions import Imm32Token, Imm8Token
from .instructions import RmMem, RmMemDisp, RmReg32, RmReg64, RmAbs, MovAdr
from .instructions import Jb, Jbe, Ja, Jae, Je, Jne, Js, NearJump
from .instructions import SubImm, AddImm
from .registers import XmmRegisterSingle, XmmRegisterDouble
from .registers import Register64, Register32, rsp, eax, rax
from ..generic_instructions import ArtificialInstruction, RegisterUseDef


sse1_isa = Isa()
sse2_isa = Isa()


class SseInstruction(Instruction):
    tokens = [
        PrefixToken,
        RexToken,
        OpcodeToken,
        ModRmToken,
        SibToken,
        Imm8Token,
        Imm32Token,
    ]

    def set_user_patterns(self, tokens):
        # TODO: Improve this way of setting 'r':
        tokens.set_field("r", (self.r.num & 8) >> 3)
        tokens.set_field("reg", self.r.num & 0x7)

    def encode(self):
        """ Use a custom encode... """
        # TODO: find a better way for this!
        # 1. Set patterns:
        tokens = self.get_tokens()
        self.set_all_patterns(tokens)

        # 2. Encode:
        r = bytes()

        # prefix:
        if tokens[0].prefix != 0:
            r += tokens[0].encode()

        # Rex prefix:
        if tokens[1][0:4] > 0:
            r += tokens[1].encode()

        # 0f:
        r += bytes([0x0F])

        # opcode:
        r += tokens[2].encode()

        # rm byte:
        r += tokens[3].encode()

        # Encode sib byte:
        if tokens[3].mod != 3 and tokens[3].rm == 4:
            r += tokens[4].encode()

        # Encode displacement bytes:
        if tokens[3].mod == 1:
            r += tokens[5].encode()
        if tokens[3].mod == 2:
            r += tokens[6].encode()

        # Rip relative addressing mode with disp32
        if tokens[3].mod == 0 and tokens[3].rm == 5:
            r += tokens[6].encode()

        # sib byte and ...
        if tokens[3].mod == 0 and tokens[3].rm == 4:
            if tokens[4].base == 5:
                r += tokens[6].encode()
        return r


class Sse1Instruction(SseInstruction):
    """ Sse1 instruction """

    isa = sse1_isa


class Sse2Instruction(SseInstruction):
    """ Sse2 instruction """

    isa = sse2_isa


class RmXmmReg(Constructor):
    """ Xmm register access """

    reg_rm = Operand("reg_rm", XmmRegisterDouble, read=True)
    syntax = Syntax([reg_rm])
    patterns = {"mod": 3}

    def set_user_patterns(self, tokens):
        # TODO: Improve this way of setting 'r':
        tokens.set_field("b", (self.reg_rm.num & 8) >> 3)
        tokens.set_field("rm", self.reg_rm.num & 0x7)


RmXmmRegDouble = RmXmmReg


class RmXmmRegSingle(Constructor):
    """ Xmm register access """

    reg_rm = Operand("reg_rm", XmmRegisterSingle, read=True)
    syntax = Syntax([reg_rm])
    patterns = {"mod": 3}

    def set_user_patterns(self, tokens):
        # TODO: Improve this way of setting 'r':
        tokens.set_field("b", (self.reg_rm.num & 8) >> 3)
        tokens.set_field("rm", self.reg_rm.num & 0x7)


xmm_rm_modes = (RmXmmReg, RmMem, RmMemDisp, RmAbs)
xmm_double_rm_modes = (RmXmmRegDouble, RmMem, RmMemDisp, RmAbs)
xmm_single_rm_modes = (RmXmmRegSingle, RmMem, RmMemDisp, RmAbs)


# TODO: packed single values
# class Movups(Sse1Instruction):
#     """ Move unaligned packed single-fp values """

#     r = Operand("r", XmmRegister, write=True)
#     rm = Operand("rm", xmm_rm_modes)
#     syntax = Syntax(["movups", " ", r, ",", " ", rm])
#     patterns = {"opcode": 0x10}


class Movss(Sse1Instruction):
    """ Move scalar single-fp value """

    r = Operand("r", XmmRegisterSingle, write=True)
    rm = Operand("rm", xmm_single_rm_modes)
    syntax = Syntax(["movss", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF3, "opcode": 0x10}


# TODO: packed double values
# class Movupd(Sse2Instruction):
#     """ Move unaligned packed double-fp values """

#     r = Operand("r", XmmRegister, write=True)
#     rm = Operand("rm", xmm_rm_modes)
#     syntax = Syntax(["movupd", " ", r, ",", " ", rm])
#     patterns = {"prefix": 0x66, "opcode": 0x10}


class Movsd(Sse2Instruction):
    """ Move scalar f64 r/m into double-fp register """

    r = Operand("r", XmmRegisterDouble, write=True)
    rm = Operand("rm", xmm_double_rm_modes)
    syntax = Syntax(["movsd", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF2, "opcode": 0x10}


class Movss2(Sse1Instruction):
    """ Move scalar single-fp value """

    rm = Operand("rm", xmm_single_rm_modes)
    r = Operand("r", XmmRegisterSingle, read=True)
    syntax = Syntax(["movss", " ", rm, ",", " ", r], priority=1)
    patterns = {"prefix": 0xF3, "opcode": 0x11}


class Movsd2(Sse2Instruction):
    """ Move scalar double-fp value """

    rm = Operand("rm", xmm_double_rm_modes)
    r = Operand("r", XmmRegisterDouble, read=True)
    syntax = Syntax(["movsd", " ", rm, ",", " ", r], priority=1)
    patterns = {"prefix": 0xF2, "opcode": 0x11}


class Addss(Sse1Instruction):
    """ Add scalar single-fp value """

    r = Operand("r", XmmRegisterSingle, write=True, read=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    patterns = {"prefix": 0xF3, "opcode": 0x58}
    syntax = Syntax(["addss", " ", r, ",", " ", rm])


class Addsd(Sse2Instruction):
    """ Add scalar double-fp value """

    r = Operand("r", XmmRegisterDouble, write=True, read=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    patterns = {"prefix": 0xF2, "opcode": 0x58}
    syntax = Syntax(["addsd", " ", r, ",", " ", rm])


class Subss(Sse1Instruction):
    """ Sub scalar single-fp value """

    r = Operand("r", XmmRegisterSingle, write=True, read=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    patterns = {"prefix": 0xF3, "opcode": 0x5C}
    syntax = Syntax(["subss", " ", r, ",", " ", rm])


class Subsd(Sse2Instruction):
    """ Sub scalar double-fp value """

    r = Operand("r", XmmRegisterDouble, write=True, read=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    patterns = {"prefix": 0xF2, "opcode": 0x5C}
    syntax = Syntax(["subsd", " ", r, ",", " ", rm])


class Mulss(Sse1Instruction):
    """ Multiply scalar single-fp value """

    r = Operand("r", XmmRegisterSingle, write=True, read=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    patterns = {"prefix": 0xF3, "opcode": 0x59}
    syntax = Syntax(["mulss", " ", r, ",", " ", rm])


class Mulsd(Sse2Instruction):
    """ Multiply scalar double-fp value """

    r = Operand("r", XmmRegisterDouble, write=True, read=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    patterns = {"prefix": 0xF2, "opcode": 0x59}
    syntax = Syntax(["mulsd", " ", r, ",", " ", rm])


class Divss(Sse1Instruction):
    """ Divide scalar single-fp value """

    r = Operand("r", XmmRegisterSingle, write=True, read=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    patterns = {"prefix": 0xF3, "opcode": 0x5E}
    syntax = Syntax(["divss", " ", r, ",", " ", rm])


class Divsd(Sse2Instruction):
    """ Divide scalar double-fp value """

    r = Operand("r", XmmRegisterDouble, write=True, read=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    patterns = {"prefix": 0xF2, "opcode": 0x5E}
    syntax = Syntax(["divsd", " ", r, ",", " ", rm])


class Cvtss2si(Sse1Instruction):
    """ Convert scalar single-fp to integer """

    r = Operand("r", Register64, write=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    syntax = Syntax(["cvtss2si", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF3, "opcode": 0x2D, "w": 1}


class Cvtss2si_32(Sse1Instruction):
    """ Convert scalar single-fp to integer """

    r = Operand("r", Register32, write=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    syntax = Syntax(["cvtss2si", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF3, "opcode": 0x2D, "w": 0}


class Cvtsd2si(Sse2Instruction):
    """ Convert scalar double-fp to integer """

    r = Operand("r", Register64, write=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    syntax = Syntax(["cvtsd2si", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF2, "opcode": 0x2D, "w": 1}


class Cvtsd2si_32(Sse2Instruction):
    """ Convert scalar double-fp to integer """

    r = Operand("r", Register32, write=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    syntax = Syntax(["cvtsd2si", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF2, "opcode": 0x2D, "w": 0}


class Cvtsi2ss(Sse1Instruction):
    """ Convert integer to scalar single-fp """

    r = Operand("r", XmmRegisterSingle, write=True)
    rm = Operand("rm", rm64_modes, read=True)
    syntax = Syntax(["cvtsi2ss", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF3, "opcode": 0x2A, "w": 1}


class Cvtsi2ss_32(Sse1Instruction):
    """ Convert integer to scalar single-fp """

    r = Operand("r", XmmRegisterSingle, write=True)
    rm = Operand("rm", rm32_modes, read=True)
    syntax = Syntax(["cvtsi2ss", " ", r, ",", " ", rm], priority=2)
    patterns = {"prefix": 0xF3, "opcode": 0x2A, "w": 0}


class Cvtsi2sd(Sse2Instruction):
    """ Convert integer to scalar double-fp """

    r = Operand("r", XmmRegisterDouble, write=True)
    rm = Operand("rm", rm64_modes, read=True)
    syntax = Syntax(["cvtsi2sd", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF2, "opcode": 0x2A, "w": 1}


class Cvtsi2sd_32(Sse2Instruction):
    """ Convert integer to scalar double-fp """

    r = Operand("r", XmmRegisterDouble, write=True)
    rm = Operand("rm", rm32_modes, read=True)
    syntax = Syntax(["cvtsi2sd", " ", r, ",", " ", rm], priority=2)
    patterns = {"prefix": 0xF2, "opcode": 0x2A, "w": 0}


class Cvtss2sd(Sse2Instruction):
    """ Convert scalar single-fp to scalar double-fp """

    r = Operand("r", XmmRegisterDouble, write=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    syntax = Syntax(["cvtss2sd", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF3, "opcode": 0x5A}


class Cvtsd2ss(Sse2Instruction):
    """ Convert scalar double-fp to scalar single-fp """

    r = Operand("r", XmmRegisterSingle, write=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    syntax = Syntax(["cvtsd2ss", " ", r, ",", " ", rm])
    patterns = {"prefix": 0xF2, "opcode": 0x5A}


class Comiss(Sse1Instruction):
    """ Scalar compare single-fp values """

    r = Operand("r", XmmRegisterSingle, read=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    syntax = Syntax(["comiss", " ", r, ",", " ", rm])
    patterns = {"opcode": 0x2F}


class Ucomiss(Sse1Instruction):
    """ Unordered scalar compare single-fp values """

    r = Operand("r", XmmRegisterSingle, read=True)
    rm = Operand("rm", xmm_single_rm_modes, read=True)
    syntax = Syntax(["ucomiss", " ", r, ",", " ", rm])
    patterns = {"opcode": 0x2E}


class Ucomisd(Sse2Instruction):
    """ Unordered scalar compare double-fp values """

    r = Operand("r", XmmRegisterDouble, read=True)
    rm = Operand("rm", xmm_double_rm_modes, read=True)
    syntax = Syntax(["ucomisd", " ", r, ",", " ", rm])
    patterns = {"prefix": 0x66, "opcode": 0x2E}


class SsePseudoInstruction(ArtificialInstruction):
    isa = sse1_isa


class PushXmmRegisterDouble(SsePseudoInstruction):
    r = Operand("r", XmmRegisterDouble, read=True)
    syntax = Syntax(["push", " ", r])

    def render(self):
        # sub rsp, 8
        yield SubImm(rsp, 8)

        # movdqu [rsp], r
        yield Movsd2(RmMem(rsp), self.r)


class PopXmmRegisterDouble(SsePseudoInstruction):
    r = Operand("r", XmmRegisterDouble, write=True)
    syntax = Syntax(["pop", " ", r])

    def render(self):
        # movdqu [rsp], r
        yield Movsd(self.r, RmMem(rsp))

        # add rsp, 8
        yield AddImm(rsp, 8)


class PushXmmRegisterSingle(SsePseudoInstruction):
    r = Operand("r", XmmRegisterSingle, read=True)
    syntax = Syntax(["push", " ", r])

    def render(self):
        # sub rsp, 4
        yield SubImm(rsp, 4)

        # movdqu [rsp], r
        yield Movss2(RmMem(rsp), self.r)


class PopXmmRegisterSingle(SsePseudoInstruction):
    r = Operand("r", XmmRegisterSingle, write=True)
    syntax = Syntax(["pop", " ", r])

    def render(self):
        # movdqu [rsp], r
        yield Movss(self.r, RmMem(rsp))

        # add rsp, 4
        yield AddImm(rsp, 4)


@sse2_isa.pattern("regfp32", "F64TOF32(rmf64)", size=6, cycles=3, energy=3)
def pattern_f64tof32(context, tree, c0):
    dst = context.new_reg(XmmRegisterSingle)
    context.emit(Cvtsd2ss(dst, c0))
    return dst


@sse2_isa.pattern("regfp64", "F32TOF64(rmf32)", size=6, cycles=3, energy=3)
def pattern_f32tof64(context, tree, c0):
    dst = context.new_reg(XmmRegisterDouble)
    context.emit(Cvtss2sd(dst, c0))
    return dst


# I64:
@sse1_isa.pattern("reg64", "F32TOI64(rmf32)", size=6, cycles=2, energy=2)
@sse1_isa.pattern("reg64", "F32TOU64(rmf32)", size=6, cycles=2, energy=2)
def pattern_f32toi64(context, tree, c0):
    dst = context.new_reg(Register64)
    context.emit(Cvtss2si(dst, c0))
    return dst


@sse2_isa.pattern("reg64", "F64TOI64(rmf64)", size=6, cycles=3, energy=3)
@sse2_isa.pattern("reg64", "F64TOU64(rmf64)", size=6, cycles=3, energy=3)
def pattern_f64toi64(context, tree, c0):
    dst = context.new_reg(Register64)
    context.emit(Cvtsd2si(dst, c0))
    return dst


# See also: https://stackoverflow.com/questions/11406654/
# how-to-convert-an-unsigned-integer-to-floating-point-in-x86-32-bit-assembly
@sse1_isa.pattern("regfp32", "I64TOF32(reg64)", size=6, cycles=2, energy=2)
def pattern_i64tof32(context, tree, c0):
    dst = context.new_reg(XmmRegisterSingle)
    context.emit(Cvtsi2ss(dst, RmReg64(c0)))
    return dst


@sse1_isa.pattern("regfp32", "U64TOF32(reg64)", size=6, cycles=2, energy=2)
def pattern_u64tof32(context, tree, c0):
    dst = context.new_reg(XmmRegisterSingle)
    # Special case if value is bigger than 2**63.
    # Strategy:
    # Divide in half, convert to float and then double the value
    label_special = context.new_label()
    label_end = context.new_label()
    context.emit(bits64.TestRmReg(RmReg64(c0), c0))  # Check sign bit
    context.emit(Js(label_special.name))  # If larger than 2**63

    # Happy case:
    context.emit(Cvtsi2ss(dst, RmReg64(c0)))
    context.emit(NearJump(label_end.name))

    # Special case:
    context.emit(label_special)
    tmp = context.new_reg(Register64)
    context.move(tmp, c0)
    context.emit(bits64.ShrRm(RmReg64(tmp)))  # Divide in half
    context.emit(Cvtsi2ss(dst, RmReg64(tmp)))  # Convert
    context.emit(Addss(dst, RmXmmRegSingle(dst)))  # Double
    context.emit(label_end)
    return dst


@sse2_isa.pattern("regfp64", "I64TOF64(reg64)", size=6, cycles=3, energy=3)
def pattern_i64tof64(context, tree, c0):
    dst = context.new_reg(XmmRegisterDouble)
    context.emit(Cvtsi2sd(dst, RmReg64(c0)))
    return dst


@sse2_isa.pattern("regfp64", "U64TOF64(reg64)", size=6, cycles=3, energy=3)
def pattern_u64tof64(context, tree, c0):
    dst = context.new_reg(XmmRegisterDouble)
    # Special case if value is bigger than 2**63.
    # Strategy:
    # Divide in half, convert to float and then double the value
    label_special = context.new_label()
    label_end = context.new_label()
    context.emit(bits64.TestRmReg(RmReg64(c0), c0))  # Check sign bit
    context.emit(Js(label_special.name))  # If larger than 2**63

    # Happy case:
    context.emit(Cvtsi2sd(dst, RmReg64(c0)))
    context.emit(NearJump(label_end.name))

    # Special case:
    context.emit(label_special)
    tmp = context.new_reg(Register64)
    context.move(tmp, c0)
    context.emit(bits64.ShrRm(RmReg64(tmp)))  # Divide in half
    context.emit(Cvtsi2sd(dst, RmReg64(tmp)))  # Convert
    context.emit(Addsd(dst, RmXmmRegDouble(dst)))  # Double
    context.emit(label_end)
    return dst


# I32:
@sse1_isa.pattern("reg32", "F32TOI32(rmf32)", size=6, cycles=2, energy=2)
@sse1_isa.pattern("reg32", "F32TOU32(rmf32)", size=6, cycles=2, energy=2)
def pattern_f32toi32(context, tree, c0):
    dst = context.new_reg(Register32)
    context.emit(Cvtss2si_32(dst, c0))
    return dst


@sse2_isa.pattern("reg32", "F64TOI32(rmf64)", size=6, cycles=3, energy=3)
@sse2_isa.pattern("reg32", "F64TOU32(rmf64)", size=6, cycles=3, energy=3)
def pattern_f64toi32(context, tree, c0):
    dst = context.new_reg(Register32)
    context.emit(Cvtsd2si_32(dst, c0))
    return dst


@sse1_isa.pattern("regfp32", "I32TOF32(reg32)", size=6, cycles=2, energy=2)
def pattern_i32tof32(context, tree, c0):
    dst = context.new_reg(XmmRegisterSingle)
    context.emit(Cvtsi2ss_32(dst, RmReg32(c0)))
    return dst


@sse1_isa.pattern("regfp32", "U32TOF32(reg32)", size=6, cycles=2, energy=2)
def pattern_u32tof32(context, tree, c0):
    dst = context.new_reg(XmmRegisterSingle)

    # Special case for unsigned 32 bits integers
    context.emit(bits64.XorRmReg(RmReg64(rax), rax))
    context.move(eax, c0)

    defu2 = RegisterUseDef()
    defu2.add_use(eax)
    defu2.add_def(rax)
    context.emit(defu2)

    context.emit(Cvtsi2ss(dst, RmReg64(rax)))
    return dst


@sse2_isa.pattern("regfp64", "I32TOF64(reg32)", size=6, cycles=3, energy=3)
def pattern_i32tof64(context, tree, c0):
    dst = context.new_reg(XmmRegisterDouble)
    context.emit(Cvtsi2sd_32(dst, RmReg32(c0)))
    return dst


@sse2_isa.pattern("regfp64", "U32TOF64(reg32)", size=6, cycles=3, energy=3)
def pattern_u32tof64(context, tree, c0):
    dst = context.new_reg(XmmRegisterDouble)

    # Special case for unsigned 32 bits integers
    context.emit(bits64.XorRmReg(RmReg64(rax), rax))
    context.move(eax, c0)

    defu2 = RegisterUseDef()
    defu2.add_use(eax)
    defu2.add_def(rax)
    context.emit(defu2)

    context.emit(Cvtsi2sd(dst, RmReg64(rax)))
    return dst


@sse1_isa.pattern("regfp32", "CONSTF32", size=8, cycles=6, energy=2)
def pattern_const_f32(context, tree):
    float_const = struct.pack("f", tree.value)
    const_label = context.frame.add_constant(float_const)
    label_addr = context.new_reg(Register64)
    context.emit(MovAdr(label_addr, const_label))
    dst = context.new_reg(XmmRegisterSingle)
    context.emit(Movss(dst, RmMem(label_addr)))
    return dst


@sse2_isa.pattern("regfp64", "CONSTF64", size=8, cycles=9, energy=4)
def pattern_const_f64(context, tree):
    float_const = struct.pack("d", tree.value)
    const_label = context.frame.add_constant(float_const)
    label_addr = context.new_reg(Register64)
    context.emit(MovAdr(label_addr, const_label))
    dst = context.new_reg(XmmRegisterDouble)
    context.emit(Movsd(dst, RmMem(label_addr)))
    return dst


@sse2_isa.pattern("regfp64", "NEGF64(rmf64)", size=8, cycles=9, energy=4)
def pattern_neg_f64(context, tree, c0):
    """ Multiply by -1 """
    float_const = struct.pack("d", -1)
    const_label = context.frame.add_constant(float_const)
    label_addr = context.new_reg(Register64)
    context.emit(MovAdr(label_addr, const_label))
    dst = context.new_reg(XmmRegisterDouble)
    context.emit(Movsd(dst, c0))
    context.emit(Mulsd(dst, RmMem(label_addr)))
    return dst


@sse1_isa.pattern("regfp32", "NEGF32(rmf32)", size=8, cycles=9, energy=4)
def pattern_neg_f32(context, tree, c0):
    """ Multiply by -1 """
    float_const = struct.pack("f", -1)
    const_label = context.frame.add_constant(float_const)
    label_addr = context.new_reg(Register64)
    context.emit(MovAdr(label_addr, const_label))
    dst = context.new_reg(XmmRegisterSingle)
    context.emit(Movss(dst, c0))
    context.emit(Mulss(dst, RmMem(label_addr)))
    return dst


@sse1_isa.pattern(
    "regfp32", "ADDF32(regfp32, rmf32)", size=6, cycles=2, energy=2
)
def pattern_addf32(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterSingle)
    context.move(dst, c0)
    context.emit(Addss(dst, c1))
    return dst


@sse2_isa.pattern(
    "regfp64", "ADDF64(regfp64, rmf64)", size=6, cycles=4, energy=3
)
def pattern_addf64(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterDouble)
    context.move(dst, c0)
    context.emit(Addsd(dst, c1))
    return dst


@sse1_isa.pattern(
    "regfp32", "SUBF32(regfp32, rmf32)", size=6, cycles=2, energy=2
)
def pattern_sub_f32(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterSingle)
    context.move(dst, c0)
    context.emit(Subss(dst, c1))
    return dst


@sse2_isa.pattern(
    "regfp64", "SUBF64(regfp64, rmf64)", size=6, cycles=3, energy=3
)
def pattern_sub_f64(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterDouble)
    context.move(dst, c0)
    context.emit(Subsd(dst, c1))
    return dst


@sse1_isa.pattern(
    "regfp32", "MULF32(regfp32, rmf32)", size=6, cycles=2, energy=2
)
def pattern_mul_f32(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterSingle)
    context.move(dst, c0)
    context.emit(Mulss(dst, c1))
    return dst


@sse2_isa.pattern(
    "regfp64", "MULF64(regfp64, rmf64)", size=6, cycles=3, energy=3
)
def pattern_mul_f64(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterDouble)
    context.move(dst, c0)
    context.emit(Mulsd(dst, c1))
    return dst


@sse1_isa.pattern(
    "regfp32", "DIVF32(regfp32, rmf32)", size=6, cycles=2, energy=2
)
def pattern_div_f32(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterSingle)
    context.move(dst, c0)
    context.emit(Divss(dst, c1))
    return dst


@sse2_isa.pattern(
    "regfp64", "DIVF64(regfp64, rmf64)", size=6, cycles=4, energy=3
)
def pattern_div_f64(context, tree, c0, c1):
    dst = context.new_reg(XmmRegisterDouble)
    context.move(dst, c0)
    context.emit(Divsd(dst, c1))
    return dst


@sse1_isa.pattern("stm", "MOVF32(regfp32)", size=3, cycles=2, energy=2)
def pattern_mov_f32(context, tree, c0):
    """ Generate a f32 register - f32 register move. """
    context.move(tree.value, c0)


@sse1_isa.pattern("stm", "MOVF32(rmf32)", size=3, cycles=2, energy=2)
def pattern_mov_rmf32(context, tree, c0):
    """ Move any r/m source into a f32 register. """
    context.emit(Movss(tree.value, c0))


@sse2_isa.pattern("stm", "MOVF64(regfp64)", size=3, cycles=3, energy=3)
def pattern_mov_f64(context, tree, c0):
    """ Generate a f64 register - f64 register move """
    context.move(tree.value, c0)


@sse2_isa.pattern("stm", "MOVF64(rmf64)", size=3, cycles=3, energy=3)
def pattern_mov_rmf64(context, tree, c0):
    """ Move any r/m f64 source into a f64 register """
    context.emit(Movsd(tree.value, c0))


@sse2_isa.pattern("regfp64", "REGF64", size=0, cycles=0, energy=0)
@sse1_isa.pattern("regfp32", "REGF32", size=0, cycles=0, energy=0)
def pattern_reg_fp(context, tree):
    return tree.value


@sse1_isa.pattern("stm", "STRF32(mem64, regfp32)", size=6, cycles=2, energy=2)
def pattern_str_f32(context, tree, c0, c1):
    context.emit(Movss2(c0, c1))


@sse2_isa.pattern("stm", "STRF64(mem64, regfp64)", size=6, cycles=3, energy=3)
def pattern_str_f64(context, tree, c0, c1):
    context.emit(Movsd2(c0, c1))


@sse1_isa.pattern("regfp32", "rmf32", size=6, cycles=2, energy=2)
def pattern_ldr_f32(context, tree, c0):
    dst = context.new_reg(XmmRegisterSingle)
    context.emit(Movss(dst, c0))
    return dst


@sse1_isa.pattern("rmf32", "LDRF32(mem64)", size=1, cycles=2, energy=2)
def pattern_rmf32_ldr_f32(context, tree, c0):
    return c0


@sse2_isa.pattern("rmf32", "regfp32", size=1, cycles=1, energy=1)
def pattern_rmf32_f32(context, tree, c0):
    return RmXmmRegSingle(c0)


@sse2_isa.pattern("regfp64", "rmf64", size=6, cycles=3, energy=3)
def pattern_ldr_f64(context, tree, c0):
    dst = context.new_reg(XmmRegisterDouble)
    context.emit(Movsd(dst, c0))
    return dst


@sse2_isa.pattern("rmf64", "LDRF64(mem64)", size=1, cycles=2, energy=2)
def pattern_rmf64_ldr_f64(context, tree, c0):
    return c0


@sse2_isa.pattern("rmf64", "regfp64", size=1, cycles=1, energy=1)
def pattern_rmf64_f64(context, tree, c0):
    return RmXmmRegDouble(c0)


jump_opnames = {"<": Jb, ">": Ja, "==": Je, "!=": Jne, ">=": Jae, "<=": Jbe}


def pattern_cjmp(context, value):
    op, yes_label, no_label = value
    Bop = jump_opnames[op]
    jmp_ins = NearJump(no_label.name, jumps=[no_label])
    context.emit(Bop(yes_label.name, jumps=[yes_label, jmp_ins]))
    context.emit(jmp_ins)


@sse1_isa.pattern(
    "stm", "CJMPF32(regfp32,regfp32)", size=6, cycles=3, energy=3
)
def pattern_cjmp_f32(context, tree, c0, c1):
    context.emit(Ucomiss(c0, RmXmmRegSingle(c1)))
    pattern_cjmp(context, tree.value)


@sse1_isa.pattern(
    "stm", "CJMPF64(regfp64,regfp64)", size=6, cycles=3, energy=3
)
def pattern_cjmp_f64(context, tree, c0, c1):
    context.emit(Ucomisd(c0, RmXmmRegDouble(c1)))
    pattern_cjmp(context, tree.value)
