【问题标题】:Can I modify the return data type from int to int8_t or int16_t?我可以将返回数据类型从 int 修改为 int8_t 或 int16_t 吗?
【发布时间】:2015-10-30 20:44:46
【问题描述】:

我正在使用 MIPS32 并使用 C 进行编码。

目前我的代码中的许多函数都返回“int”数据类型。

由于我的开发是在资源受限的硬件上(甚至字节也很重要),并且返回值只是错误代码(不超过 255),因此我计划将返回类型缩小为 int8_t 或 int16_t。

我想要实现的是减少调用者的堆栈/内存使用量。

在我尝试之前, 这会导致调用者的堆栈/内存使用减少吗?或

由于我听说过内存对齐(主要为 4 个字节)并且不太了解,这会在这里玩坏游戏吗?

示例

int caller(){
    int8_t status;
    status = callee();

}

int8_t callee() {
    ...
    return -1;
}

在上面的示例中,status 标识符声明为 int8_tint16_tint 在 mips32 中是否重要?

【问题讨论】:

  • 如果您提供一个您实际拥有的示例并说明您计划做什么以及您的担忧在哪里,将有助于了解您尝试实现的目标。目前我的回答只是“当然,你为什么不可以?”
  • 你应该检查你的调用约定。在某些情况下,寄存器可能用于返回数据,并且由于它是 32 位的,因此 1,2 或 4 字节之间没有差异。请参阅en.wikipedia.org/wiki/Calling_convention#MIPS 它说“返回值存储在寄存器 $v0 中”
  • @user996142 这就是我想知道的。在哪些情况下,这没有区别?
  • @Benvin 总之。将返回类型从 int 更改为 int8_t 不会减少堆栈的使用。如果您的堆栈空间不足,那么您的程序通常有问题。
  • 我不是低级大师,所以你应该用调试器重新检查我的答案或等待真正知道答案的人,但我相信这没有什么区别,因为寄存器用于返回值,并注册反正是 32 位的。

标签: c


【解决方案1】:

当涉及到调用堆栈时,这绝对不会产生任何变化,可以在此处找到 MIPS 调用堆栈的示例。 https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf

$31 $ra 这 退货地址 在子程序调用中。

下面是一张图片,您将看到返回地址是一个完整的寄存器,在您使用 32 位机器的情况下,您的寄存器将是 32 位的大小,并且没有改变。

我不得不问,你在做什么需要 MIPS?一般来说,这是一种用于教学目的的语言,在现实世界的实际用途中没有太多的使用方式,因为它有很多缺陷。例如,现代程序集(如 X86)中不存在返回地址的概念,其中堆栈指针将包含所有这些信息。

编辑: 正如下面的人指出的那样,我有点不公平。从技术上讲,这些地址也存在。

$2-$3 $v0-$v1 这些寄存器包含 返回值 子程序的;如果 该值为 1 个字,只有 $v0 有意义。

同样,尽管它们具有固定的大小,并且从调用堆栈的角度来看,它们使用的是一个完整的寄存器。从理论上讲,我相信 MIPS 有办法在一个寄存器中存储 4 个字节,但我对此不确定。更重要的是,尽管使用 MIPS 的工作方式,这些返回寄存器只能在调用深度为一个函数时使用。如果你在一个函数中调用一个函数,这个概念就会分崩离析,返回地址就变成了必需的,因此我最初只是展示了一个。

【讨论】:

  • 返回地址和返回值有区别。
【解决方案2】:

首先,“不要超过 255”意味着您应该使用 uint 而不是 int

当手动优化代码大小时,您应该使用uint_leastn_t 类型。这些类型允许编译器选择代码工作所需的最小可能类型,该类型至少为n 字节宽。

在您的情况下,这将是 uint_least8_t。当然,如果编译器总是选择 32 位类型,因为这是对齐访问所必需的,那么通过替换 int 获得的唯一好处就是更好的可移植性。

【讨论】:

    【解决方案3】:

    在 MIPS32 上,前四个函数参数(整数或指针;为简单起见,我不考虑 64 位整数、浮点数或结构)到达寄存器 a0 到 a3。其余的进入堆栈,堆栈内存的每个机器字只保存一个参数。因此,在传递错误代码方面不会有任何区别。

    如果您必须将错误代码存储在本地(自动)变量中,很大程度上取决于代码。但是,MIPS 有很多寄存器,并且可能会有一个寄存器可用于错误代码,因此不需要堆栈空间。

    如果您有保存错误代码的全局变量,那么使用不同大小的类型肯定会有所不同。

    回到堆栈,你应该注意到还有其他一些事情在起作用......

    首先,堆栈必须对齐。现代编译器倾向于将堆栈指针对齐而不是机器字的倍数,而是两个机器字的倍数,这一事实使情况变得更糟。因此,如果您只考虑一个错误代码,那么编译器很可能会在堆栈上填充局部变量以使它们的累积大小成为两个机器字的倍数,从而抵消任何收益。

    其次,堆栈指针通常只在函数入口处减少一次局部变量和临时变量的大小(而在退出时只执行一次相反的操作)。这意味着在函数的某些地方可能有一些未使用的堆栈空间,这些空间仅保留用于函数的其他地方。因此,从函数的某些地方调用(尤其是深度递归调用)会过度浪费堆栈空间。

    第三,ABI 要求从 a0 到 a3 到达的这四个参数具有与之关联的堆栈内存,因此它们可以存储在那里并通过 printf 等函数中的指针寻址(回忆 stdarg.h 的 va_list、va_start( )、va_arg() 等)。因此,许多调用也可能会浪费这 16 个字节的堆栈空间。

    您可能需要考虑的另一件事是,当函数返回 8 位或 16 位整数类型时,调用者需要将这些 8/16 位符号扩展(或零扩展)为完整的机器字大小(32 位),这意味着调用者必须使用额外的指令,如 seb、seh 和 andi。因此,这些可能会对代码大小产生负面影响。

    最终,这在很大程度上取决于您的代码和编译器。您可以使用这两种类型并使用编译器的不同优化选项来测量堆栈使用情况,并选择最佳的。您还可以尝试重组代码以避免调用或使编译器更容易对其进行优化(例如,静态函数会有所帮助,因为编译器在调用它们时可能会偏离 ABI 并更有效地优化它们并将值传递和返回到和从他们)。而这确实是你应该做的,尝试不同的事情并选择你最喜欢的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-07
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 2020-02-15
      • 1970-01-01
      • 2010-10-10
      • 1970-01-01
      相关资源
      最近更新 更多