Repository: tetratelabs/wazero
Pull request #1908 (2024-01-10 17:29:11)

wazevo(amd64): addressing mode encodings 


Patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr.go b/internal/engine/wazevo/backend/isa/amd64/instr.go
index 12a7c675..6225c401 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr.go
@@ -84,8 +84,8 @@ func (i *instruction) String() string {
 		panic("TODO")
 	case mov64MR:
 		panic("TODO")
-	case loadEffectiveAddress:
-		panic("TODO")
+	case lea:
+		return fmt.Sprintf("lea %s, %s", i.op1.format(true), i.op2.format(true))
 	case movsxRmR:
 		panic("TODO")
 	case movRM:
@@ -245,7 +245,7 @@ const (
 	mov64MR
 
 	// Loads the memory address of addr into dst.
-	loadEffectiveAddress
+	lea
 
 	// Sign-extended loads and moves: movs (bl bq wl wq lq) addr reg.
 	movsxRmR
@@ -397,8 +397,8 @@ func (k instructionKind) String() string {
 		return "movzxRmR"
 	case mov64MR:
 		return "mov64MR"
-	case loadEffectiveAddress:
-		return "loadEffectiveAddress"
+	case lea:
+		return "lea"
 	case movsxRmR:
 		return "movsxRmR"
 	case movRM:
@@ -486,6 +486,13 @@ func (a aluRmiROpcode) String() string {
 	}
 }
 
+func (i *instruction) asLEA(a amode, rd regalloc.VReg) *instruction {
+	i.kind = lea
+	i.op1 = newOperandMem(a)
+	i.op2 = newOperandReg(rd)
+	return i
+}
+
 func (i *instruction) asRet(abi *backend.FunctionABI) *instruction {
 	i.kind = ret
 	i.abi = abi
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
index 4137f835..2054bad3 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
@@ -56,17 +56,20 @@ func (i *instruction) encode(c backend.Compiler) {
 		} else {
 			rex = rex.clearW()
 		}
-		aluOp := aluRmiROpcode(i.u1)
 
+		dst := regEncodings[i.op2.r.RealReg()]
+
+		aluOp := aluRmiROpcode(i.u1)
 		if aluOp == aluRmiROpcodeMul {
 			op1 := i.op1
+			const regMemOpc, regMemOpcNum = 0x0FAF, 2
 			switch op1.kind {
 			case operandKindReg:
 				src := regEncodings[op1.r.RealReg()]
-				dst := regEncodings[i.op2.r.RealReg()]
-				encodeRegReg(c, legacyPrefixesNone, 0x0faf, 2, dst, src, rex)
+				encodeRegReg(c, legacyPrefixesNone, regMemOpc, regMemOpcNum, dst, src, rex)
 			case operandKindMem:
-				panic("TODO")
+				m := i.op1.amode
+				encodeRegMem(c, legacyPrefixesNone, regMemOpc, regMemOpcNum, dst, m, rex)
 			case operandImm32:
 				imm8 := lower8willSignExtendTo32(op1.imm32)
 				var opc uint32
@@ -75,7 +78,6 @@ func (i *instruction) encode(c backend.Compiler) {
 				} else {
 					opc = 0x69
 				}
-				dst := regEncodings[i.op2.r.RealReg()]
 				encodeRegReg(c, legacyPrefixesNone, opc, 1, dst, dst, rex)
 				if imm8 {
 					c.EmitByte(byte(op1.imm32))
@@ -84,6 +86,7 @@ func (i *instruction) encode(c backend.Compiler) {
 				}
 			}
 		} else {
+			const opcodeNum = 1
 			var opcR, opcM, subOpcImm uint32
 			switch aluOp {
 			case aluRmiROpcodeAdd:
@@ -104,11 +107,10 @@ func (i *instruction) encode(c backend.Compiler) {
 			switch op1.kind {
 			case operandKindReg:
 				src := regEncodings[op1.r.RealReg()]
-				dst := regEncodings[i.op2.r.RealReg()]
-				encodeRegReg(c, legacyPrefixesNone, opcR, 1, src, dst, rex)
+				encodeRegReg(c, legacyPrefixesNone, opcR, opcodeNum, src, dst, rex)
 			case operandKindMem:
-				fmt.Println(opcM)
-				panic("TODO")
+				m := i.op1.amode
+				encodeRegMem(c, legacyPrefixesNone, opcM, opcodeNum, dst, m, rex)
 			case operandImm32:
 				imm8 := lower8willSignExtendTo32(op1.imm32)
 				var opc uint32
@@ -117,8 +119,7 @@ func (i *instruction) encode(c backend.Compiler) {
 				} else {
 					opc = 0x81
 				}
-				dst := regEncodings[i.op2.r.RealReg()]
-				encodeRegReg(c, legacyPrefixesNone, opc, 1, regEnc(subOpcImm), dst, rex)
+				encodeRegReg(c, legacyPrefixesNone, opc, opcodeNum, regEnc(subOpcImm), dst, rex)
 				if imm8 {
 					c.EmitByte(byte(op1.imm32))
 				} else {
@@ -326,13 +327,16 @@ func (i *instruction) encode(c backend.Compiler) {
 			panic(fmt.Sprintf("Unsupported sseOpcode: %s", op))
 		}
 
+		dst := regEncodings[i.op2.r.RealReg()]
+
+		rex := rexInfo(0).clearW()
 		op1 := i.op1
 		if op1.kind == operandKindReg {
 			src := regEncodings[op1.r.RealReg()]
-			dst := regEncodings[i.op2.r.RealReg()]
-			encodeRegReg(c, legPrex, opcode, opcodeNum, dst, src, rexInfo(0).clearW())
+			encodeRegReg(c, legPrex, opcode, opcodeNum, dst, src, rex)
 		} else if i.op1.kind == operandKindMem {
-			panic("TODO")
+			m := i.op1.amode
+			encodeRegMem(c, legPrex, opcode, opcodeNum, dst, m, rex)
 		} else {
 			panic("BUG: invalid operand kind")
 		}
@@ -358,14 +362,15 @@ func (i *instruction) encode(c backend.Compiler) {
 		} else {
 			rex = rex.clearW()
 		}
+		dst := regEncodings[i.op2.r.RealReg()]
 
 		op1 := i.op1
 		if op1.kind == operandKindReg {
 			src := regEncodings[op1.r.RealReg()]
-			dst := regEncodings[i.op2.r.RealReg()]
 			encodeRegReg(c, legPrefix, opcode, opcodeNum, dst, src, rex)
 		} else if i.op1.kind == operandKindMem {
-			panic("TODO")
+			m := i.op1.amode
+			encodeRegMem(c, legPrefix, opcode, opcodeNum, dst, m, rex)
 		} else {
 			panic("BUG: invalid operand kind")
 		}
@@ -438,13 +443,16 @@ func (i *instruction) encode(c backend.Compiler) {
 			panic(fmt.Sprintf("Unsupported sseOpcode: %s", op))
 		}
 
+		dst := regEncodings[i.op2.r.RealReg()]
+
+		rex := rexInfo(0).clearW()
 		op1 := i.op1
 		if op1.kind == operandKindReg {
 			src := regEncodings[op1.r.RealReg()]
-			dst := regEncodings[i.op2.r.RealReg()]
-			encodeRegReg(c, prefix, opcode, opcodeNum, dst, src, rexInfo(0).clearW())
+			encodeRegReg(c, prefix, opcode, opcodeNum, dst, src, rex)
 		} else if i.op1.kind == operandKindMem {
-			panic("TODO")
+			m := i.op1.amode
+			encodeRegMem(c, prefix, opcode, opcodeNum, dst, m, rex)
 		} else {
 			panic("BUG: invalid operand kind")
 		}
@@ -467,8 +475,11 @@ func (i *instruction) encode(c backend.Compiler) {
 		panic("TODO")
 	case mov64MR:
 		panic("TODO")
-	case loadEffectiveAddress:
-		panic("TODO")
+	case lea:
+		a := i.op1.amode
+		dst := regEncodings[i.op2.r.RealReg()]
+		encodeRegMem(c, legacyPrefixesNone, 0x8d, 1, dst, a, rexInfo(0).setW())
+
 	case movsxRmR:
 		panic("TODO")
 	case movRM:
@@ -494,7 +505,10 @@ func (i *instruction) encode(c backend.Compiler) {
 			}
 			c.EmitByte(0x50 | dst.encoding())
 		case operandKindMem:
-			panic("TODO")
+			m := op.amode
+			encodeRegMem(
+				c, legacyPrefixesNone, 0xff, 1, regEnc(6), m, rexInfo(0).clearW(),
+			)
 		case operandImm32:
 			c.EmitByte(0x68)
 			c.Emit4Bytes(op.imm32)
@@ -568,6 +582,110 @@ func encodeModRM(mod byte, reg byte, rm byte) byte {
 	return mod<<6 | reg<<3 | rm
 }
 
+func encodeSIB(shift byte, encIndex byte, encBase byte) byte {
+	return shift<<6 | encIndex<<3 | encBase
+}
+
+func encodeRegMem(
+	c backend.Compiler, legPrefixes legacyPrefixes, opcodes uint32, opcodeNum uint32, r regEnc, m amode, rex rexInfo,
+) {
+	legPrefixes.encode(c)
+
+	const (
+		modNoDisplacement    = 0b00
+		modShortDisplacement = 0b01
+		modLongDisplacement  = 0b10
+
+		useSBI = 4 // the encoding of rsp or r12 register.
+	)
+
+	switch m.kind {
+	case amodeImmReg:
+		base := m.base.RealReg()
+		baseEnc := regEncodings[base]
+
+		rex.encode(c, r, baseEnc)
+
+		for opcodeNum > 0 {
+			opcodeNum--
+			c.EmitByte(byte((opcodes >> (opcodeNum << 3)) & 0xff))
+		}
+
+		// SIB byte is the last byte of the memory encoding before the displacement
+		const sibByte = 0x24 // == encodeSIB(0, 4, 4)
+
+		immZero, baseRbp, baseR13 := m.imm32 == 0, base == rbp, base == r13
+		short := lower8willSignExtendTo32(m.imm32)
+		rspOrR12 := base == rsp || base == r12
+
+		if immZero && !baseRbp && !baseR13 { // rbp or r13 can't be used as base for without displacement encoding.
+			c.EmitByte(encodeModRM(modNoDisplacement, r.encoding(), baseEnc.encoding()))
+			if rspOrR12 {
+				c.EmitByte(sibByte)
+			}
+		} else if short { // Note: this includes the case where m.imm32 == 0 && base == rbp || base == r13.
+			c.EmitByte(encodeModRM(modShortDisplacement, r.encoding(), baseEnc.encoding()))
+			if rspOrR12 {
+				c.EmitByte(sibByte)
+			}
+			c.EmitByte(byte(m.imm32))
+		} else {
+			c.EmitByte(encodeModRM(modLongDisplacement, r.encoding(), baseEnc.encoding()))
+			if rspOrR12 {
+				c.EmitByte(sibByte)
+			}
+			c.Emit4Bytes(m.imm32)
+		}
+
+	case amodeRegRegShift:
+		base := m.base.RealReg()
+		baseEnc := regEncodings[base]
+		index := m.index.RealReg()
+		indexEnc := regEncodings[index]
+
+		if index == rsp {
+			panic("BUG: rsp can't be used as index of addressing mode")
+		}
+
+		rex.encodeForIndex(c, r, indexEnc, baseEnc)
+
+		for opcodeNum > 0 {
+			opcodeNum--
+			c.EmitByte(byte((opcodes >> (opcodeNum << 3)) & 0xff))
+		}
+
+		immZero, baseRbp, baseR13 := m.imm32 == 0, base == rbp, base == r13
+		if immZero && !baseRbp && !baseR13 { // rbp or r13 can't be used as base for without displacement encoding. (curious why? because it's interpreted as RIP relative addressing).
+			c.EmitByte(encodeModRM(modNoDisplacement, r.encoding(), useSBI))
+			c.EmitByte(encodeSIB(m.shift, indexEnc.encoding(), baseEnc.encoding()))
+		} else if lower8willSignExtendTo32(m.imm32) {
+			c.EmitByte(encodeModRM(modShortDisplacement, r.encoding(), useSBI))
+			c.EmitByte(encodeSIB(m.shift, indexEnc.encoding(), baseEnc.encoding()))
+			c.EmitByte(byte(m.imm32))
+		} else {
+			c.EmitByte(encodeModRM(modLongDisplacement, r.encoding(), useSBI))
+			c.EmitByte(encodeSIB(m.shift, indexEnc.encoding(), baseEnc.encoding()))
+			c.Emit4Bytes(m.imm32)
+		}
+
+	case amodeRipRelative:
+		if m.label != backend.LabelInvalid {
+			panic("BUG: label must be resolved for amodeRipRelative at this point")
+		}
+
+		rex.encode(c, r, 0)
+		for opcodeNum > 0 {
+			opcodeNum--
+			c.EmitByte(byte((opcodes >> (opcodeNum << 3)) & 0xff))
+		}
+
+		// Indicate "LEAQ [RIP + 32bit displacement].
+		// https://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing
+		c.EmitByte(encodeModRM(0b00, r.encoding(), 0b101))
+		c.Emit4Bytes(m.imm32)
+	}
+}
+
 const (
 	rexEncodingDefault byte = 0x40
 	rexEncodingW            = rexEncodingDefault | 0x08
@@ -583,7 +701,7 @@ func (ri rexInfo) setW() rexInfo {
 	return ri | 0x01
 }
 
-func (ri rexInfo) clearW() rexInfo { //nolint
+func (ri rexInfo) clearW() rexInfo {
 	return ri & 0x02
 }
 
@@ -608,7 +726,7 @@ func (ri rexInfo) encode(c backend.Compiler, encR regEnc, encRM regEnc) {
 	}
 }
 
-func (ri rexInfo) encodeForIndex(c backend.Compiler, encR regEnc, encIndex regEnc, encBase regEnc) { //nolint
+func (ri rexInfo) encodeForIndex(c backend.Compiler, encR regEnc, encIndex regEnc, encBase regEnc) {
 	var w byte = 0
 	if ri&0x01 != 0 {
 		w = 0x01
diff --git a/internal/engine/wazevo/backend/isa/amd64/operands.go b/internal/engine/wazevo/backend/isa/amd64/operands.go
index a3959362..af3c383f 100644
--- a/internal/engine/wazevo/backend/isa/amd64/operands.go
+++ b/internal/engine/wazevo/backend/isa/amd64/operands.go
@@ -3,6 +3,7 @@ package amd64
 import (
 	"fmt"
 
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 )
 
@@ -48,35 +49,90 @@ func newOperandImm32(imm32 uint32) operand {
 	return operand{kind: operandImm32, imm32: imm32}
 }
 
-// nolint
+func newOperandMem(amode amode) operand {
+	return operand{kind: operandKindMem, amode: amode}
+}
+
+// amode is a memory operand (addressing mode).
 type amode struct {
 	kind  amodeKind
 	imm32 uint32
-
-	// For amodeRegRegShit:
-
 	base  regalloc.VReg
+
+	// For amodeRegRegShift:
 	index regalloc.VReg
 	shift byte // 0, 1, 2, 3
+
+	// For amodeRipRelative.
+	// If kind == amodeRipRelative, and label is invalid,
+	// then imm32 should represent the resolved address.
+	label backend.Label
 }
 
 type amodeKind byte
 
 const (
-
-	// immediate sign-extended and a Register.
+	// amodeRegRegShift calcualtes sign-extend-32-to-64(Immediate) + base
 	amodeImmReg amodeKind = iota + 1
 
-	// sign-extend-32-to-64(Immediate) + Register1 + (Register2 << Shift)
-	amodeRegRegShit
+	// amodeRegRegShift calculates sign-extend-32-to-64(Immediate) + base + (Register2 << Shift)
+	amodeRegRegShift
+
+	// amodeRipRelative is a memory operand with RIP-relative addressing mode.
+	amodeRipRelative
+
+	// TODO: there are other addressing modes such as the one with base register is absent.
 )
 
+func newAmodeImmReg(imm32 uint32, base regalloc.VReg) amode {
+	return amode{kind: amodeImmReg, imm32: imm32, base: base}
+}
+
+func newAmodeRegRegShit(imm32 uint32, base, index regalloc.VReg, shift byte) amode {
+	if shift > 3 {
+		panic(fmt.Sprintf("BUG: invalid shift (must be 3>=): %d", shift))
+	}
+	return amode{kind: amodeRegRegShift, imm32: imm32, base: base, index: index, shift: shift}
+}
+
+func (a *amode) resolveRipRelative(imm32 uint32) {
+	if a.kind != amodeRipRelative {
+		panic("BUG: invalid amode kind")
+	}
+	a.imm32 = imm32
+	a.label = backend.LabelInvalid
+}
+
+func newAmodeRipRelative(label backend.Label) amode {
+	if label == backend.LabelInvalid {
+		panic("BUG: invalid label")
+	}
+	return amode{kind: amodeRipRelative, label: label}
+}
+
+// String implements fmt.Stringer.
 func (a *amode) String() string {
 	switch a.kind {
 	case amodeImmReg:
-		panic("TODO")
-	case amodeRegRegShit:
-		panic("TODO")
+		if a.imm32 == 0 {
+			return fmt.Sprintf("(%s)", formatVRegSized(a.base, true))
+		}
+		return fmt.Sprintf("%d(%s)", int32(a.imm32), formatVRegSized(a.base, true))
+	case amodeRegRegShift:
+		if a.imm32 == 0 {
+			return fmt.Sprintf(
+				"(%s,%s,%d)",
+				formatVRegSized(a.base, true), formatVRegSized(a.index, true), 1<<a.shift)
+		}
+		return fmt.Sprintf(
+			"%d(%s,%s,%d)",
+			int32(a.imm32), formatVRegSized(a.base, true), formatVRegSized(a.index, true), 1<<a.shift)
+	case amodeRipRelative:
+		if a.label != backend.LabelInvalid {
+			return fmt.Sprintf("%s(%%rip)", a.label)
+		} else {
+			return fmt.Sprintf("%d(%%rip)", int32(a.imm32))
+		}
 	}
 	panic("BUG: invalid amode kind")
 }


Test patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
index 741b7f59..fd07d0d4 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
@@ -111,6 +111,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "f3450f58dd",
 			wantFormat: "addss %xmm13, %xmm11",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmRmR(sseOpcodeAddss, newOperandMem(newAmodeImmReg(0, r13VReg)), xmm11VReg, true)
+			},
+			want:       "f3450f585d00",
+			wantFormat: "addss (%r13), %xmm11",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmRmR(sseOpcodeAddss, newOperandMem(newAmodeImmReg(123, r13VReg)), xmm11VReg, true)
+			},
+			want:       "f3450f585d7b",
+			wantFormat: "addss 123(%r13), %xmm11",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmRmR(sseOpcodeAddss, newOperandMem(newAmodeImmReg(1<<25, r13VReg)), xmm11VReg, true)
+			},
+			want:       "f3450f589d00000002",
+			wantFormat: "addss 33554432(%r13), %xmm11",
+		},
 		// addsd
 		{
 			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
@@ -122,6 +143,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "f2450f58dd",
 			wantFormat: "addsd %xmm13, %xmm11",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmRmR(sseOpcodeAddsd, newOperandMem(newAmodeImmReg(0, rbpVReg)), xmm11VReg, true)
+			},
+			want:       "f2440f585d00",
+			wantFormat: "addsd (%rbp), %xmm11",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmRmR(sseOpcodeAddsd, newOperandMem(newAmodeImmReg(123, rbpVReg)), xmm11VReg, true)
+			},
+			want:       "f2440f585d7b",
+			wantFormat: "addsd 123(%rbp), %xmm11",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmRmR(sseOpcodeAddsd, newOperandMem(newAmodeImmReg(1<<25, rbpVReg)), xmm11VReg, true)
+			},
+			want:       "f2440f589d00000002",
+			wantFormat: "addsd 33554432(%rbp), %xmm11",
+		},
 		// addps
 		{
 			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodeAddps, newOperandReg(xmm1VReg), xmm0VReg, true) },
@@ -794,6 +836,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "660f3833c1",
 			wantFormat: "pmovzxwd %xmm1, %xmm0",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(0, rbpVReg)), xmm11VReg, true)
+			},
+			want:       "66440f38335d00",
+			wantFormat: "pmovzxwd (%rbp), %xmm11",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(123, rbpVReg)), xmm11VReg, true)
+			},
+			want:       "66440f38335d7b",
+			wantFormat: "pmovzxwd 123(%rbp), %xmm11",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(1<<25, rbpVReg)), xmm11VReg, true)
+			},
+			want:       "66440f38339d00000002",
+			wantFormat: "pmovzxwd 33554432(%rbp), %xmm11",
+		},
 		// pmovzxwq
 		{
 			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwq, newOperandReg(xmm1VReg), xmm0VReg, true) },
