Repository: tetratelabs/wazero
Pull request #1378 (2023-04-18 05:44:08)

asm/arm64: uses enum for operand types
This speeds up the large switch in the assembler.

```
goos: darwin
goarch: arm64
pkg: github.com/tetratelabs/wazero/internal/integration_test/bench
                                   │   old.txt   │              new.txt              │
                                   │   sec/op    │   sec/op     vs base              │
Compilation_sqlite3/compiler-10      191.9m ± 1%   183.5m ± 1%  -4.39% (p=0.001 n=7)
Compilation_sqlite3/interpreter-10   61.83m ± 0%   61.86m ± 1%       ~ (p=0.902 n=7)
geomean                              108.9m        106.5m       -2.20%

                                   │   old.txt    │              new.txt               │
                                   │     B/op     │     B/op      vs base              │
Compilation_sqlite3/compiler-10      42.93Mi ± 0%   42.93Mi ± 0%       ~ (p=0.902 n=7)
Compilation_sqlite3/interpreter-10   51.75Mi ± 0%   51.75Mi ± 0%       ~ (p=0.128 n=7)
geomean                              47.13Mi        47.13Mi       -0.00%

                                   │   old.txt   │              new.txt              │
                                   │  allocs/op  │  allocs/op   vs base              │
Compilation_sqlite3/compiler-10      26.50k ± 0%   26.50k ± 0%       ~ (p=0.878 n=7)
Compilation_sqlite3/interpreter-10   13.90k ± 0%   13.90k ± 0%       ~ (p=0.629 n=7)
geomean                              19.19k        19.19k       -0.01%

```


Patch:
diff --git a/internal/asm/arm64/impl.go b/internal/asm/arm64/impl.go
index 281d1d4c..a9cbd317 100644
--- a/internal/asm/arm64/impl.go
+++ b/internal/asm/arm64/impl.go
@@ -118,91 +118,79 @@ func (n *nodeImpl) String() (ret string) {
 	return
 }
 
-// operandType represents where an operand is placed for an instruction.
-// Note: this is almost the same as obj.AddrType in GO assembler.
-type operandType byte
+// operandTypes represents types of operands of a node.
+type operandTypes byte
 
 const (
-	operandTypeNone operandType = iota
-	operandTypeRegister
-	operandTypeLeftShiftedRegister
-	operandTypeTwoRegisters
-	operandTypeThreeRegisters
-	operandTypeRegisterAndConst
-	operandTypeMemory
-	operandTypeConst
-	operandTypeBranch
-	operandTypeSIMDByte
-	operandTypeTwoSIMDBytes
-	operandTypeVectorRegister
-	operandTypeTwoVectorRegisters
-	operandTypeStaticConst
+	operandTypesNoneToNone operandTypes = iota
+	operandTypesNoneToRegister
+	operandTypesNoneToBranch
+	operandTypesRegisterToRegister
+	operandTypesLeftShiftedRegisterToRegister
+	operandTypesTwoRegistersToRegister
+	operandTypesThreeRegistersToRegister
+	operandTypesTwoRegistersToNone
+	operandTypesRegisterAndConstToNone
+	operandTypesRegisterAndConstToRegister
+	operandTypesRegisterToMemory
+	operandTypesMemoryToRegister
+	operandTypesConstToRegister
+	operandTypesRegisterToVectorRegister
+	operandTypesVectorRegisterToRegister
+	operandTypesMemoryToVectorRegister
+	operandTypesVectorRegisterToMemory
+	operandTypesVectorRegisterToVectorRegister
+	operandTypesTwoVectorRegistersToVectorRegister
+	operandTypesStaticConstToVectorRegister
 )
 
