Repository: tetratelabs/wazero
Pull request #1931 (2024-01-20 23:07:18)

wazevo(amd64): clobbered register handling


Patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr.go b/internal/engine/wazevo/backend/isa/amd64/instr.go
index b404b04e..bbe7506b 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr.go
@@ -66,7 +66,7 @@ func (i *instruction) String() string {
 	case gprToXmm:
 		return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
 	case xmmUnaryRmR:
-		return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(i.b1), i.op2.format(i.b1))
+		return fmt.Sprintf("%s %s, %s", sseOpcode(i.u1), i.op1.format(false), i.op2.format(false))
 	case unaryRmR:
 		var suffix string
 		if i.b1 {
@@ -852,7 +852,7 @@ func (i *instruction) asCmove(c cond, rm operand, rd regalloc.VReg, _64 bool) *i
 	return i
 }
 
-func (i *instruction) asXmmUnaryRmR(op sseOpcode, rm operand, rd regalloc.VReg, _64 bool) *instruction {
+func (i *instruction) asXmmUnaryRmR(op sseOpcode, rm operand, rd regalloc.VReg) *instruction {
 	if rm.kind != operandKindReg && rm.kind != operandKindMem {
 		panic("BUG")
 	}
@@ -860,7 +860,6 @@ func (i *instruction) asXmmUnaryRmR(op sseOpcode, rm operand, rd regalloc.VReg,
 	i.op1 = rm
 	i.op2 = newOperandReg(rd)
 	i.u1 = uint64(op)
-	i.b1 = _64
 	return i
 }
 
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine.go b/internal/engine/wazevo/backend/isa/amd64/machine.go
index 92ef809b..b954240f 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine.go
@@ -240,7 +240,7 @@ func (m *machine) InsertMove(dst, src regalloc.VReg, typ ssa.Type) {
 		case ssa.TypeV128:
 			op = sseOpcodeMovdqa
 		}
-		i := m.allocateInstr().asXmmUnaryRmR(op, operand{kind: operandKindReg, r: src}, dst, typ.Bits() == 64)
+		i := m.allocateInstr().asXmmUnaryRmR(op, newOperandReg(src), dst)
 		m.insert(i)
 	default:
 		panic("BUG")
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue.go b/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue.go
index 54211a68..2c485381 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue.go
@@ -1,5 +1,7 @@
 package amd64
 
+import "github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
+
 // CompileStackGrowCallSequence implements backend.Machine.
 func (m *machine) CompileStackGrowCallSequence() []byte {
 	// TODO
@@ -30,11 +32,7 @@ func (m *machine) SetupPrologue() {
 	//                    (low address)
 
 	// First, we push the RBP, and update the RBP to the current RSP.
-	// 		push %rbp
-	// 		mov %rsp, %rbp
-	cur = linkInstr(cur, m.allocateInstr().asPush64(newOperandReg(rbpVReg)))
-	cur = linkInstr(cur, m.allocateInstr().asMovRR(rspVReg, rbpVReg, true))
-
+	//
 	//                   (high address)                     (high address)
 	//       RBP ----> +-----------------+                +-----------------+
 	//                 |     .......     |                |     .......     |
@@ -46,15 +44,54 @@ func (m *machine) SetupPrologue() {
 	//                 |      arg 1      |                |      arg 1      |
 	//                 |      arg 0      |                |      arg 0      |
 	//                 |   Return Addr   |                |   Return Addr   |
-	//       RSP ----> +-----------------+                |    Frame Addr   |
+	//       RSP ----> +-----------------+                |    Caller_RBP   |
 	//                    (low address)                   +-----------------+ <----- RSP, RBP
 	//
+	// 		push %rbp
+	// 		mov %rsp, %rbp
+	cur = linkInstr(cur, m.allocateInstr().asPush64(newOperandReg(rbpVReg)))
+	cur = linkInstr(cur, m.allocateInstr().asMovRR(rspVReg, rbpVReg, true))
+
 	if !m.stackBoundsCheckDisabled { //nolint
 		// TODO: stack bounds check
 	}
 
+	//
+	//            (high address)
+	//          +-----------------+                  +-----------------+
+	//          |     .......     |                  |     .......     |
+	//          |      ret Y      |                  |      ret Y      |
+	//          |     .......     |                  |     .......     |
+	//          |      ret 0      |                  |      ret 0      |
+	//          |      arg X      |                  |      arg X      |
+	//          |     .......     |                  |     .......     |
+	//          |      arg 1      |                  |      arg 1      |
+	//          |      arg 0      |                  |      arg 0      |
+	//          |      xxxxx      |                  |      xxxxx      |
+	//          |   Return Addr   |                  |   Return Addr   |
+	//          |    Caller_RBP   |      ====>       |    Caller_RBP   |
+	// RBP,RSP->+-----------------+                  +-----------------+ <----- RBP
+	//             (low address)                     |   clobbered M   |
+	//                                               |   clobbered 1   |
+	//                                               |   ...........   |
+	//                                               |   clobbered 0   |
+	//                                               +-----------------+ <----- RSP
+	//
 	if regs := m.clobberedRegs; len(regs) > 0 {
-		panic("TODO: save clobbered registers")
+		for i := range regs {
+			r := regs[len(regs)-1-i] // Reverse order.
+			if r.RegType() == regalloc.RegTypeInt {
+				cur = linkInstr(cur, m.allocateInstr().asPush64(newOperandReg(r)))
+			} else {
+				// Push the XMM register is not supported by the PUSH instruction.
+				spDec := m.allocateInstr().asAluRmiR(aluRmiROpcodeSub, newOperandImm32(uint32(16)), rspVReg, true)
+				cur = linkInstr(cur, spDec)
+				push := m.allocateInstr().asXmmMovRM(
+					sseOpcodeMovdqu, r, newOperandMem(newAmodeImmReg(0, rspVReg)),
+				)
+				cur = linkInstr(cur, push)
+			}
+		}
 	}
 
 	if size := m.spillSlotSize; size > 0 {
@@ -117,11 +154,47 @@ func (m *machine) setupEpilogueAfter(cur *instruction) {
 	if size := m.spillSlotSize; size > 0 {
 		panic("TODO: deallocate spill slots")
 	}
+
+	//
+	//             (high address)
+	//            +-----------------+                     +-----------------+
+	//            |     .......     |                     |     .......     |
+	//            |      ret Y      |                     |      ret Y      |
+	//            |     .......     |                     |     .......     |
+	//            |      ret 0      |                     |      ret 0      |
+	//            |      arg X      |                     |      arg X      |
+	//            |     .......     |                     |     .......     |
+	//            |      arg 1      |                     |      arg 1      |
+	//            |      arg 0      |                     |      arg 0      |
+	//            |   ReturnAddress |                     |   ReturnAddress |
+	//            |    Caller_RBP   |                     |    Caller_RBP   |
+	//   RBP ---> +-----------------+      ========>      +-----------------+ <---- RSP, RBP
+	//            |    clobbered M  |
+	//            |   ............  |
+	//            |    clobbered 1  |
+	//            |    clobbered 0  |
+	//   RSP ---> +-----------------+
+	//               (low address)
+	//
 	if regs := m.clobberedRegs; len(regs) > 0 {
-		panic("TODO: restore clobbered registers")
+		for _, r := range regs {
+			if r.RegType() == regalloc.RegTypeInt {
+				cur = linkInstr(cur, m.allocateInstr().asPop64(r))
+			} else {
+				// Pop the XMM register is not supported by the POP instruction.
+				pop := m.allocateInstr().asXmmUnaryRmR(
+					sseOpcodeMovdqu, newOperandMem(newAmodeImmReg(0, rspVReg)), r,
+				)
+				cur = linkInstr(cur, pop)
+				spInc := m.allocateInstr().asAluRmiR(
+					aluRmiROpcodeAdd, newOperandImm32(uint32(16)), rspVReg, true,
+				)
+				cur = linkInstr(cur, spInc)
+			}
+		}
 	}
 
-	// Now roll back the RSP to the return address, and pop the RBP.
+	// Now roll back the RSP to RBP, and pop the caller's RBP.
 	// 		mov  %rbp, %rsp
 	// 		pop  %rbp
 	cur = linkInstr(cur, m.allocateInstr().asMovRR(rbpVReg, rspVReg, true))
diff --git a/internal/engine/wazevo/backend/isa/amd64/operands.go b/internal/engine/wazevo/backend/isa/amd64/operands.go
index 49d35500..59c7b824 100644
--- a/internal/engine/wazevo/backend/isa/amd64/operands.go
+++ b/internal/engine/wazevo/backend/isa/amd64/operands.go
@@ -47,6 +47,8 @@ func (o operandKind) String() string {
 	}
 }
 
+// format returns the string representation of the operand.
+// _64 is only for the case where the operand is a register, and it's integer.
 func (o *operand) format(_64 bool) string {
 	switch o.kind {
 	case operandKindReg:


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 494a226f..2e5c78a8 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
@@ -890,211 +890,211 @@ func TestInstruction_format_encode(t *testing.T) {
 		},
 		// cvtss2sd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeCvtss2sd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeCvtss2sd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f30f5ac1",
 			wantFormat: "cvtss2sd %xmm1, %xmm0",
 		},
 		// cvtsd2ss
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeCvtsd2ss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeCvtsd2ss, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f20f5ac1",
 			wantFormat: "cvtsd2ss %xmm1, %xmm0",
 		},
 		// movaps
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovaps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovaps, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "0f28c1",
 			wantFormat: "movaps %xmm1, %xmm0",
 		},
 		// movapd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovapd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovapd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f28c1",
 			wantFormat: "movapd %xmm1, %xmm0",
 		},
 		// movdqa
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovdqa, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovdqa, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f6fc1",
 			wantFormat: "movdqa %xmm1, %xmm0",
 		},
 		// movdqu
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovdqu, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovdqu, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f30f6fc1",
 			wantFormat: "movdqu %xmm1, %xmm0",
 		},
 		// movsd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovsd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f20f10c1",
 			wantFormat: "movsd %xmm1, %xmm0",
 		},
 		// movss
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovss, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f30f10c1",
 			wantFormat: "movss %xmm1, %xmm0",
 		},
 		// movups
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovups, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovups, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "0f10c1",
 			wantFormat: "movups %xmm1, %xmm0",
 		},
 		// movupd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovupd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeMovupd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f10c1",
 			wantFormat: "movupd %xmm1, %xmm0",
 		},
 		// pabsb
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsb, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsb, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f381cc1",
 			wantFormat: "pabsb %xmm1, %xmm0",
 		},
 		// pabsw
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsw, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f381dc1",
 			wantFormat: "pabsw %xmm1, %xmm0",
 		},
 		// pabsd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePabsd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f381ec1",
 			wantFormat: "pabsd %xmm1, %xmm0",
 		},
 		// pmovsxbd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3821c1",
 			wantFormat: "pmovsxbd %xmm1, %xmm0",
 		},
 		// pmovsxbw
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbw, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3820c1",
 			wantFormat: "pmovsxbw %xmm1, %xmm0",
 		},
 		// pmovsxbq
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxbq, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3822c1",
 			wantFormat: "pmovsxbq %xmm1, %xmm0",
 		},
 		// pmovsxwd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxwd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxwd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3823c1",
 			wantFormat: "pmovsxwd %xmm1, %xmm0",
 		},
 		// pmovsxwq
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxwq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxwq, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3824c1",
 			wantFormat: "pmovsxwq %xmm1, %xmm0",
 		},
 		// pmovsxdq
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxdq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovsxdq, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3825c1",
 			wantFormat: "pmovsxdq %xmm1, %xmm0",
 		},
 		// pmovzxbd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3831c1",
 			wantFormat: "pmovzxbd %xmm1, %xmm0",
 		},
 		// pmovzxbw
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbw, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbw, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3830c1",
 			wantFormat: "pmovzxbw %xmm1, %xmm0",
 		},
 		// pmovzxbq
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxbq, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3832c1",
 			wantFormat: "pmovzxbq %xmm1, %xmm0",
 		},
 		// pmovzxwd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3833c1",
 			wantFormat: "pmovzxwd %xmm1, %xmm0",
 		},
 		{
 			setup: func(i *instruction) {
-				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(0, rbpVReg)), xmm11VReg, true)
+				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(0, rbpVReg)), xmm11VReg)
 			},
 			want:       "66440f38335d00",
 			wantFormat: "pmovzxwd (%rbp), %xmm11",
 		},
 		{
 			setup: func(i *instruction) {
-				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(123, rbpVReg)), xmm11VReg, true)
+				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(123, rbpVReg)), xmm11VReg)
 			},
 			want:       "66440f38335d7b",
 			wantFormat: "pmovzxwd 123(%rbp), %xmm11",
 		},
 		{
 			setup: func(i *instruction) {
-				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(1<<25, rbpVReg)), xmm11VReg, true)
+				i.asXmmUnaryRmR(sseOpcodePmovzxwd, newOperandMem(newAmodeImmReg(1<<25, rbpVReg)), xmm11VReg)
 			},
 			want:       "66440f38339d00000002",
 			wantFormat: "pmovzxwd 33554432(%rbp), %xmm11",
 		},
 		// pmovzxwq
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxwq, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3834c1",
 			wantFormat: "pmovzxwq %xmm1, %xmm0",
 		},
 		// pmovzxdq
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxdq, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodePmovzxdq, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f3835c1",
 			wantFormat: "pmovzxdq %xmm1, %xmm0",
 		},
 		// sqrtps
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtps, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtps, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "0f51c1",
 			wantFormat: "sqrtps %xmm1, %xmm0",
 		},
 		// sqrtpd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtpd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtpd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "660f51c1",
 			wantFormat: "sqrtpd %xmm1, %xmm0",
 		},
 		// sqrtss
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtss, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtss, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f30f51c1",
 			wantFormat: "sqrtss %xmm1, %xmm0",
 		},
 		// sqrtsd
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm1VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm1VReg), xmm0VReg) },
 			want:       "f20f51c1",
 			wantFormat: "sqrtsd %xmm1, %xmm0",
 		},
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm15VReg), xmm0VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm15VReg), xmm0VReg) },
 			want:       "f2410f51c7",
 			wantFormat: "sqrtsd %xmm15, %xmm0",
 		},
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm1VReg), xmm15VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm1VReg), xmm15VReg) },
 			want:       "f2440f51f9",
 			wantFormat: "sqrtsd %xmm1, %xmm15",
 		},
 		{
-			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm11VReg), xmm15VReg, true) },
+			setup:      func(i *instruction) { i.asXmmUnaryRmR(sseOpcodeSqrtsd, newOperandReg(xmm11VReg), xmm15VReg) },
 			want:       "f2450f51fb",
 			wantFormat: "sqrtsd %xmm11, %xmm15",
 		},
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue_test.go b/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue_test.go
index e8d15942..a5ba3984 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine_pro_epi_logue_test.go
@@ -24,6 +24,29 @@ func TestMachine_SetupPrologue(t *testing.T) {
 	ud2
 `,
 		},
+		{
+			exp: `
+	pushq %rbp
+	movq %rsp, %rbp
+	sub $16, %rsp
+	movdqu %xmm15, (%rsp)
+	sub $16, %rsp
+	movdqu %xmm1, (%rsp)
+	sub $16, %rsp
+	movdqu %xmm0, (%rsp)
+	pushq %rcx
+	pushq %rax
+	ud2
+`,
+			spillSlotSize: 0,
+			clobberedRegs: []regalloc.VReg{
+				raxVReg,
+				rcxVReg,
+				xmm0VReg,
+				xmm1VReg,
+				xmm15VReg,
+			},
+		},
 		// TODO: add more test cases.
 	} {
 		tc := tc
@@ -65,6 +88,29 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 			spillSlotSize: 0,
 			clobberedRegs: nil,
 		},
+		{
+			exp: `
+	popq %rax
+	popq %rcx
+	movdqu (%rsp), %xmm0
+	add $16, %rsp
+	movdqu (%rsp), %xmm1
+	add $16, %rsp
+	movdqu (%rsp), %xmm15
+	add $16, %rsp
+	movq %rbp, %rsp
+	popq %rbp
+	ret
+`,
+			spillSlotSize: 0,
+			clobberedRegs: []regalloc.VReg{
+				raxVReg,
+				rcxVReg,
+				xmm0VReg,
+				xmm1VReg,
+				xmm15VReg,
+			},
+		},
 		// TODO: add more test cases.
 	} {
 		tc := tc
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index 064c60c5..ad50e630 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -109,9 +109,7 @@ func TestE2E(t *testing.T) {
 		},
 		{
 			name: "call_simple", m: testcases.CallSimple.Module, calls: []callCase{{expResults: []uint64{40}}},
-			skipAMD64: false,
 		},
-
 		{
 			name: "call", m: testcases.Call.Module, calls: []callCase{{expResults: []uint64{45, 45}}},
 			skipAMD64: true,