@@ -851,6 +914,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "660f6ec7",
 			wantFormat: "movd %edi, %xmm0",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asGprToXmm(sseOpcodeMovd, newOperandMem(newAmodeImmReg(0, rspVReg)), xmm0VReg, true)
+			},
+			want:       "66480f6e0424",
+			wantFormat: "movd (%rsp), %xmm0",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asGprToXmm(sseOpcodeMovd, newOperandMem(newAmodeImmReg(123, rspVReg)), xmm0VReg, true)
+			},
+			want:       "66480f6e44247b",
+			wantFormat: "movd 123(%rsp), %xmm0",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asGprToXmm(sseOpcodeMovd, newOperandMem(newAmodeImmReg(1<<25, rspVReg)), xmm0VReg, true)
+			},
+			want:       "66480f6e842400000002",
+			wantFormat: "movd 33554432(%rsp), %xmm0",
+		},
 		// movq
 		{
 			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeMovq, newOperandReg(rcxVReg), xmm0VReg, true) },
@@ -863,6 +947,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "f3480f2ac7",
 			wantFormat: "cvtsi2ss %rdi, %xmm0",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asGprToXmm(sseOpcodeCvtsi2ss, newOperandMem(newAmodeImmReg(0, rspVReg)), xmm0VReg, true)
