| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:typed_data'; |
| |
| import 'package:cfg/ir/constant_value.dart'; |
| import 'package:cfg/utils/misc.dart'; |
| import 'package:native_compiler/back_end/arm64/assembler.dart'; |
| import 'package:native_compiler/back_end/assembler.dart'; |
| import 'package:native_compiler/back_end/code.dart'; |
| import 'package:native_compiler/back_end/object_pool.dart'; |
| import 'package:native_compiler/runtime/object_layout.dart'; |
| import 'package:native_compiler/runtime/vm_defs.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'disassembler.dart' show Disassembler; |
| |
| void main() { |
| final vmOffsets = Arm64VMOffsets(); |
| final objectLayout = ObjectLayout( |
| vmOffsets, |
| wordSize: wordSize, |
| compressedWordSize: wordSize, |
| ); |
| final objectPoolBase = vmOffsets.ObjectPool_elementOffset(0); |
| late Arm64Assembler asm; |
| |
| setUp(() { |
| asm = Arm64Assembler(vmOffsets, null, objectLayout); |
| }); |
| |
| void expectDisassembly(String expected) { |
| final bytes = asm.bytes; |
| final actual = Disassembler.decodeInstructions( |
| Uint32List.view(bytes.buffer, 0, bytes.length >> 2), |
| ); |
| expect(actual, equals(expected)); |
| } |
| |
| void expectThrows(void Function() actual) { |
| expect(actual, throwsA(anything)); |
| } |
| |
| test('simple', () { |
| asm.add(R0, ZR, ZR); |
| asm.add(R0, R0, Immediate(42)); |
| asm.ret(); |
| expectDisassembly( |
| 'add r0, zr, zr\n' |
| 'add r0, r0, #0x2a\n' |
| 'ret\n', |
| ); |
| }); |
| |
| // Negative test cases require enabled assertions. |
| test('check assertions enabled', () { |
| expectThrows(() { |
| assert(false); |
| }); |
| }); |
| |
| group('macro-instruction', () { |
| test('address', () { |
| asm.ldr(R0, asm.address(R0, -256)); |
| asm.str(R1, asm.address(R0, 0x7ff8)); |
| asm.ldr(R2, asm.address(R0, -257)); |
| asm.str(R3, asm.address(R0, 257)); |
| asm.ldr(R4, asm.address(R0, 0x8002, .u16), .u16); |
| expectDisassembly( |
| 'ldr r0, [r0, #-256]\n' |
| 'str r1, [r0, #32760]\n' |
| 'sub r17, r0, #0x200\n' |
| 'ldr r2, [r17, #255]\n' |
| 'add r17, r0, #0x100\n' |
| 'str r3, [r17, #1]\n' |
| 'add r17, r0, #0x8000\n' |
| 'ldrh r4, [r17, #2]\n', |
| ); |
| }); |
| test('pairAddress', () { |
| asm.ldp(R1, R2, asm.pairAddress(R0, -0x200)); |
| asm.stp(R1, R2, asm.pairAddress(R0, 0x1f8)); |
| asm.ldp(R1, R2, asm.pairAddress(R0, 3)); |
| asm.stp(R1, R2, asm.pairAddress(R0, -1)); |
| asm.ldp(R1, R2, asm.pairAddress(R0, -0x208)); |
| asm.stp(R1, R2, asm.pairAddress(R0, 0x204, .s32), .s32); |
| expectDisassembly( |
| 'ldp r1, r2, [r0, #-512]\n' |
| 'stp r1, r2, [r0, #504]\n' |
| 'add r17, r0, #0x3\n' |
| 'ldp r1, r2, [r17, #0]\n' |
| 'sub r17, r0, #0x1\n' |
| 'stp r1, r2, [r17, #0]\n' |
| 'sub r17, r0, #0x400\n' |
| 'ldp r1, r2, [r17, #504]\n' |
| 'add r17, r0, #0x200\n' |
| 'stpw r1, r2, [r17, #4]\n', |
| ); |
| }); |
| test('enterDartFrame', () { |
| asm.enterDartFrame(); |
| expectDisassembly( |
| 'stp fp, lr, [sp, #-16]!\n' |
| 'mov fp, sp\n' |
| 'add pp, pp, #0x1\n' |
| 'stp pp, code, [sp, #-16]!\n' |
| 'ldr pp, [code, #${vmOffsets.Code_object_pool_offset - heapObjectTag}]\n' |
| 'sub pp, pp, #0x1\n', |
| ); |
| }); |
| test('leaveDartFrame', () { |
| asm.leaveDartFrame(); |
| expectDisassembly( |
| 'ldr pp, [fp, #-16]\n' |
| 'sub pp, pp, #0x1\n' |
| 'mov sp, fp\n' |
| 'ldp fp, lr, [sp], #16 !\n', |
| ); |
| }); |
| test('push', () { |
| asm.push(R0); |
| asm.push(ZR); |
| expectThrows(() { |
| asm.push(SP); |
| }); |
| expectDisassembly( |
| 'str r0, [sp, #-8]!\n' |
| 'str zr, [sp, #-8]!\n', |
| ); |
| }); |
| test('pop', () { |
| asm.pop(R1); |
| asm.pop(ZR); |
| expectThrows(() { |
| asm.push(SP); |
| }); |
| expectDisassembly( |
| 'ldr r1, [sp], #8 !\n' |
| 'ldr zr, [sp], #8 !\n', |
| ); |
| }); |
| test('pushPair', () { |
| asm.pushPair(R0, R1); |
| asm.pushPair(FP, LR); |
| asm.pushPair(R0, R0); |
| asm.pushPair(R2, ZR); |
| expectThrows(() { |
| asm.pushPair(stackPointerReg, R0); |
| }); |
| expectThrows(() { |
| asm.pushPair(R0, SP); |
| }); |
| expectDisassembly( |
| 'stp r0, r1, [sp, #-16]!\n' |
| 'stp fp, lr, [sp, #-16]!\n' |
| 'stp r0, r0, [sp, #-16]!\n' |
| 'stp r2, zr, [sp, #-16]!\n', |
| ); |
| }); |
| test('popPair', () { |
| asm.popPair(R0, R1); |
| asm.popPair(FP, LR); |
| asm.popPair(ZR, R2); |
| expectThrows(() { |
| asm.popPair(R0, stackPointerReg); |
| }); |
| expectThrows(() { |
| asm.popPair(SP, R1); |
| }); |
| expectDisassembly( |
| 'ldp r0, r1, [sp], #16 !\n' |
| 'ldp fp, lr, [sp], #16 !\n' |
| 'ldp zr, r2, [sp], #16 !\n', |
| ); |
| }); |
| test('loadFromPool', () { |
| asm.loadFromPool(R0, ConstantValue.fromString('abc') as Object); |
| asm.loadFromPool(R1, ConstantValue.fromString('def') as Object); |
| asm.loadFromPool(R2, ConstantValue.fromString('abc') as Object); |
| expectDisassembly( |
| 'ldr r0, [pp, #${objectPoolBase}]\n' |
| 'ldr r1, [pp, #${objectPoolBase + 8}]\n' |
| 'ldr r2, [pp, #${objectPoolBase}]\n', |
| ); |
| }); |
| test('loadFromPool - large offset', () { |
| final expected = StringBuffer(); |
| for (var offs = objectPoolBase; offs < 32768; offs += 8) { |
| asm.loadFromPool(R0, ConstantValue.fromString('$offs') as Object); |
| expected.write('ldr r0, [pp, #$offs]\n'); |
| } |
| asm.loadFromPool(R0, ConstantValue.fromString('oops1') as Object); |
| expected.write( |
| 'add r17, pp, #0x8000\n' |
| 'ldr r0, [r17]\n', |
| ); |
| asm.loadFromPool(R0, ConstantValue.fromString('oops2') as Object); |
| expected.write( |
| 'add r17, pp, #0x8000\n' |
| 'ldr r0, [r17, #8]\n', |
| ); |
| expectDisassembly(expected.toString()); |
| }); |
| test('loadConstant', () { |
| asm.loadConstant(R0, ConstantValue.fromString('abc')); |
| asm.loadConstant(R1, ConstantValue(UnboxedIntConstant(42))); |
| asm.loadConstant(R2, ConstantValue.fromInt(42)); |
| asm.loadConstant(R3, ConstantValue.fromInt(0x7fffffff_ffffffff)); |
| asm.loadConstant(R4, ConstantValue.fromNull()); |
| asm.loadConstant(R5, ConstantValue.fromBool(true)); |
| asm.loadConstant(R6, ConstantValue.fromBool(false)); |
| expectDisassembly( |
| 'ldr r0, [pp, #${objectPoolBase}]\n' |
| 'movz r1, #0x2a\n' |
| 'movz r2, #0x54\n' |
| 'ldr r3, [pp, #${objectPoolBase + 8}]\n' |
| 'mov r4, null\n' |
| 'add r5, null, #0x${trueOffsetFromNull(wordSize).toRadixString(16)}\n' |
| 'add r6, null, #0x${falseOffsetFromNull(wordSize).toRadixString(16)}\n', |
| ); |
| }); |
| test('loadImmediate', () { |
| asm.loadImmediate(R0, 0); |
| asm.loadImmediate(R1, 1); |
| asm.loadImmediate(R2, -1); |
| asm.loadImmediate(R3, 42); |
| asm.loadImmediate(R0, -42); |
| asm.loadImmediate(LR, 0xaabb); |
| asm.loadImmediate(LR, 0xaabbccdd); |
| asm.loadImmediate(R1, 0xffffffff_ffffaabb); |
| asm.loadImmediate(R1, 0xffffffff_aabbccdd); |
| asm.loadImmediate(R2, 0x11223344_55667788); |
| asm.loadImmediate(R2, 0xaabbccdd_eeff0011); |
| asm.loadImmediate(R3, 0xfefefefe); |
| asm.loadImmediate(R4, 0xfefefefe_fefefefe); |
| asm.loadImmediate(R5, 0xff00ff00_ff00ff00); |
| expectDisassembly( |
| 'movz r0, #0x0\n' |
| 'movz r1, #0x1\n' |
| 'movn r2, #0x0\n' |
| 'movz r3, #0x2a\n' |
| 'movn r0, #0x29\n' |
| 'movz lr, #0xaabb\n' |
| 'movz lr, #0xccdd\n' |
| 'movk lr, #0xaabb lsl 16\n' |
| 'movn r1, #0x5544\n' |
| 'movn r1, #0x3322\n' |
| 'movk r1, #0xaabb lsl 16\n' |
| 'movz r2, #0x7788\n' |
| 'movk r2, #0x5566 lsl 16\n' |
| 'movk r2, #0x3344 lsl 32\n' |
| 'movk r2, #0x1122 lsl 48\n' |
| 'movz r2, #0x11\n' |
| 'movk r2, #0xeeff lsl 16\n' |
| 'movk r2, #0xccdd lsl 32\n' |
| 'movk r2, #0xaabb lsl 48\n' |
| 'movz r3, #0xfefe\n' |
| 'movk r3, #0xfefe lsl 16\n' |
| 'mov r4, 0xfefefefefefefefe\n' |
| 'mov r5, 0xff00ff00ff00ff00\n', |
| ); |
| }); |
| test('loadDoubleImmediate', () { |
| asm.loadDoubleImmediate(V0, 1.0); |
| asm.loadDoubleImmediate(V31, -3.5); |
| asm.loadDoubleImmediate(V1, 0.0); |
| asm.loadDoubleImmediate(V2, 0.123456789); |
| asm.loadDoubleImmediate(V3, double.nan); |
| expectDisassembly( |
| 'fmovd v0, 1.0\n' |
| 'fmovd v31, -3.5\n' |
| 'fmovdr v1, zr\n' |
| 'fldrd v2, [pp, #${objectPoolBase}]\n' |
| 'fldrd v3, [pp, #${objectPoolBase + 8}]\n', |
| ); |
| }); |
| test('addImmediate', () { |
| asm.addImmediate(R0, R0, 0); |
| asm.addImmediate(R1, R2, 0); |
| asm.addImmediate(R1, R2, 0, .s32); |
| asm.addImmediate(R1, R2, 0xabc); |
| asm.addImmediate(R1, R2, -0xabc); |
| asm.addImmediate(R1, R2, 0xabc000); |
| asm.addImmediate(R1, R2, -0xabc000); |
| asm.addImmediate(R1, R2, 0x11223344_55667788); |
| asm.addImmediate(SP, FP, 0x11223344_55667788); |
| expectDisassembly( |
| 'mov r1, r2\n' |
| 'movw r1, r2\n' |
| 'add r1, r2, #0xabc\n' |
| 'sub r1, r2, #0xabc\n' |
| 'add r1, r2, #0xabc000\n' |
| 'sub r1, r2, #0xabc000\n' |
| 'movz r17, #0x7788\n' |
| 'movk r17, #0x5566 lsl 16\n' |
| 'movk r17, #0x3344 lsl 32\n' |
| 'movk r17, #0x1122 lsl 48\n' |
| 'add r1, r2, r17\n' |
| 'movz r17, #0x7788\n' |
| 'movk r17, #0x5566 lsl 16\n' |
| 'movk r17, #0x3344 lsl 32\n' |
| 'movk r17, #0x1122 lsl 48\n' |
| 'add csp, fp, r17 uxtx 0\n', |
| ); |
| }); |
| test('subImmediate', () { |
| asm.subImmediate(R3, R3, 0); |
| asm.subImmediate(SP, FP, 0); |
| asm.subImmediate(R1, R2, 0, .u32); |
| asm.subImmediate(R1, R2, 0xabc); |
| asm.subImmediate(R1, R2, -0xabc); |
| asm.subImmediate(R1, R2, 0xabc000); |
| asm.subImmediate(R1, R2, -0xabc000); |
| asm.subImmediate(R1, R2, 0x11223344_55667788); |
| asm.subImmediate(SP, FP, 0x11223344_55667788); |
| expectDisassembly( |
| 'mov csp, fp\n' |
| 'movw r1, r2\n' |
| 'sub r1, r2, #0xabc\n' |
| 'add r1, r2, #0xabc\n' |
| 'sub r1, r2, #0xabc000\n' |
| 'add r1, r2, #0xabc000\n' |
| 'movz r17, #0x7788\n' |
| 'movk r17, #0x5566 lsl 16\n' |
| 'movk r17, #0x3344 lsl 32\n' |
| 'movk r17, #0x1122 lsl 48\n' |
| 'sub r1, r2, r17\n' |
| 'movz r17, #0x7788\n' |
| 'movk r17, #0x5566 lsl 16\n' |
| 'movk r17, #0x3344 lsl 32\n' |
| 'movk r17, #0x1122 lsl 48\n' |
| 'sub csp, fp, r17 uxtx 0\n', |
| ); |
| }); |
| test('cmpImmediate', () { |
| asm.cmpImmediate(R1, 0); |
| asm.cmpImmediate(R3, 0xabc); |
| asm.cmpImmediate(R4, 0xabc000); |
| asm.cmpImmediate(R0, 0x1001); |
| asm.cmpImmediate(R6, 0x11223344_55667788); |
| asm.cmpImmediate(FP, -1); |
| asm.cmpImmediate(R4, -0xabc); |
| asm.cmpImmediate(R5, -0xabc000); |
| asm.cmpImmediate(R7, -0x1001); |
| expectDisassembly( |
| 'cmp r1, #0x0\n' |
| 'cmp r3, #0xabc\n' |
| 'cmp r4, #0xabc000\n' |
| 'movz r17, #0x1001\n' |
| 'cmp r0, r17\n' |
| 'movz r17, #0x7788\n' |
| 'movk r17, #0x5566 lsl 16\n' |
| 'movk r17, #0x3344 lsl 32\n' |
| 'movk r17, #0x1122 lsl 48\n' |
| 'cmp r6, r17\n' |
| 'cmn fp, #0x1\n' |
| 'cmn r4, #0xabc\n' |
| 'cmn r5, #0xabc000\n' |
| 'movn r17, #0x1000\n' |
| 'cmp r7, r17\n', |
| ); |
| }); |
| test('andImmediate', () { |
| asm.andImmediate(R1, R2, 0); |
| asm.andImmediate(R1, R2, 0, .u32); |
| asm.andImmediate(R1, R2, -1); |
| asm.andImmediate(R1, R2, -1, .u32); |
| asm.andImmediate(R1, R2, 0xff); |
| asm.andImmediate(R1, R2, 0x11223344_55667788); |
| expectDisassembly( |
| 'movz r1, #0x0\n' |
| 'movz r1, #0x0\n' |
| 'mov r1, r2\n' |
| 'movw r1, r2\n' |
| 'and r1, r2, 0xff\n' |
| 'movz r17, #0x7788\n' |
| 'movk r17, #0x5566 lsl 16\n' |
| 'movk r17, #0x3344 lsl 32\n' |
| 'movk r17, #0x1122 lsl 48\n' |
| 'and r1, r2, r17\n', |
| ); |
| }); |
| test('callRuntime', () { |
| asm.callRuntime(RuntimeEntry.AllocateObject, 2); |
| expectDisassembly( |
| 'ldr r5, [thr, #${vmOffsets.Thread_runtime_entry_offset(RuntimeEntry.AllocateObject, wordSize)}]\n' |
| 'movz r4, #0x2\n' |
| 'ldr lr, [thr, #${vmOffsets.Thread_call_to_runtime_entry_point_offset}]\n' |
| 'blr lr\n', |
| ); |
| }); |
| test('callStub', () { |
| final stub = Code('<stub>', null, Uint8List(0), ObjectPool()); |
| asm.callStub(stub); |
| expectDisassembly( |
| 'ldr code, [pp, #${objectPoolBase}]\n' |
| 'ldr lr, [code, #${vmOffsets.Code_entry_point_offset.first - heapObjectTag}]\n' |
| 'blr lr\n', |
| ); |
| }); |
| test('breakpoint', () { |
| asm.breakpoint(); |
| expectDisassembly('brk #0x0\n'); |
| }); |
| test('inlineAllocation - object size 16', () { |
| final slowPath = Label(); |
| asm.inlineAllocation( |
| R0, |
| R1, |
| R2, |
| R3, |
| 16, |
| slowPath, |
| initializeFields: true, |
| ); |
| asm.bind(slowPath); |
| expectDisassembly( |
| 'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'add r3, r0, #0x10\n' |
| 'cmp r2, r3\n' |
| 'bls +20\n' |
| 'str r3, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'str r1, [r0]\n' |
| 'str null, [r0, #8]\n' |
| 'add r0, r0, #0x1\n', |
| ); |
| }); |
| test('inlineAllocation - object size 16, no field initialization', () { |
| final slowPath = Label(); |
| asm.inlineAllocation( |
| R0, |
| R1, |
| R2, |
| R3, |
| 16, |
| slowPath, |
| initializeFields: false, |
| ); |
| asm.bind(slowPath); |
| expectDisassembly( |
| 'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'add r3, r0, #0x10\n' |
| 'cmp r2, r3\n' |
| 'bls +16\n' |
| 'str r3, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'str r1, [r0]\n' |
| 'add r0, r0, #0x1\n', |
| ); |
| }); |
| test('inlineAllocation - object size 32', () { |
| final slowPath = Label(); |
| asm.inlineAllocation( |
| R0, |
| R1, |
| R2, |
| R3, |
| 32, |
| slowPath, |
| initializeFields: true, |
| ); |
| asm.bind(slowPath); |
| expectDisassembly( |
| 'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'add r3, r0, #0x20\n' |
| 'cmp r2, r3\n' |
| 'bls +24\n' |
| 'str r3, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'str r1, [r0]\n' |
| 'stp null, null, [r0, #8]\n' |
| 'str null, [r0, #24]\n' |
| 'add r0, r0, #0x1\n', |
| ); |
| }); |
| test('inlineAllocation - object size 160', () { |
| final slowPath = Label(); |
| asm.inlineAllocation( |
| R0, |
| R1, |
| R2, |
| R3, |
| 160, |
| slowPath, |
| initializeFields: true, |
| ); |
| asm.bind(slowPath); |
| expectDisassembly( |
| 'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'add r3, r0, #0xa0\n' |
| 'cmp r2, r3\n' |
| 'bls +32\n' |
| 'str r3, [thr, #${vmOffsets.Thread_top_offset}]\n' |
| 'str r1, [r0]\n' |
| 'add r2, r0, #0x8\n' |
| 'stp null, null, [r2], #16 !\n' |
| 'cmp r2, r3\n' |
| 'bcc -8\n' |
| 'add r0, r0, #0x1\n', |
| ); |
| }); |
| test('loadClassId', () { |
| asm.loadClassId(R0, R0); |
| asm.loadClassId(R1, R5); |
| final lowBit = vmOffsets.UntaggedObject_kClassIdTagPos; |
| final highBit = |
| vmOffsets.UntaggedObject_kClassIdTagPos + |
| vmOffsets.UntaggedObject_kClassIdTagSize - |
| 1; |
| expectDisassembly( |
| 'ldr r0, [r0, #${vmOffsets.Object_tags_offset - heapObjectTag}]\n' |
| 'ubfm r0, r0, #$lowBit, #$highBit\n' |
| 'ldr r1, [r5, #${vmOffsets.Object_tags_offset - heapObjectTag}]\n' |
| 'ubfm r1, r1, #$lowBit, #$highBit\n', |
| ); |
| }); |
| }); |
| |
| group('instruction', () { |
| test('add', () { |
| asm.add(R0, R0, R1); |
| asm.add(R0, R0, ShiftedRegOperand(R1, .LSL, 1)); |
| asm.add(R2, ZR, ShiftedRegOperand(R1, .LSR, 8)); |
| asm.add(R0, R0, ShiftedRegOperand(R1, .ASR, 63)); |
| asm.add(R1, ZR, ShiftedRegOperand(R1, .LSL, 3), .s32); |
| asm.addw(R0, R0, ShiftedRegOperand(R1, .ASR, 3)); |
| asm.add(R0, R0, ExtRegOperand(R1, .SXTW, 0)); |
| asm.add(R0, R1, ExtRegOperand(R1, .UXTX, 3)); |
| asm.add(R0, SP, ExtRegOperand(R1, .UXTX, 0)); |
| asm.add(R0, R1, Immediate(42)); |
| asm.add(SP, SP, Immediate(16)); |
| expectDisassembly( |
| 'add r0, r0, r1\n' |
| 'add r0, r0, r1 lsl #1\n' |
| 'add r2, zr, r1 lsr #8\n' |
| 'add r0, r0, r1 asr #63\n' |
| 'addw r1, zr, r1 lsl #3\n' |
| 'addw r0, r0, r1 asr #3\n' |
| 'add r0, r0, r1 sxtw\n' |
| 'add r0, r1, r1 uxtx 3\n' |
| 'add r0, csp, r1 uxtx 0\n' |
| 'add r0, r1, #0x2a\n' |
| 'add csp, csp, #0x10\n', |
| ); |
| expectThrows(() { |
| asm.add(R0, R0, SP); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ShiftedRegOperand(SP, .LSL, 1)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ShiftedRegOperand(R1, .LSL, 64)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ShiftedRegOperand(R1, .LSL, 32), .s32); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ShiftedRegOperand(R1, .LSL, -1)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ExtRegOperand(SP, .UXTX, 0)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ExtRegOperand(R1, .UXTX, 5)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, ExtRegOperand(R1, .UXTX, -1)); |
| }); |
| expectThrows(() { |
| asm.add(R0, ZR, ExtRegOperand(R1, .UXTX, 0)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, Immediate(4097)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, Immediate(-1)); |
| }); |
| expectThrows(() { |
| asm.add(R0, R0, R1, .s8); |
| }); |
| }); |
| |
| test('adds', () { |
| asm.adds(tempReg, R2, R1); |
| asm.adds(tempReg, R2, R1, .s32); |
| expectDisassembly( |
| 'adds tmp, r2, r1\n' |
| 'addws tmp, r2, r1\n', |
| ); |
| expectThrows(() { |
| asm.adds(R0, R0, SP); |
| }); |
| expectThrows(() { |
| asm.adds(SP, R0, ExtRegOperand(R1, .UXTX, 0)); |
| }); |
| }); |
| |
| test('sub', () { |
| asm.sub(R3, R0, R1); |
| asm.sub(R3, ZR, R0); |
| asm.sub(R1, ZR, ShiftedRegOperand(R1, .LSL, 1), .s32); |
| asm.sub(R0, R0, ExtRegOperand(R1, .UXTB, 0)); |
| asm.subw(R0, R1, Immediate(42)); |
| asm.sub(SP, SP, Immediate(16)); |
| expectDisassembly( |
| 'sub r3, r0, r1\n' |
| 'neg r3, r0\n' |
| 'negw r1, r1 lsl #1\n' |
| 'sub r0, r0, r1 uxtb\n' |
| 'subw r0, r1, #0x2a\n' |
| 'sub csp, csp, #0x10\n', |
| ); |
| }); |
| |
| test('subs', () { |
| asm.subs(tempReg, R2, R0); |
| asm.subs(R1, R1, R1, .s32); |
| expectDisassembly( |
| 'subs tmp, r2, r0\n' |
| 'subws r1, r1, r1\n', |
| ); |
| }); |
| |
| test('cmp', () { |
| asm.cmp(R0, ExtRegOperand(R0, .SXTW, 0)); |
| expectDisassembly('cmp r0, r0 sxtw\n'); |
| }); |
| |
| test('cmn', () { |
| asm.cmn(SP, Immediate(32)); |
| expectDisassembly('cmn csp, #0x20\n'); |
| }); |
| |
| test('adc', () { |
| asm.adc(R1, R2, R3); |
| asm.adc(tempReg, ZR, R0, .s32); |
| expectDisassembly( |
| 'adc r1, r2, r3\n' |
| 'adcw tmp, zr, r0\n', |
| ); |
| expectThrows(() { |
| asm.adds(R0, R0, SP); |
| }); |
| expectThrows(() { |
| asm.adds(R0, R0, R1, .u16); |
| }); |
| }); |
| |
| test('adcs', () { |
| asm.adcs(tempReg, R2, R0); |
| asm.adcsw(R0, ZR, R1); |
| expectDisassembly( |
| 'adcs tmp, r2, r0\n' |
| 'adcws r0, zr, r1\n', |
| ); |
| }); |
| |
| test('sbc', () { |
| asm.sbc(R0, R1, R2); |
| asm.sbc(tempReg, R0, ZR, .s32); |
| expectDisassembly( |
| 'sbc r0, r1, r2\n' |
| 'sbcw tmp, r0, zr\n', |
| ); |
| }); |
| |
| test('sbcs', () { |
| asm.sbcs(tempReg, R0, R0); |
| asm.sbcs(R1, ZR, R0, .s32); |
| expectDisassembly( |
| 'sbcs tmp, r0, r0\n' |
| 'sbcws r1, zr, r0\n', |
| ); |
| }); |
| |
| test('madd', () { |
| asm.madd(R0, R1, R2, R3); |
| asm.madd(R0, R0, R0, R0, .s32); |
| expectDisassembly( |
| 'madd r0, r1, r2, r3\n' |
| 'maddw r0, r0, r0, r0\n', |
| ); |
| expectThrows(() { |
| asm.madd(SP, R1, R2, R3); |
| }); |
| expectThrows(() { |
| asm.madd(R0, SP, R2, R3); |
| }); |
| expectThrows(() { |
| asm.madd(R0, R1, SP, R3); |
| }); |
| expectThrows(() { |
| asm.madd(R0, R1, R2, SP); |
| }); |
| }); |
| |
| test('mul', () { |
| asm.mul(R1, R2, R3); |
| asm.mul(R0, R0, R1, .u32); |
| expectDisassembly( |
| 'mul r1, r2, r3\n' |
| 'mulw r0, r0, r1\n', |
| ); |
| expectThrows(() { |
| asm.mul(SP, R2, R3); |
| }); |
| expectThrows(() { |
| asm.mul(R1, SP, R3); |
| }); |
| expectThrows(() { |
| asm.mul(R1, R2, SP); |
| }); |
| }); |
| |
| test('csel', () { |
| asm.csel(R0, R1, R2, .greater); |
| asm.csel(R1, ZR, R0, .unsignedLessOrEqual); |
| asm.csel(R1, R1, R3, .notEqual, .s32); |
| expectDisassembly( |
| 'csel r0, r1, r2, gt\n' |
| 'csel r1, zr, r0, ls\n' |
| 'cselw r1, r1, r3, ne\n', |
| ); |
| expectThrows(() { |
| asm.csel(SP, R1, R2, .greater); |
| }); |
| expectThrows(() { |
| asm.csel(R0, SP, R2, .greater); |
| }); |
| expectThrows(() { |
| asm.csel(R0, R1, SP, .greater); |
| }); |
| }); |
| |
| test('csinc', () { |
| asm.csinc(R0, R1, R2, .less); |
| asm.csinc(R1, ZR, R0, .noOverflow); |
| asm.csinc(R1, R1, R3, .unsignedGreater, .s32); |
| expectDisassembly( |
| 'csinc r0, r1, r2, lt\n' |
| 'csinc r1, zr, r0, vc\n' |
| 'csincw r1, r1, r3, hi\n', |
| ); |
| expectThrows(() { |
| asm.csinc(SP, R1, R2, .less); |
| }); |
| expectThrows(() { |
| asm.csinc(R0, SP, R2, .less); |
| }); |
| expectThrows(() { |
| asm.csinc(R0, R1, SP, .less); |
| }); |
| }); |
| |
| test('csinv', () { |
| asm.csinv(R5, R6, R7, .negative); |
| asm.csinv(R1, ZR, R0, .equal); |
| asm.csinv(R1, R1, R3, .lessOrEqual, .s32); |
| expectDisassembly( |
| 'csinv r5, r6, r7, mi\n' |
| 'csinv r1, zr, r0, eq\n' |
| 'csinvw r1, r1, r3, le\n', |
| ); |
| expectThrows(() { |
| asm.csinv(SP, R1, R2, .negative); |
| }); |
| expectThrows(() { |
| asm.csinv(R0, SP, R2, .negative); |
| }); |
| expectThrows(() { |
| asm.csinv(R0, R1, SP, .negative); |
| }); |
| }); |
| |
| test('csneg', () { |
| asm.csneg(R5, R6, R7, .greaterOrEqual); |
| asm.csneg(R1, ZR, R0, .unsignedLess); |
| asm.csneg(R1, R1, R3, .positiveOrZero, .s32); |
| expectDisassembly( |
| 'csneg r5, r6, r7, ge\n' |
| 'csneg r1, zr, r0, cc\n' |
| 'csnegw r1, r1, r3, pl\n', |
| ); |
| expectThrows(() { |
| asm.csneg(SP, R1, R2, .negative); |
| }); |
| expectThrows(() { |
| asm.csneg(R0, SP, R2, .negative); |
| }); |
| expectThrows(() { |
| asm.csneg(R0, R1, SP, .negative); |
| }); |
| }); |
| |
| test('ubfx', () { |
| asm.ubfx(R0, R1, 4, 8); |
| expectDisassembly('ubfm r0, r1, #4, #11\n'); |
| expectThrows(() { |
| asm.ubfx(R0, R1, -1, 8); |
| }); |
| expectThrows(() { |
| asm.ubfx(R0, R1, 62, 3); |
| }); |
| }); |
| |
| test('sbfx', () { |
| asm.sbfx(R0, R1, 4, 8); |
| expectDisassembly('sbfm r0, r1, #4, #11\n'); |
| expectThrows(() { |
| asm.sbfx(R0, R1, 0, -1); |
| }); |
| }); |
| |
| test('bfi', () { |
| asm.bfi(R0, R1, 12, 5); |
| expectDisassembly('bfm r0, r1, #52, #4\n'); |
| expectThrows(() { |
| asm.bfi(R0, R1, 12, 65); |
| }); |
| }); |
| |
| test('ubfiz', () { |
| asm.ubfiz(R0, R1, 1, 30); |
| asm.ubfiz(R0, R1, 0, 32); |
| expectDisassembly( |
| 'ubfm r0, r1, #63, #29\n' |
| 'ubfm r0, r1, #0, #31\n', |
| ); |
| expectThrows(() { |
| asm.ubfiz(R0, R1, 3, 34, .s32); |
| }); |
| }); |
| |
| test('bfxil', () { |
| asm.bfxil(R0, R1, 4, 8); |
| expectDisassembly('bfm r0, r1, #4, #11\n'); |
| expectThrows(() { |
| asm.bfxil(R0, SP, 4, 8); |
| }); |
| }); |
| |
| test('sbfiz', () { |
| asm.sbfiz(R0, R1, 4, 12); |
| expectDisassembly('sbfm r0, r1, #60, #11\n'); |
| }); |
| |
| test('sxtb', () { |
| asm.sxtb(R1, R2); |
| expectDisassembly('sxtb r1, r2\n'); |
| expectThrows(() { |
| asm.sxtb(SP, R2); |
| }); |
| }); |
| |
| test('sxth', () { |
| asm.sxth(R1, R2); |
| expectDisassembly('sxth r1, r2\n'); |
| expectThrows(() { |
| asm.sxtb(R1, SP); |
| }); |
| }); |
| |
| test('sxtw', () { |
| asm.sxtw(R1, R2); |
| expectDisassembly('sxtw r1, r2\n'); |
| }); |
| |
| test('uxtb', () { |
| asm.uxtb(R1, R2); |
| expectDisassembly('uxtb r1, r2\n'); |
| }); |
| |
| test('uxth', () { |
| asm.uxth(R1, R2); |
| expectDisassembly('uxth r1, r2\n'); |
| }); |
| |
| test('asr', () { |
| asm.asr(R0, R1, 0); |
| asm.asr(R0, R1, 0, .s32); |
| asm.asr(R1, R2, 1); |
| asm.asr(R1, R2, 1, .s32); |
| asm.asr(R4, R2, 31, .s32); |
| asm.asr(R4, R2, 63); |
| expectDisassembly( |
| 'mov r0, r1\n' |
| 'movw r0, r1\n' |
| 'asr r1, r2, #1\n' |
| 'asrw r1, r2, #1\n' |
| 'asrw r4, r2, #31\n' |
| 'asr r4, r2, #63\n', |
| ); |
| expectThrows(() { |
| asm.asr(R4, R2, -1); |
| }); |
| expectThrows(() { |
| asm.asr(R4, R2, 64); |
| }); |
| expectThrows(() { |
| asm.asr(R4, R2, 32, .s32); |
| }); |
| expectThrows(() { |
| asm.asr(SP, R2, 1); |
| }); |
| expectThrows(() { |
| asm.asr(R4, SP, 1); |
| }); |
| }); |
| |
| test('lsl', () { |
| asm.lsl(R0, R1, 0); |
| asm.lsl(R0, R1, 0, .s32); |
| asm.lsl(R1, R2, 1); |
| asm.lsl(R1, R2, 1, .s32); |
| asm.lsl(R4, R2, 31, .s32); |
| asm.lsl(R4, R2, 63); |
| expectDisassembly( |
| 'mov r0, r1\n' |
| 'movw r0, r1\n' |
| 'lsl r1, r2, #1\n' |
| 'lslw r1, r2, #1\n' |
| 'lslw r4, r2, #31\n' |
| 'lsl r4, r2, #63\n', |
| ); |
| expectThrows(() { |
| asm.lsl(R4, R2, -1); |
| }); |
| expectThrows(() { |
| asm.lsl(R4, R2, 64); |
| }); |
| expectThrows(() { |
| asm.lsl(R4, R2, 32, .s32); |
| }); |
| expectThrows(() { |
| asm.lsl(SP, R2, 1); |
| }); |
| expectThrows(() { |
| asm.lsl(R4, SP, 1); |
| }); |
| }); |
| |
| test('lsr', () { |
| asm.lsr(R0, R1, 0); |
| asm.lsr(R0, R1, 0, .s32); |
| asm.lsr(R1, R2, 1); |
| asm.lsr(R1, R2, 1, .s32); |
| asm.lsr(R4, R2, 31, .s32); |
| asm.lsr(R4, R2, 63); |
| expectDisassembly( |
| 'mov r0, r1\n' |
| 'movw r0, r1\n' |
| 'lsr r1, r2, #1\n' |
| 'lsrw r1, r2, #1\n' |
| 'lsrw r4, r2, #31\n' |
| 'lsr r4, r2, #63\n', |
| ); |
| expectThrows(() { |
| asm.lsr(R4, R2, -1); |
| }); |
| expectThrows(() { |
| asm.lsr(R4, R2, 64); |
| }); |
| expectThrows(() { |
| asm.lsr(R4, R2, 32, .s32); |
| }); |
| expectThrows(() { |
| asm.lsr(SP, R2, 1); |
| }); |
| expectThrows(() { |
| asm.lsr(R4, SP, 1); |
| }); |
| }); |
| |
| test('asrv', () { |
| asm.asrv(R0, R1, R2); |
| asm.asrv(R3, R4, R5, .s32); |
| expectDisassembly( |
| 'asr r0, r1, r2\n' |
| 'asrw r3, r4, r5\n', |
| ); |
| expectThrows(() { |
| asm.asrv(SP, R1, R2); |
| }); |
| expectThrows(() { |
| asm.asrv(R0, SP, R2); |
| }); |
| expectThrows(() { |
| asm.asrv(R0, R1, SP); |
| }); |
| }); |
| |
| test('lslv', () { |
| asm.lslv(R0, R1, R2); |
| asm.lslv(R3, R4, R5, .s32); |
| expectDisassembly( |
| 'lsl r0, r1, r2\n' |
| 'lslw r3, r4, r5\n', |
| ); |
| expectThrows(() { |
| asm.lslv(SP, R1, R2); |
| }); |
| expectThrows(() { |
| asm.lslv(R0, SP, R2); |
| }); |
| expectThrows(() { |
| asm.lslv(R0, R1, SP); |
| }); |
| }); |
| |
| test('lsrv', () { |
| asm.lsrv(R0, R1, R2); |
| asm.lsrv(R3, R4, R5, .s32); |
| expectDisassembly( |
| 'lsr r0, r1, r2\n' |
| 'lsrw r3, r4, r5\n', |
| ); |
| expectThrows(() { |
| asm.lsrv(SP, R1, R2); |
| }); |
| expectThrows(() { |
| asm.lsrv(R0, SP, R2); |
| }); |
| expectThrows(() { |
| asm.lsrv(R0, R1, SP); |
| }); |
| }); |
| |
| test('and', () { |
| asm.and(R0, R1, R2); |
| asm.and(R0, R0, Immediate(-512)); |
| asm.andw(R0, R0, Immediate(-512)); |
| asm.and(SP, R0, Immediate(-16)); |
| expectDisassembly( |
| 'and r0, r1, r2\n' |
| 'and r0, r0, 0xfffffffffffffe00\n' |
| 'andw r0, r0, 0xfffffe00\n' |
| 'and csp, r0, 0xfffffffffffffff0\n', |
| ); |
| expectThrows(() { |
| asm.and(R0, R1, Immediate(0)); |
| }); |
| expectThrows(() { |
| asm.and(R0, R1, Immediate(0x1101111)); |
| }); |
| }); |
| |
| test('ands', () { |
| asm.ands(ZR, R1, R2); |
| asm.ands(R0, R0, Immediate(-512)); |
| asm.ands(R0, R0, Immediate(-512), .s32); |
| expectDisassembly( |
| 'tst r1, r2\n' |
| 'ands r0, r0, 0xfffffffffffffe00\n' |
| 'andws r0, r0, 0xfffffe00\n', |
| ); |
| expectThrows(() { |
| asm.ands(SP, R0, Immediate(-16)); |
| }); |
| }); |
| |
| test('eor', () { |
| asm.eor(R0, R1, R2); |
| asm.eor(R0, R0, Immediate(0xff00)); |
| asm.eor(R0, R0, Immediate(0xff00), .s32); |
| expectDisassembly( |
| 'eor r0, r1, r2\n' |
| 'eor r0, r0, 0xff00\n' |
| 'eorw r0, r0, 0xff00\n', |
| ); |
| }); |
| |
| test('orr', () { |
| asm.orr(R0, R1, R2); |
| asm.orr(R0, R0, Immediate(0xff00)); |
| expectDisassembly( |
| 'orr r0, r1, r2\n' |
| 'orr r0, r0, 0xff00\n', |
| ); |
| }); |
| |
| test('bic', () { |
| asm.bic(R0, R1, ZR); |
| asm.bic(R0, R1, ShiftedRegOperand(R1, .LSR, 3)); |
| asm.bicw(R0, R1, ZR); |
| expectDisassembly( |
| 'bic r0, r1, zr\n' |
| 'bic r0, r1, r1 lsr #3\n' |
| 'bicw r0, r1, zr\n', |
| ); |
| }); |
| |
| test('bics', () { |
| asm.bics(R0, ZR, R0); |
| asm.bics(R0, R1, ShiftedRegOperand(R0, .ASR, 16)); |
| expectDisassembly( |
| 'bics r0, zr, r0\n' |
| 'bics r0, r1, r0 asr #16\n', |
| ); |
| }); |
| |
| test('eon', () { |
| asm.eon(R0, R0, R1); |
| asm.eon(R0, R0, ShiftedRegOperand(R1, .LSL, 15)); |
| asm.eon(R0, R0, ShiftedRegOperand(R1, .LSL, 15), .s32); |
| expectDisassembly( |
| 'eon r0, r0, r1\n' |
| 'eon r0, r0, r1 lsl #15\n' |
| 'eonw r0, r0, r1 lsl #15\n', |
| ); |
| }); |
| |
| test('orn', () { |
| asm.orn(R3, R2, R1); |
| asm.ornw(R3, R2, R1); |
| asm.orn(R3, R2, ShiftedRegOperand(R4, .ROR, 2)); |
| expectDisassembly( |
| 'orn r3, r2, r1\n' |
| 'ornw r3, r2, r1\n' |
| 'orn r3, r2, r4 ror #2\n', |
| ); |
| }); |
| |
| test('mov', () { |
| asm.mov(R1, R0); |
| asm.mov(R0, R1, .s32); |
| asm.mov(R0, ZR); |
| expectDisassembly( |
| 'mov r1, r0\n' |
| 'movw r0, r1\n' |
| 'mov r0, zr\n', |
| ); |
| expectThrows(() { |
| asm.mov(R1, R0, .s8); |
| }); |
| }); |
| |
| test('movz', () { |
| asm.movz(R0, 42, 0); |
| asm.movz(R0, 42, 16); |
| asm.movz(R0, 42, 32); |
| asm.movz(R0, 42, 48); |
| asm.movz(R0, 0x8000, 0); |
| expectDisassembly( |
| 'movz r0, #0x2a\n' |
| 'movz r0, #0x2a lsl 16\n' |
| 'movz r0, #0x2a lsl 32\n' |
| 'movz r0, #0x2a lsl 48\n' |
| 'movz r0, #0x8000\n', |
| ); |
| expectThrows(() { |
| asm.movz(R0, 42, 15); |
| }); |
| expectThrows(() { |
| asm.movz(R0, -1, 0); |
| }); |
| expectThrows(() { |
| asm.movz(R0, 0x10000, 0); |
| }); |
| expectThrows(() { |
| asm.movz(SP, 42, 0); |
| }); |
| }); |
| |
| test('movn', () { |
| asm.movn(R0, 42, 0); |
| asm.movn(R0, 42, 16); |
| asm.movn(R0, 42, 32); |
| asm.movn(R0, 42, 48); |
| expectDisassembly( |
| 'movn r0, #0x2a\n' |
| 'movn r0, #0x2a lsl 16\n' |
| 'movn r0, #0x2a lsl 32\n' |
| 'movn r0, #0x2a lsl 48\n', |
| ); |
| }); |
| |
| test('movk', () { |
| asm.movk(R0, 42, 0); |
| asm.movk(R0, 42, 16); |
| asm.movk(R0, 42, 32); |
| asm.movk(R0, 42, 48); |
| expectDisassembly( |
| 'movk r0, #0x2a\n' |
| 'movk r0, #0x2a lsl 16\n' |
| 'movk r0, #0x2a lsl 32\n' |
| 'movk r0, #0x2a lsl 48\n', |
| ); |
| }); |
| |
| test('ldr', () { |
| asm.ldr(R0, RegOffsetAddress(R1, 7)); |
| asm.ldr(R0, RegOffsetAddress(R1, 7), .u32); |
| asm.ldr(R0, RegOffsetAddress(R1, 7), .s32); |
| asm.ldr(R0, RegOffsetAddress(R1, 7), .u16); |
| asm.ldr(R0, RegOffsetAddress(R1, 7), .s16); |
| asm.ldr(R0, RegOffsetAddress(R1, 7), .u8); |
| asm.ldr(R0, RegOffsetAddress(R1, 7), .s8); |
| asm.ldr(R0, RegOffsetAddress(SP, 4096)); |
| asm.ldr(R0, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true)); |
| asm.ldr(R0, WritebackRegOffsetAddress(R1, -8, isPostIndexed: false)); |
| expectDisassembly( |
| 'ldr r0, [r1, #7]\n' |
| 'ldrw r0, [r1, #7]\n' |
| 'ldrsw r0, [r1, #7]\n' |
| 'ldrh r0, [r1, #7]\n' |
| 'ldrsh r0, [r1, #7]\n' |
| 'ldrb r0, [r1, #7]\n' |
| 'ldrsb r0, [r1, #7]\n' |
| 'ldr r0, [csp, #4096]\n' |
| 'ldr r0, [r1], #16 !\n' |
| 'ldr r0, [r1, #-8]!\n', |
| ); |
| expectThrows(() { |
| asm.ldr(R0, RegOffsetAddress(R1, 32768)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, RegOffsetAddress(R1, 4097)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, RegOffsetAddress(R1, -512)); |
| }); |
| expectThrows(() { |
| asm.ldr(SP, RegOffsetAddress(R1, 8)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, WritebackRegOffsetAddress(R1, 512, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, WritebackRegOffsetAddress(R1, -513, isPostIndexed: false)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: false)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.ldr(R0, RegOffsetAddress(R1, 0), .simd128); |
| }); |
| }); |
| |
| test('str', () { |
| asm.str(ZR, RegOffsetAddress(SP, -8)); |
| asm.str(R1, RegOffsetAddress(R0, 7)); |
| asm.str(R0, RegOffsetAddress(R1, 7), .s32); |
| asm.str(R0, RegOffsetAddress(R1, 7), .s16); |
| asm.str(R0, RegOffsetAddress(R1, 7), .s8); |
| asm.str(R0, WritebackRegOffsetAddress(R1, -32, isPostIndexed: true)); |
| asm.str(R0, WritebackRegOffsetAddress(R1, 8, isPostIndexed: false)); |
| expectDisassembly( |
| 'str zr, [csp, #-8]\n' |
| 'str r1, [r0, #7]\n' |
| 'strw r0, [r1, #7]\n' |
| 'strh r0, [r1, #7]\n' |
| 'strb r0, [r1, #7]\n' |
| 'str r0, [r1], #-32 !\n' |
| 'str r0, [r1, #8]!\n', |
| ); |
| expectThrows(() { |
| asm.str(R0, RegOffsetAddress(R1, 32768)); |
| }); |
| expectThrows(() { |
| asm.str(R0, RegOffsetAddress(R1, 4097)); |
| }); |
| expectThrows(() { |
| asm.str(R0, RegOffsetAddress(R1, -512)); |
| }); |
| expectThrows(() { |
| asm.str(SP, RegOffsetAddress(R1, 8)); |
| }); |
| expectThrows(() { |
| asm.str(R0, RegOffsetAddress(ZR, 8)); |
| }); |
| expectThrows(() { |
| asm.str(R0, WritebackRegOffsetAddress(R1, 512, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.str(R0, WritebackRegOffsetAddress(R1, -513, isPostIndexed: false)); |
| }); |
| expectThrows(() { |
| asm.str(R0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: false)); |
| }); |
| expectThrows(() { |
| asm.str(R0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.str(R0, RegOffsetAddress(R1, 0), .simd128); |
| }); |
| }); |
| |
| test('fldr', () { |
| asm.fldr(V0, RegOffsetAddress(R1, 7)); |
| asm.fldr(V1, RegOffsetAddress(R1, 7), .s16); |
| asm.fldr(V2, RegOffsetAddress(R1, 32), .s32); |
| asm.fldr(V3, RegOffsetAddress(R1, -5), .s64); |
| asm.fldr(V4, RegOffsetAddress(SP, 32768), .simd128); |
| asm.fldr(V5, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true)); |
| asm.fldr( |
| V6, |
| WritebackRegOffsetAddress(R1, -8, isPostIndexed: false), |
| .simd128, |
| ); |
| asm.fldr(V0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true), .s32); |
| expectDisassembly( |
| 'fldrd v0, [r1, #7]\n' |
| 'fldrh v1, [r1, #7]\n' |
| 'fldrs v2, [r1, #32]\n' |
| 'fldrd v3, [r1, #-5]\n' |
| 'fldrq v4, [csp, #32768]\n' |
| 'fldrd v5, [r1], #16 !\n' |
| 'fldrq v6, [r1, #-8]!\n' |
| 'fldrs v0, [r0], #8 !\n', |
| ); |
| expectThrows(() { |
| asm.fldr(V0, RegOffsetAddress(R1, 32768)); |
| }); |
| expectThrows(() { |
| asm.fldr(V0, RegOffsetAddress(R1, 4097)); |
| }); |
| expectThrows(() { |
| asm.fldr(V0, RegOffsetAddress(R1, -512)); |
| }); |
| expectThrows(() { |
| asm.fldr(V0, WritebackRegOffsetAddress(R1, 512, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.fldr(V0, WritebackRegOffsetAddress(R1, -513, isPostIndexed: false)); |
| }); |
| }); |
| |
| test('fstr', () { |
| asm.fstr(V0, RegOffsetAddress(R1, 7)); |
| asm.fstr(V1, RegOffsetAddress(R1, 7), .s16); |
| asm.fstr(V2, RegOffsetAddress(R1, 32), .s32); |
| asm.fstr(V3, RegOffsetAddress(R1, -5), .s64); |
| asm.fstr(V4, RegOffsetAddress(SP, 32768), .simd128); |
| asm.fstr(V5, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true)); |
| asm.fstr( |
| V6, |
| WritebackRegOffsetAddress(R1, -8, isPostIndexed: false), |
| .simd128, |
| ); |
| asm.fstr(V0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true), .s32); |
| expectDisassembly( |
| 'fstrd v0, [r1, #7]\n' |
| 'fstrh v1, [r1, #7]\n' |
| 'fstrs v2, [r1, #32]\n' |
| 'fstrd v3, [r1, #-5]\n' |
| 'fstrq v4, [csp, #32768]\n' |
| 'fstrd v5, [r1], #16 !\n' |
| 'fstrq v6, [r1, #-8]!\n' |
| 'fstrs v0, [r0], #8 !\n', |
| ); |
| expectThrows(() { |
| asm.fstr(V0, RegOffsetAddress(R1, 32768)); |
| }); |
| expectThrows(() { |
| asm.fstr(V0, RegOffsetAddress(R1, 4097)); |
| }); |
| expectThrows(() { |
| asm.fstr(V0, RegOffsetAddress(R1, -512)); |
| }); |
| expectThrows(() { |
| asm.fstr(V0, WritebackRegOffsetAddress(R1, 512, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.fstr(V0, WritebackRegOffsetAddress(R1, -513, isPostIndexed: false)); |
| }); |
| }); |
| |
| test('ldp', () { |
| asm.ldp(R0, R1, RegOffsetAddress(R2, -512)); |
| asm.ldp(R0, R1, RegOffsetAddress(R2, 16), .u32); |
| asm.ldp(R0, R1, RegOffsetAddress(R2, 252), .s32); |
| asm.ldp(R3, R1, RegOffsetAddress(SP, 256)); |
| asm.ldp(R2, R0, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true)); |
| asm.ldp(R0, R4, WritebackRegOffsetAddress(R1, -8, isPostIndexed: false)); |
| expectDisassembly( |
| 'ldp r0, r1, [r2, #-512]\n' |
| 'ldpw r0, r1, [r2, #16]\n' |
| 'ldpsw r0, r1, [r2, #252]\n' |
| 'ldp r3, r1, [csp, #256]\n' |
| 'ldp r2, r0, [r1], #16 !\n' |
| 'ldp r0, r4, [r1, #-8]!\n', |
| ); |
| expectThrows(() { |
| asm.ldp(R0, R1, RegOffsetAddress(R2, 1)); |
| }); |
| expectThrows(() { |
| asm.ldp(R0, R1, RegOffsetAddress(R2, 512)); |
| }); |
| expectThrows(() { |
| asm.ldp(R0, R1, RegOffsetAddress(R2, -520)); |
| }); |
| expectThrows(() { |
| asm.ldp(R0, R1, RegOffsetAddress(R2, 256), .s32); |
| }); |
| expectThrows(() { |
| asm.ldp(R0, R1, RegOffsetAddress(R2, -260), .u32); |
| }); |
| expectThrows(() { |
| asm.ldp(R0, R1, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.ldp( |
| R0, |
| R1, |
| WritebackRegOffsetAddress(R1, -8, isPostIndexed: false), |
| ); |
| }); |
| expectThrows(() { |
| asm.ldp( |
| R0, |
| R1, |
| WritebackRegOffsetAddress(R2, 512, isPostIndexed: true), |
| ); |
| }); |
| expectThrows(() { |
| asm.ldp( |
| R0, |
| R1, |
| WritebackRegOffsetAddress(R2, -520, isPostIndexed: false), |
| ); |
| }); |
| }); |
| |
| test('stp', () { |
| asm.stp(R0, R1, RegOffsetAddress(R2, 16)); |
| asm.stp(R0, R1, RegOffsetAddress(R2, 4), .u32); |
| asm.stp(R0, R1, RegOffsetAddress(R2, -256), .s32); |
| asm.stp(R3, R1, RegOffsetAddress(SP, 256)); |
| asm.stp(R2, R0, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true)); |
| asm.stp(R0, R4, WritebackRegOffsetAddress(R1, -8, isPostIndexed: false)); |
| expectDisassembly( |
| 'stp r0, r1, [r2, #16]\n' |
| 'stpw r0, r1, [r2, #4]\n' |
| 'stpw r0, r1, [r2, #-256]\n' |
| 'stp r3, r1, [csp, #256]\n' |
| 'stp r2, r0, [r1], #16 !\n' |
| 'stp r0, r4, [r1, #-8]!\n', |
| ); |
| expectThrows(() { |
| asm.stp(R0, R1, RegOffsetAddress(R2, 4)); |
| }); |
| expectThrows(() { |
| asm.stp(R0, R1, RegOffsetAddress(R2, 512)); |
| }); |
| expectThrows(() { |
| asm.stp(R0, R1, RegOffsetAddress(R2, -520)); |
| }); |
| expectThrows(() { |
| asm.stp(R0, R1, WritebackRegOffsetAddress(ZR, 8, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.stp(R0, R1, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true)); |
| }); |
| expectThrows(() { |
| asm.stp( |
| R0, |
| R1, |
| WritebackRegOffsetAddress(R1, -8, isPostIndexed: false), |
| ); |
| }); |
| expectThrows(() { |
| asm.stp( |
| R0, |
| R1, |
| WritebackRegOffsetAddress(R2, 512, isPostIndexed: true), |
| ); |
| }); |
| expectThrows(() { |
| asm.stp( |
| R0, |
| R1, |
| WritebackRegOffsetAddress(R2, -520, isPostIndexed: false), |
| ); |
| }); |
| }); |
| |
| test('nop', () { |
| asm.nop(); |
| expectDisassembly('nop\n'); |
| }); |
| |
| test('b', () { |
| final loop = Label(); |
| final done = Label(); |
| asm.movz(R0, 1); |
| asm.bind(loop); |
| asm.cmp(R0, Immediate(10)); |
| asm.b(done, .greaterOrEqual); |
| asm.add(R0, R0, Immediate(1)); |
| asm.b(loop); |
| asm.bind(done); |
| expectDisassembly( |
| 'movz r0, #0x1\n' |
| 'cmp r0, #0xa\n' |
| 'bge +12\n' |
| 'add r0, r0, #0x1\n' |
| 'b -12\n', |
| ); |
| }); |
| |
| test('b - large offset', () { |
| final target0 = Label(); |
| final target1 = Label(); |
| final target2 = Label(); |
| asm.bind(target0); |
| asm.nop(); |
| asm.b(target2); |
| asm.b(target1); |
| for (var i = 0; i < (1 << 25) - 3; ++i) { |
| asm.nop(); |
| } |
| asm.b(target0); |
| asm.bind(target1); |
| // TODO: support far jumps |
| expectThrows(() { |
| asm.b(target0); |
| }); |
| expectThrows(() { |
| asm.bind(target2); |
| }); |
| }); |
| |
| test('b.cond - large offset', () { |
| final target0 = Label(); |
| final target1 = Label(); |
| final target2 = Label(); |
| asm.bind(target0); |
| asm.nop(); |
| asm.b(target2, .less); |
| asm.b(target1, .equal); |
| for (var i = 0; i < (1 << 18) - 3; ++i) { |
| asm.nop(); |
| } |
| asm.b(target0, .notZero); |
| asm.bind(target1); |
| // TODO: support far jumps |
| expectThrows(() { |
| asm.b(target0, .notZero); |
| }); |
| expectThrows(() { |
| asm.bind(target2); |
| }); |
| }); |
| |
| test('cbz/cbnz', () { |
| final loop = Label(); |
| final done = Label(); |
| asm.movz(R0, 10); |
| asm.bind(loop); |
| asm.sub(R0, R0, Immediate(1)); |
| asm.cbz(R0, done); |
| asm.cbnz(R0, loop); |
| asm.bind(done); |
| expectDisassembly( |
| 'movz r0, #0xa\n' |
| 'sub r0, r0, #0x1\n' |
| 'cbz r0, +8\n' |
| 'cbnz r0, -8\n', |
| ); |
| }); |
| |
| test('cbz/cbnz - large offset', () { |
| final target0 = Label(); |
| final target1 = Label(); |
| final target2 = Label(); |
| asm.bind(target0); |
| asm.nop(); |
| asm.cbz(R0, target2); |
| asm.cbz(R1, target1); |
| for (var i = 0; i < (1 << 18) - 3; ++i) { |
| asm.nop(); |
| } |
| asm.cbnz(R0, target0); |
| asm.bind(target1); |
| // TODO: support far jumps |
| expectThrows(() { |
| asm.cbnz(R0, target0); |
| }); |
| expectThrows(() { |
| asm.bind(target2); |
| }); |
| }); |
| |
| test('tbz/tbnz', () { |
| final loop = Label(); |
| final isEven = Label(); |
| asm.movz(R0, 0xff); |
| asm.bind(loop); |
| asm.sub(R0, R0, Immediate(1)); |
| asm.tbz(R0, 0, isEven); |
| asm.sub(R0, R0, Immediate(1)); |
| asm.bind(isEven); |
| asm.tbnz(R0, 40, loop); |
| expectDisassembly( |
| 'movz r0, #0xff\n' |
| 'sub r0, r0, #0x1\n' |
| 'tbzw r0, #0, +8\n' |
| 'sub r0, r0, #0x1\n' |
| 'tbnz r0, #40, -12\n', |
| ); |
| }); |
| |
| test('tbz/tbnz - large offset', () { |
| final target0 = Label(); |
| final target1 = Label(); |
| final target2 = Label(); |
| asm.bind(target0); |
| asm.nop(); |
| asm.tbnz(R0, 31, target2); |
| asm.tbz(R1, 63, target1); |
| for (var i = 0; i < (1 << 13) - 3; ++i) { |
| asm.nop(); |
| } |
| asm.tbz(R1, 0, target0); |
| asm.bind(target1); |
| // TODO: support far jumps |
| expectThrows(() { |
| asm.tbz(R1, 0, target0); |
| }); |
| expectThrows(() { |
| asm.bind(target2); |
| }); |
| }); |
| |
| test('br', () { |
| asm.br(R4); |
| expectDisassembly('br r4\n'); |
| }); |
| |
| test('blr', () { |
| asm.blr(R0); |
| expectDisassembly('blr r0\n'); |
| }); |
| |
| test('ret', () { |
| asm.ret(); |
| asm.ret(R1); |
| expectDisassembly( |
| 'ret\n' |
| 'ret r1\n', |
| ); |
| }); |
| test('brk', () { |
| asm.brk(0x1234); |
| expectDisassembly('brk #0x1234\n'); |
| expectThrows(() { |
| asm.brk(-1); |
| }); |
| expectThrows(() { |
| asm.brk(0x10000); |
| }); |
| }); |
| test('scvtf', () { |
| asm.scvtf(V0, R0); |
| asm.scvtf(V1, R2, .s32); |
| asm.scvtf(V3, ZR); |
| expectDisassembly( |
| 'scvtfd v0, r0\n' |
| 'scvtfdw v1, r2\n' |
| 'scvtfd v3, zr\n', |
| ); |
| expectThrows(() { |
| asm.scvtf(V0, R0, .s16); |
| }); |
| expectThrows(() { |
| asm.scvtf(V0, R0, .s64, .s8); |
| }); |
| expectThrows(() { |
| asm.scvtf(V0, R0, .s64, .simd128); |
| }); |
| expectThrows(() { |
| asm.scvtf(V0, SP); |
| }); |
| }); |
| |
| test('fmov', () { |
| asm.fmov(V0, R0); |
| asm.fmov(V2, ZR); |
| asm.fmov(V3, R2, .s32); |
| asm.fmov(V0, Immediate(doubleToIntBits(1.0))); |
| asm.fmov(V1, Immediate(doubleToIntBits(2.0))); |
| asm.fmov(V31, Immediate(doubleToIntBits(-0.25))); |
| expectDisassembly( |
| 'fmovdr v0, r0\n' |
| 'fmovdr v2, zr\n' |
| 'fmovsrw v3, r2\n' |
| 'fmovd v0, 1.0\n' |
| 'fmovd v1, 2.0\n' |
| 'fmovd v31, -0.25\n', |
| ); |
| expectThrows(() { |
| asm.fmov(V0, Immediate(doubleToIntBits(0.0))); |
| }); |
| expectThrows(() { |
| asm.fmov(V1, Immediate(doubleToIntBits(1.23456789))); |
| }); |
| }); |
| |
| test('fadd', () { |
| asm.fadd(V0, V1, V2); |
| asm.fadd(V31, V30, V29); |
| // TODO: extend disassembler to handle single and half precision. |
| expectDisassembly( |
| 'faddd v0, v1, v2\n' |
| 'faddd v31, v30, v29\n', |
| ); |
| }); |
| |
| test('fsub', () { |
| asm.fsub(V0, V1, V2); |
| asm.fsub(V31, V30, V29); |
| // TODO: extend disassembler to handle single and half precision. |
| expectDisassembly( |
| 'fsubd v0, v1, v2\n' |
| 'fsubd v31, v30, v29\n', |
| ); |
| }); |
| |
| test('fmul', () { |
| asm.fmul(V0, V1, V2); |
| asm.fmul(V31, V30, V29); |
| // TODO: extend disassembler to handle single and half precision. |
| expectDisassembly( |
| 'fmuld v0, v1, v2\n' |
| 'fmuld v31, v30, v29\n', |
| ); |
| }); |
| |
| test('fdiv', () { |
| asm.fdiv(V0, V1, V2); |
| asm.fdiv(V31, V30, V29); |
| // TODO: extend disassembler to handle single and half precision. |
| expectDisassembly( |
| 'fdivd v0, v1, v2\n' |
| 'fdivd v31, v30, v29\n', |
| ); |
| }); |
| |
| test('fcmp', () { |
| asm.fcmp(V0, V1); |
| asm.fcmp(V31, V30); |
| asm.fcmp(V0, Immediate(0)); |
| asm.fcmp(V5, Immediate(0)); |
| // TODO: extend disassembler to handle single and half precision. |
| expectDisassembly( |
| 'fcmpd v0, v1\n' |
| 'fcmpd v31, v30\n' |
| 'fcmpd v0, #0.0\n' |
| 'fcmpd v5, #0.0\n', |
| ); |
| expectThrows(() { |
| asm.fcmp(V0, Immediate(1)); |
| }); |
| expectThrows(() { |
| asm.fcmp(V0, R0); |
| }); |
| }); |
| }); |
| } |