我正在用 ARM 汇编编写一个 main.s 程序,并将其与 checkPrimeNumber.c 程序一起运行。程序提示用户输入两个正数并输出它们之间的素数。例如,如果输入 2 和 6,则输出的素数将为 3 和 5。 main 中调用 checkPrimeNumber 函数来检查给定整数 n 是否为素数。我遇到一个问题,如果我输入两个数字,每次尝试运行程序时都会得到 1 的值。
我在调试器中运行程序并修复了一些寄存器,以便它们不占用程序中的空间,但这并没有改变结果。我不确定这是否是内存分配问题,或者我是否没有在程序中正确进行比较。作为参考,我提供了 main.s 代码和 checkPrimeNumber.c 代码
主.s
.cpu cortex-a53
.fpu neon-fp-armv8
@ Data section
.data
inp1: .asciz "Enter two positive numbers"
inp2: .asciz "%d %d"
outp: .asciz "Prime numbers between %d and %d are: %d\n "
.balign @ Align the data section to 4 bytes
n1: .word 0
n2: .word 0
i: .word 0
flag: .word 0
.text
.align 2 @ Align the code section to 2 bytes
.global main @ Declare 'main' as a global symbol
.type main, %function @ Specify the type of the 'main' symbol as a function
main:
push {fp, lr} @ Save the frame pointer (fp) and link register (lr) to the stack
add fp, sp, #4 @ Set the frame pointer (fp) to the current stack pointer (sp)
@ Print the prompt "Enter two positive numbers"
ldr r0, =inp1
bl printf
@ Scanf ("%d %d", &n1, &n2)
ldr r0, =inp2
ldr r1, =n1 @ Load the memory address of n1 into r1
ldr r2, =n2 @ Load the memory address of n2 into r2
bl scanf
@ Initialize flag = 1 (to assume that numbers are prime)
ldr r0, =flag
mov r5, #1
str r5, [r0]
@ Load the input values from memory into r1 and r2
ldr r1, =n1 @ Load the value of n1 into r1
ldr r8, [r1] @ Load the content of the memory address pointed by r1 into r1
ldr r2, =n2 @ Load the value of n2 into r2
ldr r6, [r2] @ Load the content of the memory address pointed by r2 into r2
@ Initialize i = n1 + 1
add r3, r8, #1
forloop: @ Loop label (start of the loop)
@Check if i < n2
cmp r3, r2 @ Compare i with n2
bgt done @ If i is greater than or equal to n2, exit the loop
@ Call the function checkPrimeNumber(i) and store the result in 'flag'
mov r0, r3 @ Move the current value of i to r0 (the function argument)
bl checkPrimeNumber
@ Check if flag == 1 (if the number is prime)
cmp r0, #1
bne increment_num @ If not equal to 1, go to increment_num label and proceed to the next iteration
@ Print the output statement "Prime numbers between %d and %d are: "
ldr r0, =outp @ print out end prompt
mov r1, r8 @ Move the first range value (n1) to r1 (for printf)
mov r2, r6 @ Move the second range value (n2) to r2 (for printf)
mov r4, r3 @move the resulting prime numbers (i) to r4 (for printf)
bl printf
increment_num: @ Increment the loop variable (i) and continue to the next iteration
add r3, r3, #1
b forloop @ Branch back to the forloop label
done:
sub sp, fp, #4 @ Restore the stack pointer (sp) to its original value
pop {fp, pc} @ Restore the frame pointer (fp) and program counter (pc) to return from the function
检查PrimeNumber.c
#include <stdio.h>
int checkPrimeNumber(int n);
int main()
{
int n1, n2, i, flag;
printf("Enter two positive integers: ");
scanf("%d %d", &n1, &n2);
printf("Prime numbers between %d and %d are: ", n1, n2);
for(i=n1+1; i<n2; ++i)
{
// i is a prime number, flag will be equal to 1
flag = checkPrimeNumber(i);
if(flag == 1)
printf("%d ",i);
}
return 0;
}
您的代码不遵守 AAPCS ABI 规定的调用约定的寄存器使用规则:
子程序必须保留寄存器 r4-r8、r10、r11 和 SP 的内容(以及 PCS 变体中将 r9 指定为 v6 的 r9)的内容。
所以:
您必须假设 r0-r3 被任何函数调用覆盖(除非您自己用纯 asm 编写的函数)。它们是“被调用破坏的寄存器”。在您的代码中,您使用 r2 和 r3 进行循环索引,但它们可能会被对
checkPrimeNumber
和 printf
的调用覆盖。
r4-r8 和 r10 不会被函数调用覆盖(也就是说,如果函数使用这些寄存器,它将在返回之前恢复其原始值)。它们是“呼叫保留的”。因此它们适合循环索引等值。但是,您的
main
函数本身受 ABI 规则的约束,因此您必须确保在使用任何这些寄存器时,在 main
返回之前恢复其原始值。最简单的方法是将它们添加到函数入口和出口处的 push
和 pop
指令的寄存器列表中。
另一个注意事项是,每当调用函数时,堆栈指针都必须与 8 字节对齐。输入
main
时保证对齐,因此确保这一点的一个简单方法是确保在函数的入口/出口时推送/弹出偶数个寄存器(前提是您不修改 sp
其他任何地方)。现在您的代码满足了这一点,但在添加到推送/弹出列表以保留您使用的调用保留寄存器时请注意这一点。如果最终得到的是奇数,您可以简单地在列表中添加一个额外的寄存器,例如 r1
。
其他一些评论:
您的asm代码不包含C代码中的
return 0
。在 mov r0, #0
之后添加 done:
。
实际上没有必要将
i
和 flag
保留在记忆中。事实上,您的代码已经没有真正使用它们;您在那里存储永远不会加载的值。您只需选择一个用于 i
的调用保留寄存器并在整个过程中使用它。并且 flag
根本不需要存在,因为它纯粹是暂时的。你在 checkPrimeNumber
中得到 r0
的返回值,然后简单地执行 cmp r0, #1
就可以了。这类似于将 C 代码编写为 if (checkPrimeNumber(i) == 1)
,而没有不必要的 flag
变量。
C 代码中的变量
n1, n2
是 main
的本地变量,但在您的 asm 代码中,您已将它们设为全局变量。无论哪种方式都可以正常工作,但是将它们放在本地会更干净一些,因此您可以尝试更改 asm 版本以将它们保留在堆栈上而不是数据部分中。您可能会发现这也会稍微缩短您的代码。例如,如果 n1
位于 sp+4
,那么您只需执行 ldr r1, =n1
,而不是 add r1, sp, 4
;您可以将 ldr r1, =n1 ; ldr r8, [r1]
替换为 ldr r8, [sp, #4]
。