+			},
+			want:       "f3480f2a0424",
+			wantFormat: "cvtsi2ss (%rsp), %xmm0",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asGprToXmm(sseOpcodeCvtsi2ss, newOperandMem(newAmodeImmReg(123, rspVReg)), xmm0VReg, true)
+			},
+			want:       "f3480f2a44247b",
+			wantFormat: "cvtsi2ss 123(%rsp), %xmm0",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asGprToXmm(sseOpcodeCvtsi2ss, newOperandMem(newAmodeImmReg(1<<25, rspVReg)), xmm0VReg, true)
+			},
+			want:       "f3480f2a842400000002",
+			wantFormat: "cvtsi2ss 33554432(%rsp), %xmm0",
+		},
 		// cvtsi2sd
 		{
 			setup:      func(i *instruction) { i.asGprToXmm(sseOpcodeCvtsi2sd, newOperandReg(rdiVReg), xmm0VReg, true) },
@@ -894,6 +999,76 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "415f",
 			wantFormat: "popq %r15",
 		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(0, rbpVReg, r13VReg, 2))) },
+			want:       "42ff74ad00",
+			wantFormat: "pushq (%rbp,%r13,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(1, rspVReg, r13VReg, 2))) },
+			want:       "42ff74ac01",
+			wantFormat: "pushq 1(%rsp,%r13,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(1<<14, rspVReg, r13VReg, 3))) },
+			want:       "42ffb4ec00400000",
+			wantFormat: "pushq 16384(%rsp,%r13,8)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(1<<14, rspVReg, rcxVReg, 3))) },
+			want:       "ffb4cc00400000",
+			wantFormat: "pushq 16384(%rsp,%rcx,8)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(1<<14, rspVReg, rbpVReg, 3))) },
+			want:       "ffb4ec00400000",
+			wantFormat: "pushq 16384(%rsp,%rbp,8)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(1<<14, rsiVReg, rcxVReg, 3))) },
+			want:       "ffb4ce00400000",
+			wantFormat: "pushq 16384(%rsi,%rcx,8)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(321, rsiVReg, rcxVReg, 3))) },
+			want:       "ffb4ce41010000",
+			wantFormat: "pushq 321(%rsi,%rcx,8)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(0, rsiVReg, rcxVReg, 3))) },
+			want:       "ff34ce",
+			wantFormat: "pushq (%rsi,%rcx,8)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(321, r9VReg, rbxVReg, 2))) },
+			want:       "41ffb49941010000",
+			wantFormat: "pushq 321(%r9,%rbx,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(321, r9VReg, rbpVReg, 2))) },
+			want:       "41ffb4a941010000",
+			wantFormat: "pushq 321(%r9,%rbp,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(321, r9VReg, r13VReg, 2))) },
+			want:       "43ffb4a941010000",
+			wantFormat: "pushq 321(%r9,%r13,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(321, rbpVReg, r13VReg, 2))) },
+			want:       "42ffb4ad41010000",
+			wantFormat: "pushq 321(%rbp,%r13,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(0, r9VReg, r13VReg, 2))) },
+			want:       "43ff34a9",
+			wantFormat: "pushq (%r9,%r13,4)",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandMem(newAmodeRegRegShit(1<<20, r9VReg, r13VReg, 2))) },
+			want:       "43ffb4a900001000",
+			wantFormat: "pushq 1048576(%r9,%r13,4)",
+		},
 		{
 			setup:      func(i *instruction) { i.asPush64(newOperandReg(rdiVReg)) },
 			want:       "57",
@@ -938,6 +1113,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "685f173b8a",
 			wantFormat: "pushq $-1975838881",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandMem(newAmodeImmReg(0, rdiVReg)), rdxVReg, true)
