github.com/AR1011/wazero@v1.0.5/internal/engine/wazevo/backend/isa/arm64/machine_test.go (about) 1 package arm64 2 3 import ( 4 "testing" 5 6 "github.com/AR1011/wazero/internal/engine/wazevo/backend/regalloc" 7 "github.com/AR1011/wazero/internal/engine/wazevo/wazevoapi" 8 "github.com/AR1011/wazero/internal/testing/require" 9 ) 10 11 func TestMachine_insertAtHead(t *testing.T) { 12 t.Run("no head", func(t *testing.T) { 13 m := &machine{} 14 i := &instruction{kind: condBr} 15 m.insertAtPerBlockHead(i) 16 require.Equal(t, i, m.perBlockHead) 17 require.Equal(t, i, m.perBlockEnd) 18 }) 19 t.Run("has head", func(t *testing.T) { 20 prevHead := &instruction{kind: br} 21 m := &machine{perBlockHead: prevHead, perBlockEnd: prevHead} 22 i := &instruction{kind: condBr} 23 m.insertAtPerBlockHead(i) 24 require.Equal(t, i, m.perBlockHead) 25 require.Equal(t, prevHead, m.perBlockEnd) 26 require.Equal(t, nil, prevHead.next) 27 require.Equal(t, i, prevHead.prev) 28 require.Equal(t, prevHead, i.next) 29 require.Equal(t, nil, i.prev) 30 }) 31 } 32 33 func TestMachine_resolveAddressingMode(t *testing.T) { 34 t.Run("imm12/arg", func(t *testing.T) { 35 m := &machine{} 36 i := &instruction{} 37 i.asULoad(operandNR(x17VReg), addressMode{ 38 kind: addressModeKindArgStackSpace, 39 rn: spVReg, 40 imm: 128, 41 }, 64) 42 m.resolveAddressingMode(1024, 0, i) 43 require.Equal(t, addressModeKindRegUnsignedImm12, i.amode.kind) 44 require.Equal(t, int64(128+1024), i.amode.imm) 45 }) 46 t.Run("imm12/result", func(t *testing.T) { 47 m := &machine{} 48 i := &instruction{} 49 i.asULoad(operandNR(x17VReg), addressMode{ 50 kind: addressModeKindResultStackSpace, 51 rn: spVReg, 52 imm: 128, 53 }, 64) 54 m.resolveAddressingMode(0, 256, i) 55 require.Equal(t, addressModeKindRegUnsignedImm12, i.amode.kind) 56 require.Equal(t, int64(128+256), i.amode.imm) 57 }) 58 59 t.Run("tmp reg", func(t *testing.T) { 60 m := &machine{instrPool: wazevoapi.NewPool[instruction](resetInstruction)} 61 root := &instruction{kind: udf} 62 i := &instruction{prev: root} 63 i.asULoad(operandNR(x17VReg), addressMode{ 64 kind: addressModeKindResultStackSpace, 65 rn: spVReg, 66 }, 64) 67 m.resolveAddressingMode(0, 0x40000001, i) 68 69 m.rootInstr = root 70 require.Equal(t, ` 71 udf 72 movz x27, #0x1, lsl 0 73 movk x27, #0x4000, lsl 16 74 ldr x17, [sp, x27] 75 `, m.Format()) 76 }) 77 } 78 79 func TestMachine_clobberedRegSlotSize(t *testing.T) { 80 m := &machine{clobberedRegs: make([]regalloc.VReg, 10)} 81 require.Equal(t, int64(160), m.clobberedRegSlotSize()) 82 } 83 84 func TestMachine_frameSize(t *testing.T) { 85 m := &machine{clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8} 86 require.Equal(t, int64(16*18), m.frameSize()) 87 } 88 89 func TestMachine_requiredStackSize(t *testing.T) { 90 m := &machine{ 91 clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8, 92 maxRequiredStackSizeForCalls: 320, 93 } 94 require.Equal(t, int64(16*18)+int64(320)+32, m.requiredStackSize()) 95 } 96 97 func TestMachine_arg0OffsetFromSP(t *testing.T) { 98 m := &machine{clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8} 99 require.Equal(t, int64(16*18)+32, m.arg0OffsetFromSP()) 100 } 101 102 func TestMachine_ret0OffsetFromSP(t *testing.T) { 103 m := &machine{ 104 clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8, 105 currentABI: &abiImpl{argStackSize: 180}, 106 } 107 require.Equal(t, int64(16*18)+32+180, m.ret0OffsetFromSP()) 108 } 109 110 func TestMachine_getVRegSpillSlotOffsetFromSP(t *testing.T) { 111 m := &machine{spillSlots: make(map[regalloc.VRegID]int64)} 112 id := regalloc.VRegID(1) 113 offset := m.getVRegSpillSlotOffsetFromSP(id, 8) 114 require.Equal(t, int64(16), offset) 115 require.Equal(t, int64(8), m.spillSlotSize) 116 _, ok := m.spillSlots[id] 117 require.True(t, ok) 118 119 id = 100 120 offset = m.getVRegSpillSlotOffsetFromSP(id, 16) 121 require.Equal(t, int64(16+8), offset) 122 require.Equal(t, int64(24), m.spillSlotSize) 123 _, ok = m.spillSlots[id] 124 require.True(t, ok) 125 } 126 127 func TestMachine_insertConditionalJumpTrampoline(t *testing.T) { 128 for _, tc := range []struct { 129 brAtEnd bool 130 expBefore, expAfter string 131 }{ 132 { 133 brAtEnd: true, 134 expBefore: ` 135 L100: 136 b.eq L12345 137 b L888888888 138 L200: 139 exit_sequence x0 140 `, 141 expAfter: ` 142 L100: 143 b.eq L10000000 144 b L888888888 145 L10000000: 146 b L12345 147 L200: 148 exit_sequence x0 149 `, 150 }, 151 { 152 brAtEnd: false, 153 expBefore: ` 154 L100: 155 b.eq L12345 156 udf 157 L200: 158 exit_sequence x0 159 `, 160 expAfter: ` 161 L100: 162 b.eq L10000000 163 udf 164 b L200 165 L10000000: 166 b L12345 167 L200: 168 exit_sequence x0 169 `, 170 }, 171 } { 172 var name string 173 if tc.brAtEnd { 174 name = "brAtEnd" 175 } else { 176 name = "brNotAtEnd" 177 } 178 179 t.Run(name, func(t *testing.T) { 180 m := NewBackend().(*machine) 181 const ( 182 originLabel = 100 183 originLabelNext = 200 184 targetLabel = 12345 185 ) 186 187 cbr := m.allocateInstr() 188 cbr.asCondBr(eq.asCond(), targetLabel, false) 189 190 end := m.allocateInstr() 191 if tc.brAtEnd { 192 end.asBr(888888888) 193 } else { 194 end.asUDF() 195 } 196 197 originalEndNext := m.allocateInstr() 198 originalEndNext.asExitSequence(x0VReg) 199 200 originLabelPos := m.allocateLabelPosition(originLabel) 201 originLabelPos.begin = cbr 202 originLabelPos.end = linkInstr(cbr, end) 203 originNextLabelPos := m.allocateLabelPosition(originLabelNext) 204 originNextLabelPos.begin = originalEndNext 205 linkInstr(originLabelPos.end, originalEndNext) 206 207 m.labelPositions[originLabel] = originLabelPos 208 m.labelPositions[originLabelNext] = originNextLabelPos 209 210 m.rootInstr = cbr 211 require.Equal(t, tc.expBefore, m.Format()) 212 213 m.nextLabel = 9999999 214 m.insertConditionalJumpTrampoline(cbr, originLabelPos, originLabelNext) 215 216 require.Equal(t, tc.expAfter, m.Format()) 217 218 // The original label position should be updated to the unconditional jump to the original target destination. 219 require.Equal(t, "b L12345", originLabelPos.end.String()) 220 }) 221 } 222 }