Repository: tetratelabs/wazero
Pull request #1907 (2024-01-09 23:06:49)

wazevo(amd64): adds pop64, push64 and aluRmiR encodings 


Patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr.go b/internal/engine/wazevo/backend/isa/amd64/instr.go
index abc33a15..12a7c675 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr.go
@@ -101,9 +101,9 @@ func (i *instruction) String() string {
 	case cmove:
 		panic("TODO")
 	case push64:
-		panic("TODO")
+		return fmt.Sprintf("pushq %s", i.op1.format(true))
 	case pop64:
-		panic("TODO")
+		return fmt.Sprintf("popq %s", i.op1.format(true))
 	case xmmMovRM:
 		panic("TODO")
 	case xmmLoadConst:
@@ -480,7 +480,7 @@ func (a aluRmiROpcode) String() string {
 	case aluRmiROpcodeXor:
 		return "xor"
 	case aluRmiROpcodeMul:
-		return "mul"
+		return "imul"
 	default:
 		panic("BUG")
 	}
@@ -556,6 +556,21 @@ func (i *instruction) asXmmUnaryRmR(op sseOpcode, rm operand, rd regalloc.VReg,
 	return i
 }
 
+func (i *instruction) asPop64(rm regalloc.VReg) *instruction {
+	i.kind = pop64
+	i.op1 = newOperandReg(rm)
+	return i
+}
+
+func (i *instruction) asPush64(op operand) *instruction {
+	if op.kind != operandKindReg && op.kind != operandKindMem && op.kind != operandImm32 {
+		panic("BUG")
+	}
+	i.kind = push64
+	i.op1 = op
+	return i
+}
+
 type sseOpcode byte
 
 const (
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
index f24c27dc..4137f835 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
@@ -50,7 +50,82 @@ func (i *instruction) encode(c backend.Compiler) {
 		}
 
 	case aluRmiR:
-		panic("TODO")
+		var rex rexInfo
+		if i.b1 {
+			rex = rex.setW()
+		} else {
+			rex = rex.clearW()
+		}
+		aluOp := aluRmiROpcode(i.u1)
+
+		if aluOp == aluRmiROpcodeMul {
+			op1 := i.op1
+			switch op1.kind {
+			case operandKindReg:
+				src := regEncodings[op1.r.RealReg()]
+				dst := regEncodings[i.op2.r.RealReg()]
+				encodeRegReg(c, legacyPrefixesNone, 0x0faf, 2, dst, src, rex)
+			case operandKindMem:
+				panic("TODO")
+			case operandImm32:
+				imm8 := lower8willSignExtendTo32(op1.imm32)
+				var opc uint32
+				if imm8 {
+					opc = 0x6b
+				} else {
+					opc = 0x69
+				}
+				dst := regEncodings[i.op2.r.RealReg()]
+				encodeRegReg(c, legacyPrefixesNone, opc, 1, dst, dst, rex)
+				if imm8 {
+					c.EmitByte(byte(op1.imm32))
+				} else {
+					c.Emit4Bytes(op1.imm32)
+				}
+			}
+		} else {
+			var opcR, opcM, subOpcImm uint32
+			switch aluOp {
+			case aluRmiROpcodeAdd:
+				opcR, opcM, subOpcImm = 0x01, 0x03, 0x0
+			case aluRmiROpcodeSub:
+				opcR, opcM, subOpcImm = 0x29, 0x2b, 0x5
+			case aluRmiROpcodeAnd:
+				opcR, opcM, subOpcImm = 0x21, 0x23, 0x4
+			case aluRmiROpcodeOr:
+				opcR, opcM, subOpcImm = 0x09, 0x0b, 0x1
+			case aluRmiROpcodeXor:
+				opcR, opcM, subOpcImm = 0x31, 0x33, 0x6
+			default:
+				panic("BUG: invalid aluRmiROpcode")
+			}
+
+			op1 := i.op1
+			switch op1.kind {
+			case operandKindReg:
+				src := regEncodings[op1.r.RealReg()]
+				dst := regEncodings[i.op2.r.RealReg()]
+				encodeRegReg(c, legacyPrefixesNone, opcR, 1, src, dst, rex)
+			case operandKindMem:
+				fmt.Println(opcM)
+				panic("TODO")
+			case operandImm32:
+				imm8 := lower8willSignExtendTo32(op1.imm32)
+				var opc uint32
+				if imm8 {
+					opc = 0x83
+				} else {
+					opc = 0x81
+				}
+				dst := regEncodings[i.op2.r.RealReg()]
+				encodeRegReg(c, legacyPrefixesNone, opc, 1, regEnc(subOpcImm), dst, rex)
+				if imm8 {
+					c.EmitByte(byte(op1.imm32))
+				} else {
+					c.Emit4Bytes(op1.imm32)
+				}
+			}
+		}
 
 	case movRR:
 		src := regEncodings[i.op1.r.RealReg()]
@@ -409,9 +484,28 @@ func (i *instruction) encode(c backend.Compiler) {
 	case cmove:
 		panic("TODO")
 	case push64:
-		panic("TODO")
+		op := i.op1
+
+		switch op.kind {
+		case operandKindReg:
+			dst := regEncodings[op.r.RealReg()]
+			if dst.rexBit() > 0 {
+				c.EmitByte(rexEncodingDefault | 0x1)
+			}
+			c.EmitByte(0x50 | dst.encoding())
+		case operandKindMem:
+			panic("TODO")
+		case operandImm32:
+			c.EmitByte(0x68)
+			c.Emit4Bytes(op.imm32)
+		}
+
 	case pop64:
-		panic("TODO")
+		dst := regEncodings[i.op1.r.RealReg()]
+		if dst.rexBit() > 0 {
+			c.EmitByte(rexEncodingDefault | 0x1)
+		}
+		c.EmitByte(0x58 | dst.encoding())
 	case xmmMovRM:
 		panic("TODO")
 	case xmmLoadConst:
@@ -607,3 +701,8 @@ func lower32willSignExtendTo64(x uint64) bool {
 	xs := int64(x)
 	return xs == int64(uint64(int32(xs)))
 }
+
+func lower8willSignExtendTo32(x uint32) bool {
+	xs := int32(x)
+	return xs == ((xs << 24) >> 24)
+}
diff --git a/internal/engine/wazevo/backend/isa/amd64/operands.go b/internal/engine/wazevo/backend/isa/amd64/operands.go
index feba2aa1..a3959362 100644
--- a/internal/engine/wazevo/backend/isa/amd64/operands.go
+++ b/internal/engine/wazevo/backend/isa/amd64/operands.go
@@ -34,7 +34,7 @@ func (o *operand) format(_64 bool) string {
 	case operandKindMem:
 		return o.amode.String()
 	case operandImm32:
-		return fmt.Sprintf("$%d", o.imm32)
+		return fmt.Sprintf("$%d", int32(o.imm32))
 	default:
 		panic("BUG: invalid operand kind")
 	}
@@ -44,6 +44,10 @@ func newOperandReg(r regalloc.VReg) operand {
 	return operand{kind: operandKindReg, r: r}
 }
 
+func newOperandImm32(imm32 uint32) operand {
+	return operand{kind: operandImm32, imm32: imm32}
+}
+
 // nolint
 type amode struct {
 	kind  amodeKind
diff --git a/internal/engine/wazevo/backend/isa/amd64/reg.go b/internal/engine/wazevo/backend/isa/amd64/reg.go
index c47343a2..4aec856f 100644
--- a/internal/engine/wazevo/backend/isa/amd64/reg.go
+++ b/internal/engine/wazevo/backend/isa/amd64/reg.go
@@ -7,10 +7,6 @@ import (
 )
 
 // Amd64-specific registers.
-//
-// Note: naming convention intentionally matches the Go assembler: https://go.dev/doc/asm
-// See https://www.lri.fr/~filliatr/ens/compil/x86-64.pdf
-// See https://cs.brown.edu/courses/cs033/docs/guides/x64_cheatsheet.pdf
 const (
 	// rax is a gp register.
 	rax = regalloc.RealRegInvalid + 1 + iota


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 7f6f2a11..741b7f59 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
@@ -874,6 +874,231 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "f20f2ac7",
 			wantFormat: "cvtsi2sd %edi, %xmm0",
 		},
+		{
+			setup:      func(i *instruction) { i.asPop64(raxVReg) },
+			want:       "58",
+			wantFormat: "popq %rax",
+		},
+		{
+			setup:      func(i *instruction) { i.asPop64(rdiVReg) },
+			want:       "5f",
+			wantFormat: "popq %rdi",
+		},
+		{
+			setup:      func(i *instruction) { i.asPop64(r8VReg) },
+			want:       "4158",
+			wantFormat: "popq %r8",
+		},
+		{
+			setup:      func(i *instruction) { i.asPop64(r15VReg) },
+			want:       "415f",
+			wantFormat: "popq %r15",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandReg(rdiVReg)) },
+			want:       "57",
+			wantFormat: "pushq %rdi",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandReg(r8VReg)) },
+			want:       "4150",
+			wantFormat: "pushq %r8",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandImm32(128)) },
+			want:       "6880000000",
+			wantFormat: "pushq $128",
+		},
+		{
+			setup:      func(i *instruction) { i.asPush64(newOperandImm32(0x31415927)) },
+			want:       "6827594131",
+			wantFormat: "pushq $826366247",
+		},
+		{
+			setup: func(i *instruction) {
+				v := int32(-128)
+				i.asPush64(newOperandImm32(uint32(v)))
+			},
+			want:       "6880ffffff",
+			wantFormat: "pushq $-128",
+		},
+		{
+			setup: func(i *instruction) {
+				v := int32(-129)
+				i.asPush64(newOperandImm32(uint32(v)))
+			},
+			want:       "687fffffff",
+			wantFormat: "pushq $-129",
+		},
+		{
+			setup: func(i *instruction) {
+				v := int32(-0x75c4e8a1)
+				i.asPush64(newOperandImm32(uint32(v)))
+			},
+			want:       "685f173b8a",
+			wantFormat: "pushq $-1975838881",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandReg(r15VReg), rdxVReg, true)
+			},
+			want:       "490fafd7",
+			wantFormat: "imul %r15, %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandReg(rcxVReg), r8VReg, false)
+			},
+			want:       "440fafc1",
+			wantFormat: "imul %ecx, %r8d",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandReg(rcxVReg), rsiVReg, false)
+			},
+			want:       "0faff1",
+			wantFormat: "imul %ecx, %esi",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandImm32(76543210), rdxVReg, true)
+			},
+			want:       "4869d2eaf48f04",
+			wantFormat: "imul $76543210, %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				minusOne := int32(-1)
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandImm32(uint32(minusOne)), rdxVReg, true)
+			},
+			want:       "486bd2ff",
+			wantFormat: "imul $-1, %rdx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandImm32(76543210), rdxVReg, false)
+			},
+			want:       "69d2eaf48f04",
+			wantFormat: "imul $76543210, %edx",
+		},
+		{
+			setup: func(i *instruction) {
+				minusOne := int32(-1)
+				i.asAluRmiR(aluRmiROpcodeMul, newOperandImm32(uint32(minusOne)), rdxVReg, false)
+			},
+			want:       "6bd2ff",
+			wantFormat: "imul $-1, %edx",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(76543210), r15VReg, true)
+			},
+			want:       "4981c7eaf48f04",
+			wantFormat: "add $76543210, %r15",
+		},
+		{
+			setup: func(i *instruction) {
+				minusOne := int32(-1)
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(uint32(minusOne)), r15VReg, true)
+			},
+			want:       "4983c7ff",
+			wantFormat: "add $-1, %r15",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(76543210), rsiVReg, true)
+			},
+			want:       "4881c6eaf48f04",
+			wantFormat: "add $76543210, %rsi",
+		},
+		{
+			setup: func(i *instruction) {
+				minusOne := int32(-1)
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(uint32(minusOne)), rsiVReg, true)
+			},
+			want:       "4883c6ff",
+			wantFormat: "add $-1, %rsi",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(76543210), r15VReg, false)
+			},
+			want:       "4181c7eaf48f04",
+			wantFormat: "add $76543210, %r15d",
+		},
+		{
+			setup: func(i *instruction) {
+				minusOne := int32(-1)
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(uint32(minusOne)), r15VReg, false)
+			},
+			want:       "4183c7ff",
+			wantFormat: "add $-1, %r15d",
+		},
+		{
+			setup: func(i *instruction) {
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(76543210), rsiVReg, false)
+			},
+			want:       "81c6eaf48f04",
+			wantFormat: "add $76543210, %esi",
+		},
+		{
+			setup: func(i *instruction) {
+				minusOne := int32(-1)
+				i.asAluRmiR(aluRmiROpcodeAdd, newOperandImm32(uint32(minusOne)), rsiVReg, false)
+			},
+			want:       "83c6ff",
+			wantFormat: "add $-1, %esi",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAdd, newOperandReg(raxVReg), rdxVReg, true) },
+			want:       "4801c2",
+			wantFormat: "add %rax, %rdx",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAdd, newOperandReg(raxVReg), r15VReg, true) },
+			want:       "4901c7",
+			wantFormat: "add %rax, %r15",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAdd, newOperandReg(r11VReg), r15VReg, true) },
+			want:       "4d01df",
+			wantFormat: "add %r11, %r15",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAdd, newOperandReg(raxVReg), rdxVReg, false) },
+			want:       "01c2",
+			wantFormat: "add %eax, %edx",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAdd, newOperandReg(raxVReg), r15VReg, false) },
+			want:       "4101c7",
+			wantFormat: "add %eax, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAdd, newOperandReg(r11VReg), r15VReg, false) },
+			want:       "4501df",
+			wantFormat: "add %r11d, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeSub, newOperandReg(r11VReg), r15VReg, false) },
+			want:       "4529df",
+			wantFormat: "sub %r11d, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeAnd, newOperandReg(r11VReg), r15VReg, false) },
+			want:       "4521df",
+			wantFormat: "and %r11d, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeOr, newOperandReg(r11VReg), r15VReg, false) },
+			want:       "4509df",
+			wantFormat: "or %r11d, %r15d",
+		},
+		{
+			setup:      func(i *instruction) { i.asAluRmiR(aluRmiROpcodeXor, newOperandReg(r11VReg), r15VReg, false) },
+			want:       "4531df",
+			wantFormat: "xor %r11d, %r15d",
+		},
 	} {
 		tc := tc
 		t.Run(tc.wantFormat, func(t *testing.T) {