+			},
+			want:       "480faf17",
+			wantFormat: "imul (%rdi), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandMem(newAmodeImmReg(99, rdiVReg)), rdxVReg, true)
+			},
+			want:       "480faf5763",
+			wantFormat: "imul 99(%rdi), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandMem(newAmodeImmReg(1<<21, rdiVReg)), rdxVReg, true)
+			},
+			want:       "480faf9700002000",
+			wantFormat: "imul 2097152(%rdi), %rdx",
+		},
 		{
 			setup: func(i *instruction) {
 				i.asAluRmiR(aluRmiROpcodeMul, newOperandReg(r15VReg), rdxVReg, true)
@@ -989,6 +1185,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "6bd2ff",
 			wantFormat: "imul $-1, %edx",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandMem(newAmodeImmReg(0, r12VReg)), rdxVReg, true)
+			},
+			want:       "49031424",
+			wantFormat: "add (%r12), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandMem(newAmodeImmReg(123, r12VReg)), rdxVReg, true)
+			},
+			want:       "490354247b",
+			wantFormat: "add 123(%r12), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandMem(newAmodeImmReg(1<<25, r12VReg)), rdxVReg, true)
+			},
+			want:       "4903942400000002",
+			wantFormat: "add 33554432(%r12), %rdx",
+		},
 		{
 			setup: func(i *instruction) {
 				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(76543210), r15VReg, true)
@@ -1084,6 +1301,27 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "4529df",
 			wantFormat: "sub %r11d, %r15d",
 		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeSub, newOperandMem(newAmodeImmReg(0, r13VReg)), rdxVReg, true)