-// String implements fmt.Stringer.
-func (o operandType) String() (ret string) {
+// String implements fmt.Stringer
+func (o operandTypes) String() (ret string) {
 	switch o {
-	case operandTypeNone:
-		ret = "none"
-	case operandTypeRegister:
-		ret = "register"
-	case operandTypeLeftShiftedRegister:
-		ret = "left-shifted-register"
-	case operandTypeTwoRegisters:
-		ret = "two-registers"
-	case operandTypeRegisterAndConst:
-		ret = "register-and-const"
-	case operandTypeMemory:
-		ret = "memory"
-	case operandTypeConst:
-		ret = "const"
-	case operandTypeBranch:
-		ret = "branch"
-	case operandTypeSIMDByte:
-		ret = "simd-byte"
-	case operandTypeTwoSIMDBytes:
-		ret = "two-simd-bytes"
-	case operandTypeVectorRegister:
-		ret = "vector-register"
-	case operandTypeStaticConst:
-		ret = "static-const"
-	case operandTypeTwoVectorRegisters:
-		ret = "two-vector-registers"
+	case operandTypesNoneToNone:
+		ret = "NoneToNone"
+	case operandTypesNoneToRegister:
+		ret = "NoneToRegister"
+	case operandTypesNoneToBranch:
+		ret = "NoneToBranch"
+	case operandTypesRegisterToRegister:
+		ret = "RegisterToRegister"
+	case operandTypesLeftShiftedRegisterToRegister:
+		ret = "LeftShiftedRegisterToRegister"
+	case operandTypesTwoRegistersToRegister:
+		ret = "TwoRegistersToRegister"
+	case operandTypesThreeRegistersToRegister:
+		ret = "ThreeRegistersToRegister"
+	case operandTypesTwoRegistersToNone:
+		ret = "TwoRegistersToNone"
+	case operandTypesRegisterAndConstToNone:
+		ret = "RegisterAndConstToNone"
+	case operandTypesRegisterAndConstToRegister:
+		ret = "RegisterAndConstToRegister"
+	case operandTypesRegisterToMemory:
+		ret = "RegisterToMemory"
+	case operandTypesMemoryToRegister:
+		ret = "MemoryToRegister"
+	case operandTypesConstToRegister:
+		ret = "ConstToRegister"
+	case operandTypesRegisterToVectorRegister:
+		ret = "RegisterToVectorRegister"
+	case operandTypesVectorRegisterToRegister:
+		ret = "VectorRegisterToRegister"
+	case operandTypesMemoryToVectorRegister:
+		ret = "MemoryToVectorRegister"
+	case operandTypesVectorRegisterToMemory:
+		ret = "VectorRegisterToMemory"
+	case operandTypesVectorRegisterToVectorRegister:
+		ret = "VectorRegisterToVectorRegister"
+	case operandTypesTwoVectorRegistersToVectorRegister:
+		ret = "TwoVectorRegistersToVectorRegister"
+	case operandTypesStaticConstToVectorRegister:
+		ret = "StaticConstToVectorRegister"
 	}
 	return
 }
 
-// operandTypes represents the only combinations of two operandTypes used by wazero
-type operandTypes struct{ src, dst operandType }
-
-var (
-	operandTypesNoneToNone                         = operandTypes{operandTypeNone, operandTypeNone}
-	operandTypesNoneToRegister                     = operandTypes{operandTypeNone, operandTypeRegister}
-	operandTypesNoneToBranch                       = operandTypes{operandTypeNone, operandTypeBranch}
-	operandTypesRegisterToRegister                 = operandTypes{operandTypeRegister, operandTypeRegister}
-	operandTypesLeftShiftedRegisterToRegister      = operandTypes{operandTypeLeftShiftedRegister, operandTypeRegister}
-	operandTypesTwoRegistersToRegister             = operandTypes{operandTypeTwoRegisters, operandTypeRegister}
-	operandTypesThreeRegistersToRegister           = operandTypes{operandTypeThreeRegisters, operandTypeRegister}
-	operandTypesTwoRegistersToNone                 = operandTypes{operandTypeTwoRegisters, operandTypeNone}
-	operandTypesRegisterAndConstToNone             = operandTypes{operandTypeRegisterAndConst, operandTypeNone}
-	operandTypesRegisterAndConstToRegister         = operandTypes{operandTypeRegisterAndConst, operandTypeRegister}
-	operandTypesRegisterToMemory                   = operandTypes{operandTypeRegister, operandTypeMemory}
-	operandTypesMemoryToRegister                   = operandTypes{operandTypeMemory, operandTypeRegister}
-	operandTypesConstToRegister                    = operandTypes{operandTypeConst, operandTypeRegister}
-	operandTypesRegisterToVectorRegister           = operandTypes{operandTypeRegister, operandTypeVectorRegister}
-	operandTypesVectorRegisterToRegister           = operandTypes{operandTypeVectorRegister, operandTypeRegister}
-	operandTypesMemoryToVectorRegister             = operandTypes{operandTypeMemory, operandTypeVectorRegister}
-	operandTypesVectorRegisterToMemory             = operandTypes{operandTypeVectorRegister, operandTypeMemory}
-	operandTypesVectorRegisterToVectorRegister     = operandTypes{operandTypeVectorRegister, operandTypeVectorRegister}
-	operandTypesTwoVectorRegistersToVectorRegister = operandTypes{operandTypeTwoVectorRegisters, operandTypeVectorRegister}
-	operandTypesStaticConstToVectorRegister        = operandTypes{operandTypeStaticConst, operandTypeVectorRegister}
-)
-
-// String implements fmt.Stringer
-func (o operandTypes) String() string {
-	return fmt.Sprintf("from:%s,to:%s", o.src, o.dst)
-}
-
 const (
 	maxSignedInt26 int64 = 1<<25 - 1
 	minSignedInt26 int64 = -(1 << 25)


Test patch:
diff --git a/internal/asm/arm64/impl_1_test.go b/internal/asm/arm64/impl_1_test.go
index 244d7d28..7a76ac2a 100644
--- a/internal/asm/arm64/impl_1_test.go
+++ b/internal/asm/arm64/impl_1_test.go
@@ -283,8 +283,7 @@ func TestAssemblerImpl_newNode(t *testing.T) {
 	a := NewAssembler(RegR10)
 	actual := a.newNode(MOVD, operandTypesMemoryToRegister)
 	require.Equal(t, MOVD, actual.instruction)
-	require.Equal(t, operandTypeMemory, actual.types.src)
-	require.Equal(t, operandTypeRegister, actual.types.dst)
+	require.Equal(t, operandTypesMemoryToRegister, actual.types)
 	require.Equal(t, actual, a.root)
 	require.Equal(t, actual, a.current)
 }
@@ -294,8 +293,7 @@ func TestAssemblerImpl_CompileStandAlone(t *testing.T) {
 	a.CompileStandAlone(RET)
 	actualNode := a.current
 	require.Equal(t, RET, actualNode.instruction)
-	require.Equal(t, operandTypeNone, actualNode.types.src)
-	require.Equal(t, operandTypeNone, actualNode.types.dst)
+	require.Equal(t, operandTypesNoneToNone, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileConstToRegister(t *testing.T) {
@@ -305,8 +303,7 @@ func TestAssemblerImpl_CompileConstToRegister(t *testing.T) {
 	require.Equal(t, MOVD, actualNode.instruction)
 	require.Equal(t, int64(1000), actualNode.srcConst)
 	require.Equal(t, RegR10, actualNode.dstReg)
-	require.Equal(t, operandTypeConst, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesConstToRegister, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileRegisterToRegister(t *testing.T) {
@@ -316,8 +313,7 @@ func TestAssemblerImpl_CompileRegisterToRegister(t *testing.T) {
 	require.Equal(t, MOVD, actualNode.instruction)
 	require.Equal(t, RegR15, actualNode.srcReg)
 	require.Equal(t, RegR27, actualNode.dstReg)
-	require.Equal(t, operandTypeRegister, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterToRegister, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileMemoryToRegister(t *testing.T) {
@@ -328,8 +324,7 @@ func TestAssemblerImpl_CompileMemoryToRegister(t *testing.T) {
 	require.Equal(t, RegR15, actualNode.srcReg)
 	require.Equal(t, int64(100), actualNode.srcConst)
 	require.Equal(t, RegR27, actualNode.dstReg)
-	require.Equal(t, operandTypeMemory, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesMemoryToRegister, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileRegisterToMemory(t *testing.T) {
@@ -340,8 +335,7 @@ func TestAssemblerImpl_CompileRegisterToMemory(t *testing.T) {
 	require.Equal(t, RegR15, actualNode.srcReg)
 	require.Equal(t, RegR27, actualNode.dstReg)
 	require.Equal(t, int64(100), actualNode.dstConst)
-	require.Equal(t, operandTypeRegister, actualNode.types.src)
-	require.Equal(t, operandTypeMemory, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterToMemory, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileJump(t *testing.T) {
@@ -349,8 +343,7 @@ func TestAssemblerImpl_CompileJump(t *testing.T) {
 	a.CompileJump(B)
 	actualNode := a.current
 	require.Equal(t, B, actualNode.instruction)
-	require.Equal(t, operandTypeNone, actualNode.types.src)
-	require.Equal(t, operandTypeBranch, actualNode.types.dst)
+	require.Equal(t, operandTypesNoneToBranch, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileJumpToRegister(t *testing.T) {
@@ -359,8 +352,7 @@ func TestAssemblerImpl_CompileJumpToRegister(t *testing.T) {
 	actualNode := a.current
 	require.Equal(t, BCONDNE, actualNode.instruction)
 	require.Equal(t, RegR27, actualNode.dstReg)
-	require.Equal(t, operandTypeNone, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesNoneToRegister, actualNode.types)
 }
 
 func TestAssemblerImpl_CompileReadInstructionAddress(t *testing.T) {
@@ -369,8 +361,7 @@ func TestAssemblerImpl_CompileReadInstructionAddress(t *testing.T) {
 	actualNode := a.current
 	require.Equal(t, ADR, actualNode.instruction)
 	require.Equal(t, RegR10, actualNode.dstReg)
-	require.Equal(t, operandTypeMemory, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesMemoryToRegister, actualNode.types)
 	require.Equal(t, RET, actualNode.readInstructionAddressBeforeTargetInstruction)
 }
 
@@ -382,8 +373,7 @@ func Test_CompileMemoryWithRegisterOffsetToRegister(t *testing.T) {
 	require.Equal(t, RegR27, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.srcReg2)
 	require.Equal(t, RegR0, actualNode.dstReg)
-	require.Equal(t, operandTypeMemory, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesMemoryToRegister, actualNode.types)
 }
 
 func Test_CompileMemoryWithRegisterOffsetToVectorRegister(t *testing.T) {
@@ -395,8 +385,7 @@ func Test_CompileMemoryWithRegisterOffsetToVectorRegister(t *testing.T) {
 	require.Equal(t, RegR10, actualNode.srcReg2)
 	require.Equal(t, RegV31, actualNode.dstReg)
 	require.Equal(t, VectorArrangementS, actualNode.vectorArrangement)
-	require.Equal(t, operandTypeMemory, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesMemoryToVectorRegister, actualNode.types)
 }
 
 func Test_CompileRegisterToMemoryWithRegisterOffset(t *testing.T) {
@@ -407,8 +396,7 @@ func Test_CompileRegisterToMemoryWithRegisterOffset(t *testing.T) {
 	require.Equal(t, RegR27, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.dstReg)
 	require.Equal(t, RegR0, actualNode.dstReg2)
-	require.Equal(t, operandTypeRegister, actualNode.types.src)
-	require.Equal(t, operandTypeMemory, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterToMemory, actualNode.types)
 }
 
 func Test_CompileVectorRegisterToMemoryWithRegisterOffset(t *testing.T) {
@@ -420,8 +408,7 @@ func Test_CompileVectorRegisterToMemoryWithRegisterOffset(t *testing.T) {
 	require.Equal(t, RegR10, actualNode.dstReg)
 	require.Equal(t, RegR0, actualNode.dstReg2)
 	require.Equal(t, VectorArrangement2D, actualNode.vectorArrangement)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.src)
-	require.Equal(t, operandTypeMemory, actualNode.types.dst)
+	require.Equal(t, operandTypesVectorRegisterToMemory, actualNode.types)
 }
 
 func Test_CompileTwoRegistersToRegister(t *testing.T) {
@@ -432,8 +419,7 @@ func Test_CompileTwoRegistersToRegister(t *testing.T) {
 	require.Equal(t, RegR27, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.srcReg2)
 	require.Equal(t, RegR0, actualNode.dstReg)
-	require.Equal(t, operandTypeTwoRegisters, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesTwoRegistersToRegister, actualNode.types)
 }
 
 func Test_CompileThreeRegistersToRegister(t *testing.T) {
@@ -445,8 +431,7 @@ func Test_CompileThreeRegistersToRegister(t *testing.T) {
 	require.Equal(t, RegR10, actualNode.srcReg2)
 	require.Equal(t, RegR0, actualNode.dstReg)
 	require.Equal(t, RegR28, actualNode.dstReg2)
-	require.Equal(t, operandTypeThreeRegisters, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesThreeRegistersToRegister, actualNode.types)
 }
 
 func Test_CompileTwoRegistersToNone(t *testing.T) {
@@ -456,8 +441,7 @@ func Test_CompileTwoRegistersToNone(t *testing.T) {
 	require.Equal(t, CMP, actualNode.instruction)
 	require.Equal(t, RegR27, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.srcReg2)
-	require.Equal(t, operandTypeTwoRegisters, actualNode.types.src)
-	require.Equal(t, operandTypeNone, actualNode.types.dst)
+	require.Equal(t, operandTypesTwoRegistersToNone, actualNode.types)
 }
 
 func Test_CompileRegisterAndConstToNone(t *testing.T) {
@@ -467,8 +451,7 @@ func Test_CompileRegisterAndConstToNone(t *testing.T) {
 	require.Equal(t, CMP, actualNode.instruction)
 	require.Equal(t, RegR27, actualNode.srcReg)
 	require.Equal(t, int64(10), actualNode.srcConst)
-	require.Equal(t, operandTypeRegisterAndConst, actualNode.types.src)
-	require.Equal(t, operandTypeNone, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterAndConstToNone, actualNode.types)
 }
 
 func Test_CompileRegisterAndConstToRegister(t *testing.T) {
@@ -479,8 +462,7 @@ func Test_CompileRegisterAndConstToRegister(t *testing.T) {
 	require.Equal(t, RegR27, actualNode.srcReg)
 	require.Equal(t, int64(10), actualNode.srcConst)
 	require.Equal(t, RegSP, actualNode.dstReg)
-	require.Equal(t, operandTypeRegisterAndConst, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterAndConstToRegister, actualNode.types)
 }
 
 func Test_CompileLeftShiftedRegisterToRegister(t *testing.T) {
@@ -492,8 +474,7 @@ func Test_CompileLeftShiftedRegisterToRegister(t *testing.T) {
 	require.Equal(t, RegR27, actualNode.srcReg2)
 	require.Equal(t, int64(10), actualNode.srcConst)
 	require.Equal(t, RegR5, actualNode.dstReg)
-	require.Equal(t, operandTypeLeftShiftedRegister, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesLeftShiftedRegisterToRegister, actualNode.types)
 }
 
 func Test_CompileConditionalRegisterSet(t *testing.T) {
@@ -503,8 +484,7 @@ func Test_CompileConditionalRegisterSet(t *testing.T) {
 	require.Equal(t, CSET, actualNode.instruction)
 	require.Equal(t, RegCondNE, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.dstReg)
-	require.Equal(t, operandTypeRegister, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterToRegister, actualNode.types)
 }
 
 func Test_CompileMemoryToVectorRegister(t *testing.T) {
@@ -515,8 +495,7 @@ func Test_CompileMemoryToVectorRegister(t *testing.T) {
 	require.Equal(t, RegR10, actualNode.srcReg)
 	require.Equal(t, int64(10), actualNode.srcConst)
 	require.Equal(t, RegV3, actualNode.dstReg)
-	require.Equal(t, operandTypeMemory, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesMemoryToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 }
 
@@ -528,8 +507,7 @@ func Test_CompileVectorRegisterToMemory(t *testing.T) {
 	require.Equal(t, RegV3, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.dstReg)
 	require.Equal(t, int64(12), actualNode.dstConst)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.src)
-	require.Equal(t, operandTypeMemory, actualNode.types.dst)
+	require.Equal(t, operandTypesVectorRegisterToMemory, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 }
 
@@ -540,8 +518,7 @@ func Test_CompileRegisterToVectorRegister(t *testing.T) {
 	require.Equal(t, VMOV, actualNode.instruction)
 	require.Equal(t, RegV3, actualNode.srcReg)
 	require.Equal(t, RegR10, actualNode.dstReg)
-	require.Equal(t, operandTypeRegister, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesRegisterToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 	require.Equal(t, VectorIndex(10), actualNode.dstVectorIndex)
 }
@@ -553,8 +530,7 @@ func Test_CompileVectorRegisterToRegister(t *testing.T) {
 	require.Equal(t, VMOV, actualNode.instruction)
 	require.Equal(t, RegR10, actualNode.srcReg)
 	require.Equal(t, RegV3, actualNode.dstReg)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesVectorRegisterToRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 	require.Equal(t, VectorIndex(10), actualNode.srcVectorIndex)
 }
@@ -566,8 +542,7 @@ func Test_CompileVectorRegisterToVectorRegister(t *testing.T) {
 	require.Equal(t, VMOV, actualNode.instruction)
 	require.Equal(t, RegV3, actualNode.srcReg)
 	require.Equal(t, RegV10, actualNode.dstReg)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesVectorRegisterToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 	require.Equal(t, VectorIndex(1), actualNode.srcVectorIndex)
 	require.Equal(t, VectorIndex(2), actualNode.dstVectorIndex)
@@ -580,8 +555,7 @@ func Test_CompileVectorRegisterToVectorRegisterWithConst(t *testing.T) {
 	require.Equal(t, VMOV, actualNode.instruction)
 	require.Equal(t, RegV3, actualNode.srcReg)
 	require.Equal(t, RegV10, actualNode.dstReg)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesVectorRegisterToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 	require.Equal(t, int64(1234), actualNode.srcConst)
 }
@@ -594,8 +568,7 @@ func Test_CompileTwoVectorRegistersToVectorRegister(t *testing.T) {
 	require.Equal(t, RegV3, actualNode.srcReg)
 	require.Equal(t, RegV15, actualNode.srcReg2)
 	require.Equal(t, RegV10, actualNode.dstReg)
-	require.Equal(t, operandTypeTwoVectorRegisters, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesTwoVectorRegistersToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 }
 
@@ -608,8 +581,7 @@ func Test_CompileTwoVectorRegistersToVectorRegisterWithConst(t *testing.T) {
 	require.Equal(t, RegV15, actualNode.srcReg2)
 	require.Equal(t, int64(1234), actualNode.srcConst)
 	require.Equal(t, RegV10, actualNode.dstReg)
-	require.Equal(t, operandTypeTwoVectorRegisters, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesTwoVectorRegistersToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement1D, actualNode.vectorArrangement)
 }
 
@@ -621,8 +593,7 @@ func Test_CompileStaticConstToVectorRegister(t *testing.T) {
 	require.Equal(t, VMOV, actualNode.instruction)
 	require.Equal(t, s, actualNode.staticConst)
 	require.Equal(t, RegV3, actualNode.dstReg)
-	require.Equal(t, operandTypeStaticConst, actualNode.types.src)
-	require.Equal(t, operandTypeVectorRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesStaticConstToVectorRegister, actualNode.types)
 	require.Equal(t, VectorArrangement2D, actualNode.vectorArrangement)
 }
 
@@ -634,8 +605,7 @@ func Test_CompileStaticConstToRegister(t *testing.T) {
 	require.Equal(t, ADR, actualNode.instruction)
 	require.Equal(t, s, actualNode.staticConst)
 	require.Equal(t, RegR27, actualNode.dstReg)
-	require.Equal(t, operandTypeMemory, actualNode.types.src)
-	require.Equal(t, operandTypeRegister, actualNode.types.dst)
+	require.Equal(t, operandTypesMemoryToRegister, actualNode.types)
 }
 
 func Test_checkRegisterToRegisterType(t *testing.T) {
@@ -680,7 +650,7 @@ func TestAssemblerImpl_encodeNoneToNone(t *testing.T) {
 	t.Run("error", func(t *testing.T) {
 		a := NewAssembler(asm.NilRegister)
 		err := a.encodeNoneToNone(&nodeImpl{instruction: ADD})
-		require.EqualError(t, err, "ADD is unsupported for from:none,to:none type")
+		require.EqualError(t, err, "ADD is unsupported for NoneToNone type")
 	})
 	t.Run("NOP", func(t *testing.T) {
 		a := NewAssembler(asm.NilRegister)
@@ -2467,7 +2437,7 @@ func TestAssemblerImpl_EncodeLeftShiftedRegisterToRegister(t *testing.T) {
 					instruction: SUB, types: operandTypesLeftShiftedRegisterToRegister,
 					srcReg: RegR0, srcReg2: RegR0, dstReg: RegR0,
 				},
-				expErr: "SUB is unsupported for from:left-shifted-register,to:register type",
+				expErr: "SUB is unsupported for LeftShiftedRegisterToRegister type",
 			},
 			{
 				n: &nodeImpl{
@@ -2642,7 +2612,7 @@ func TestAssemblerImpl_encodeTwoRegistersToNone(t *testing.T) {
 					instruction: SUB, types: operandTypesTwoRegistersToNone,
 					srcReg: RegR0, srcReg2: RegR0, dstReg: RegR0,
 				},
-				expErr: "SUB is unsupported for from:two-registers,to:none type",
+				expErr: "SUB is unsupported for TwoRegistersToNone type",
 			},
 			{
 				n: &nodeImpl{
diff --git a/internal/asm/arm64/impl_2_test.go b/internal/asm/arm64/impl_2_test.go
index 698766b7..5f973091 100644
--- a/internal/asm/arm64/impl_2_test.go
+++ b/internal/asm/arm64/impl_2_test.go
@@ -19,7 +19,7 @@ func TestAssemblerImpl_EncodeConstToRegister(t *testing.T) {
 					instruction: ADR, types: operandTypesConstToRegister,
 					srcReg: RegR0, srcReg2: RegR0, dstReg: RegR0,
 				},
-				expErr: "ADR is unsupported for from:const,to:register type",
+				expErr: "ADR is unsupported for ConstToRegister type",
 			},
 			{
 				n:      &nodeImpl{instruction: LSR, types: operandTypesConstToRegister, dstReg: RegR0},
diff --git a/internal/asm/arm64/impl_3_test.go b/internal/asm/arm64/impl_3_test.go
index dbe981a5..4a1d398c 100644
--- a/internal/asm/arm64/impl_3_test.go
+++ b/internal/asm/arm64/impl_3_test.go
@@ -19,7 +19,7 @@ func TestAssemblerImpl_EncodeTwoRegistersToRegister(t *testing.T) {
 					instruction: ADR, types: operandTypesTwoRegistersToRegister,
 					srcReg: RegR0, srcReg2: RegR0, dstReg: RegR0,
 				},
-				expErr: "ADR is unsupported for from:two-registers,to:register type",
+				expErr: "ADR is unsupported for TwoRegistersToRegister type",
 			},
 		}
 
@@ -618,7 +618,7 @@ func TestAssemblerImpl_EncodeRegisterAndConstToNone(t *testing.T) {
 					instruction: ADR, types: operandTypesRegisterAndConstToNone,
 					srcReg: RegR0, srcReg2: RegR0, dstReg: RegR0,
 				},
-				expErr: "ADR is unsupported for from:register-and-const,to:none type",
+				expErr: "ADR is unsupported for RegisterAndConstToNone type",
 			},
 			{
 				n: &nodeImpl{
@@ -691,7 +691,7 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) {
 					instruction: ADR, types: operandTypesRegisterToRegister,
 					srcReg: RegR0, srcReg2: RegR0, dstReg: RegR0,
 				},
-				expErr: "ADR is unsupported for from:register,to:register type",
+				expErr: "ADR is unsupported for RegisterToRegister type",
 			},
 		}
 
diff --git a/internal/asm/arm64/impl_4_test.go b/internal/asm/arm64/impl_4_test.go
index b26faf0a..27de54e6 100644
--- a/internal/asm/arm64/impl_4_test.go
+++ b/internal/asm/arm64/impl_4_test.go
@@ -18,7 +18,7 @@ func TestAssemblerImpl_encodeJumpToRegister(t *testing.T) {
 		}{
 			{
 				n:      &nodeImpl{instruction: ADD, types: operandTypesNoneToRegister},
-				expErr: "ADD is unsupported for from:none,to:register type",
+				expErr: "ADD is unsupported for NoneToRegister type",
 			},
 			{
 				n:      &nodeImpl{instruction: RET, dstReg: asm.NilRegister},
@@ -103,7 +103,7 @@ func TestAssemblerImpl_EncodeMemoryToRegister(t *testing.T) {
 		}{
 			{
 				n:      &nodeImpl{instruction: SUB, types: operandTypesMemoryToRegister},
-				expErr: "SUB is unsupported for from:memory,to:register type",
+				expErr: "SUB is unsupported for MemoryToRegister type",
 			},
 		}
 
diff --git a/internal/asm/arm64/impl_5_test.go b/internal/asm/arm64/impl_5_test.go
index 50e0e824..741085d8 100644
--- a/internal/asm/arm64/impl_5_test.go
+++ b/internal/asm/arm64/impl_5_test.go
@@ -15,7 +15,7 @@ func TestAssemblerImpl_EncodeRegisterToMemory(t *testing.T) {
 		}{
 			{
 				n:      &nodeImpl{instruction: ADR, types: operandTypesRegisterToMemory},
-				expErr: "ADR is unsupported for from:register,to:memory type",
+				expErr: "ADR is unsupported for RegisterToMemory type",
 			},
 		}
 
diff --git a/internal/asm/arm64/impl_6_test.go b/internal/asm/arm64/impl_6_test.go
index 6d07f1b0..4c678755 100644
--- a/internal/asm/arm64/impl_6_test.go
+++ b/internal/asm/arm64/impl_6_test.go
@@ -55,7 +55,7 @@ func TestAssemblerImpl_EncodeRelativeJump(t *testing.T) {
 			},
 			{
 				n:      &nodeImpl{instruction: SUB, types: operandTypesNoneToBranch},
-				expErr: "SUB is unsupported for from:none,to:branch type",
+				expErr: "SUB is unsupported for NoneToBranch type",
 			},
 			{
 				n:      &nodeImpl{instruction: B, types: operandTypesNoneToBranch, offsetInBinaryField: 0, jumpTarget: &nodeImpl{offsetInBinaryField: uint64(maxSignedInt26)*4 + 4}},