arm32 codegen

This commit adds arm32 code generation for some IR insts.
Floating-point instructions are not supported, because regalloc
does not allow to represent overlapping register classes,
which are needed by VFP/Neon.

There is also no support for big-endianness, I64 and I128 types.
This commit is contained in:
Jakub Krauz
2020-09-16 12:11:29 +02:00
parent c29f4599ac
commit f6a140a662
26 changed files with 7103 additions and 381 deletions

View File

@@ -0,0 +1,269 @@
test compile
target arm
function %iadd(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = iadd v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: add r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %iadd(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = iadd v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: add r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %iadd(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = iadd v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: add r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sadd_sat(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = sadd_sat v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: qadd r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %isub(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = isub v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sub r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ssub_sat(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = ssub_sat v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: qsub r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ineg(i32) -> i32 {
block0(v0: i32):
v1 = ineg v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: rsb r0, r0, #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %imul(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = imul v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mul r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %umulhi(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = umulhi v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: umull r1, r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %smulhi(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = smulhi v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: smull r1, r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %udiv(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = udiv v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: udiv r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sdiv(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = sdiv v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sdiv r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %iadd_flags(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2, v3 = iadd_ifcout v0, v1
v4, v5 = iadd_ifcarry v1, v2, v3
v6 = iadd_ifcin v1, v4, v5
return v6
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: adds r0, r0, r1
; nextln: adcs r0, r1, r0
; nextln: adc r0, r1, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %isub_flags(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2, v3 = isub_ifbout v0, v1
v4, v5 = isub_ifborrow v1, v2, v3
v6 = isub_ifbin v1, v4, v5
return v6
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: subs r0, r0, r1
; nextln: sbcs r0, r1, r0
; nextln: sbc r0, r1, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %band(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = band v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: and r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %bor(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bor v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: orr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %bxor(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bxor v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: eor r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %bnot(i32) -> i32 {
block0(v0: i32):
v1 = bnot v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mvn r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %band_not(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = band_not v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: bic r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %bor_not(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bor_not v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: orn r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,132 @@
test compile
target arm
function %bitrev_i8(i8) -> i8 {
block0(v0: i8):
v1 = bitrev v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, r0, lsl #24
; nextln: rbit r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %bitrev_i16(i16) -> i16 {
block0(v0: i16):
v1 = bitrev v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, r0, lsl #16
; nextln: rbit r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %bitrev_i32(i32) -> i32 {
block0(v0: i32):
v1 = bitrev v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: rbit r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %clz_i8(i8) -> i8 {
block0(v0: i8):
v1 = clz v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxtb r0, r0
; nextln: clz r0, r0
; nextln: sub r0, r0, #24
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %clz_i16(i16) -> i16 {
block0(v0: i16):
v1 = clz v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxth r0, r0
; nextln: clz r0, r0
; nextln: sub r0, r0, #16
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %clz_i32(i32) -> i32 {
block0(v0: i32):
v1 = clz v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: clz r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ctz_i8(i8) -> i8 {
block0(v0: i8):
v1 = ctz v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxtb r0, r0
; nextln: rbit r0, r0
; nextln: clz r0, r0
; nextln: sub r0, r0, #24
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ctz_i16(i16) -> i16 {
block0(v0: i16):
v1 = ctz v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxth r0, r0
; nextln: rbit r0, r0
; nextln: clz r0, r0
; nextln: sub r0, r0, #16
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ctz_i32(i32) -> i32 {
block0(v0: i32):
v1 = ctz v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: rbit r0, r0
; nextln: clz r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,60 @@
test compile
target arm
function %icmp(i32, i32) -> b1 {
block0(v0: i32, v1: i32):
v2 = icmp eq v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: cmp r0, r1
; nextln: ite eq ; mov r0, #1 ; mov r0, #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ifcmp_trueif(i32, i32) -> b1 {
block0(v0: i32, v1: i32):
v2 = ifcmp v0, v1
v3 = trueif eq v2
return v3
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: cmp r0, r1
; nextln: ite eq ; mov r0, #1 ; mov r0, #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %select(i32, i32, i32) -> i32 {
block0(v0: i32, v1: i32, v2: i32):
v3 = select v0, v1, v2
return v3
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: cmp r0, #0
; nextln: ite ne ; mov r0, r1 ; mov r0, r2
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %selectif(i32, i32, i32, i32) -> i32 {
block0(v0: i32, v1: i32, v2: i32, v3: i32):
v4 = ifcmp v0, v1
v5 = selectif.i32 eq v4, v2, v3
return v5
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: cmp r0, r1
; nextln: ite eq ; mov r0, r2 ; mov r0, r3
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,107 @@
test compile
target arm
function %b1() -> b1 {
block0:
v0 = bconst.b1 true
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %b8() -> b8 {
block0:
v0 = bconst.b8 false
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %b16() -> b16 {
block0:
v0 = bconst.b16 true
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %b32() -> b32 {
block0:
v0 = bconst.b32 false
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %i8() -> i8 {
block0:
v0 = iconst.i8 0xff
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #255
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %i8() -> i16 {
block0:
v0 = iconst.i16 0xffff
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #65535
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %f() -> i32 {
block0:
v0 = iconst.i32 0xffff
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #65535
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %f() -> i32 {
block0:
v0 = iconst.i32 0xffffffff
return v0
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #65535
; nextln: movt r0, #65535
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,131 @@
test compile
target arm
function %brnz(b1) -> i32 {
block0(v0: b1):
brnz v0, block1
jump block2
block1:
v1 = iconst.i32 1
return v1
block2:
v2 = iconst.i32 2
return v2
}
; check: Block 0:
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: and r0, r0, #1
; nextln: cmp r0, #0
; nextln: bne label1 ; b label2
; check: Block 1:
; check: mov r0, #1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
; check: Block 2:
; check: mov r0, #2
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %brz(b1) -> i32 {
block0(v0: b1):
brz v0, block1
jump block2
block1:
v1 = iconst.i32 1
return v1
block2:
v2 = iconst.i32 2
return v2
}
; check: Block 0:
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: and r0, r0, #1
; nextln: cmp r0, #0
; nextln: beq label1 ; b label2
; check: Block 1:
; check: mov r0, #1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
; check: Block 2:
; check: mov r0, #2
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %trap() {
block0:
trap user0
}
; check: udf #0
function %trapif(i32, i32) {
block0(v0: i32, v1: i32):
v2 = ifcmp v0, v1
trapif eq v2, user0
return
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: cmp r0, r1
; nextln: bne 2 ; udf #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %debugtrap() {
block0:
debugtrap
return
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: bkpt #0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %call(i32) -> i32 {
fn0 = %f(i32) -> i32
block0(v0: i32):
v1 = call fn0(v0)
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: ldr r1, [pc, #4] ; b 4 ; data
; nextln: blx r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %call_indirect(i32, i32) -> i32 {
sig0 = (i32) -> i32
block0(v0: i32, v1: i32):
v2 = call_indirect.i32 sig0, v1(v0)
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: blx r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,80 @@
test compile
target arm
function %uextend_i8_i32(i8) -> i32 {
block0(v0: i8):
v1 = uextend.i32 v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxtb r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %uextend_i8_i16(i8) -> i16 {
block0(v0: i8):
v1 = uextend.i16 v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxtb r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %uextend_i16_i32(i16) -> i32 {
block0(v0: i16):
v1 = uextend.i32 v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxth r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sextend_i8_i32(i8) -> i32 {
block0(v0: i8):
v1 = sextend.i32 v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sxtb r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sextend_i8_i16(i8) -> i16 {
block0(v0: i8):
v1 = sextend.i16 v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sxtb r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sextend_i16_i32(i16) -> i32 {
block0(v0: i16):
v1 = sextend.i32 v0
return v1
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sxth r0, r0
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,47 @@
test compile
target arm
function %args(i32) -> i32 {
sig0 = (i32, i32, i32, i32) -> i32
block0(v0: i32):
v1 = iconst.i32 1
v2 = iconst.i32 2
v3 = iconst.i32 3
v4 = iconst.i32 4
v5 = call_indirect.i32 sig0, v0(v1, v2, v3, v4)
return v5
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: push {r4, ip}
; nextln: virtual_sp_offset_adjust 8
; nextln: mov r4, r0
; nextln: mov r0, #1
; nextln: mov r1, #2
; nextln: mov r2, #3
; nextln: mov r3, #4
; nextln: blx r4
; nextln: pop {r4, ip}
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %multi_return() -> i32, i32, i32, i32 {
block0:
v0 = iconst.i32 1
v1 = iconst.i32 2
v2 = iconst.i32 3
v3 = iconst.i32 4
return v0, v1, v2, v3
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: mov r0, #1
; nextln: mov r1, #2
; nextln: mov r2, #3
; nextln: mov r3, #4
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr

View File

@@ -0,0 +1,157 @@
test compile
target arm
function %ishl_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = ishl v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxtb r1, r1
; nextln: lsl r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ishl_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = ishl v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxth r1, r1
; nextln: lsl r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ishl_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = ishl v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: lsl r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ushr_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = ushr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxtb r0, r0
; nextln: uxtb r1, r1
; nextln: lsr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ushr_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = ushr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: uxth r0, r0
; nextln: uxth r1, r1
; nextln: lsr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ushr_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = ushr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: lsr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sshr_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = sshr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sxtb r0, r0
; nextln: uxtb r1, r1
; nextln: asr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sshr_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = sshr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: sxth r0, r0
; nextln: uxth r1, r1
; nextln: asr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %sshr_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = sshr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: asr r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %ror_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = rotr v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: ror r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr
function %rotl_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = rotl v0, v1
return v2
}
; check: push {fp, lr}
; nextln: mov fp, sp
; nextln: and r1, r1, #31
; nextln: rsb r1, r1, #32
; nextln: ror r0, r0, r1
; nextln: mov sp, fp
; nextln: pop {fp, lr}
; nextln: bx lr