+			},
+			want:       "492b5500",
+			wantFormat: "sub (%r13), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeSub, newOperandMem(newAmodeImmReg(123, r13VReg)), rdxVReg, true)
+			},
+			want:       "492b557b",
+			wantFormat: "sub 123(%r13), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeSub, newOperandMem(newAmodeImmReg(1<<25, r13VReg)), rdxVReg, true)
+			},
+			want:       "492b9500000002",
+			wantFormat: "sub 33554432(%r13), %rdx",
+		},
 		{
 			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAnd, newOperandReg(r11VReg), r15VReg, false) },
 			want:       "4521df",
@@ -1099,6 +1337,39 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "4531df",
 			wantFormat: "xor %r11d, %r15d",
 		},
+		{
+			setup:      func(i *instruction) { i.asLEA(newAmodeImmReg(0, rdiVReg), rdxVReg) },
+			want:       "488d17",
+			wantFormat: "lea (%rdi), %rdx",
+		},
+		{
+			setup:      func(i *instruction) { i.asLEA(newAmodeImmReg(0xffff, rdiVReg), rdxVReg) },
+			want:       "488d97ffff0000",
+			wantFormat: "lea 65535(%rdi), %rdx",
+		},
+		{
+			setup:      func(i *instruction) { i.asLEA(newAmodeRegRegShit(0xffff, rspVReg, r13VReg, 3), rdxVReg) },
+			want:       "4a8d94ecffff0000",
+			wantFormat: "lea 65535(%rsp,%r13,8), %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				a := newAmodeRipRelative(1)
+				a.resolveRipRelative(1234)
+				i.asLEA(a, r11VReg)
+			},
+			want:       "4c8d1dd2040000",
+			wantFormat: "lea 1234(%rip), %r11",
+		},
+		{
+			setup: func(i *instruction) {
+				a := newAmodeRipRelative(1)
+				a.resolveRipRelative(8)
+				i.asLEA(a, r11VReg)
+			},
+			want:       "4c8d1d08000000",
+			wantFormat: "lea 8(%rip), %r11",
+		},
 	} {
 		tc := tc
 		t.Run(tc.wantFormat, func(t *testing.T) {