ARM寄存器的功能

时间:2014-10-08 19:12:23

标签: assembly arm microprocessors

我对ARM寄存器有些怀疑。

  • r12的主要目的是什么?在其他建筑中与FP相同吗?
  • 其中一些用于“刮擦”,是否意味着临时存储/来电保存?
  • r13和r14根据模式具有不同的名称。如果我使用#MOV R0,R14_svc,我会在R0中获得它的内容吗?或者它只是一种在不同模式下区分R14并且没有硬件差异的方法?

1 个答案:

答案 0 :(得分:1)

您正在查看的结果不是症状。

访问武器网站(infocenter.arm.com)并找到armv5的arm架构参考手册,你可能不得不放弃一个电子邮件地址,但这些很容易找到,没什么大不了的。

因此,有一个手臂指令集和随之而来的手臂架构。有些寄存器是特殊的,有些则不是。显然r15非常特殊,它是程序计数器,并且有一些指令具有仅支持r15(pc相对寻址)的寻址模式。同样地,r14是特殊的,它也是用于分支链接指令的硬连线,返回是通用的,但是调用不是,确定你可以在不使用r14的情况下进行调用,但这并不重要r14是特殊的,因为它是硬连线到一个/一些指令。 / p>

在拇指模式下,r13被硬编码到推送和弹出指令中(在手臂模式下,ldm和stm在技术上可以使用任何寄存器,尽管r15可能是一个坏主意)。

可能还有其他人......

因此,某些寄存器硬连线到某些指令或寻址模式。所以除了所有这些之外,可能出于性能原因,有不同的处理器模式,管理程序,中断等......并且arm文档向您展示这些寄存器中的一些具有_svc版本或_und版本等等。是的, r14_svc位与r14_abt的位不同,如果修改r14_abt,则无法使用r14_svc或r14_irq读取该值。当特定指令根据处理器模式读取或写入r14或r13等时,选择不同的存储体并使用模式特定的寄存器组。对于r0-r7,在所有模式下使用相同的实际寄存器/位/ ram,但从r8开始知道使用哪些寄存器,您必须查看模式。根据ARM ARM的说法,此声明可能有所不同,但我现在正在查看的声明如下:

ARM处理器共有37个寄存器:

他们向你展示r0-r15的图片,它加起来是37而不是16,因为它们是独立的寄存器。

现在你开始调用与硬件无关的约定,它只是一个人们已经同意使用的习惯或约定。如果你跳上你的回程机器,你可能会发现一些x86编译器使用基于堆栈的参数传递,一些使用基于寄存器,它们彼此不兼容,有时您可以指定编译时使用哪种约定。其他一些人也是如此,但不是全部。如果你看看mips文档,那里的硬件人员喜欢通过根据约定重命名寄存器来定义调用约定,这不是刻在你不必遵守它的硅片中。与在架构手册之外的其他地方定义的arm调用约定相同,因为它永远不会有意义,但它在某处被定义,并且编译器编写者是否适合使用它们是否适用于那些标准甚至更改时手臂改变了惯例。

这是你开始看到使用r0-r3传递参数的惯例,然后在你使用堆栈之后消耗它们,如果你的计算机感觉需要有一个帧指针(懒惰的程序员,更容易调试编译器输出)他们选择r12或其他什么。约定定义了函数需要保留哪些寄存器以及它不必保留哪些寄存器,并定义了如何返回值。虽然在某些方面的约定是任意的,如果你正在制作自己的编译器,你可以创建自己的或使用堆栈,而不是注册等,你可能使用的编译器符合arm调用约定(abi / eabi) )在一个不断发展的版本中。

是的,这就是为什么一个或多个上层寄存器没有保留在约定中,这样如果你觉得需要一个帧指针就可以将它用作帧指针。

保存意味着什么?如果你看一下较旧的寄存器和较不成熟的编译器以及堆栈参数传递的旧时代,你基本上保留了你在进入函数时要触及的每个寄存器并在退出时恢复它们,你修改的东西(返回值)在堆栈中或者你在函数中使用堆栈的顶部作为易失性东西(局部变量,中间值等)。即使有16个寄存器,你也可以开始讨论使用寄存器进行参数传递,甚至可以制定一个你不必保留所有这些寄存器的约定。这就是武器公约所做的和其他一些。

unsigned int fun ( unsigned int a, unsigned int b )
{  
  return(a+b+7);
}

a将被传递给寄存器r1中寄存器r0,b中的函数,因为约定是这样说的。返回值在r0中,因为约定是这样说的,所以代码的优化版本是通过向它添加7来破坏r0,所以a值消失了,我们可以从代码中看到我们只需要一个值足够长,可以用它进行一次数学运算。所以我们可以

r0 = r0 + 7;
r0 = r0 + r1;

并返回

或者我们可以

r1 = r1 + 7;
r0 = r0 + r1;
return

或主题的其他变体。在上述任何一种情况下,我们都没有保留r0,因为约定基本上说我们必须销毁内容,因为这是我们的回报。但我们也允许至少修改r0,r1,r2,r3所以我们也可以这样做:

r2 = r1+7;
r3 = r2+r0;
r0 = r3;
return

这是合法的,因为除了我们之外没有人关心我们与r0-r3(有时是其他人)混淆。

如果你这样做

r4 = r1+7;
r0 = r0 + r4;
return

你可能会幸运而不会崩溃,或者你可能会在某种程度上崩溃,或者很长一段时间不会崩溃但是会崩溃。为什么因为规则说我们不应该修改r4,我们必须保留它:

push {r4}
r4 = r1+7;
r0 = r0 + r4;
pop {r4}
return

当我们退出函数时,它必须与我们输入时的值相同。为了说明这一点:

unsigned int fun ( unsigned int a, unsigned int b )
{  
  return(more_fun(a,b)+a+b+7);
}

要实现这一点,我们可能会这样做

push {r4,r14}
r4 = r0 + r1;
r4 = r4 + 7;
call more_fun();
r0 = r0 + r4;
pop {r4,r14}
return

我们需要记住a + b + 7基于r0和r1中的值是什么,但由于r0和r1可以被more_fun()修改,我们需要按原样保存它们,或先做数学然后保存结果。无论哪种方式,我们需要将它们放在堆栈上以保存它们或将它们放在非易失性寄存器中,并且要使用非易失性寄存器,我们需要保存它的先前内容。

现在如果more_fun()违反了规则并修改了r4,那么我们的fun()结果将是不正确的,并且不正确的值可能会导致fun()的调用者做错事或崩溃等等,或者有时候你很幸运它会修改r4但它会以相同的值或安全值的方式修改它。那么也许你改变几行代码,现在r4上游用于保存其他东西,你软管那么现在你崩溃了。

只要每个函数的每个实现都遵循约定,那么一切都会成功。 (假设惯例是手臂一般的理智)。

相关问题