两个无符号整数程序和两个有符号整数程序的相同 ARM7 汇编代码

发布于 2025-01-12 14:40:27 字数 497 浏览 0 评论 0原文

我对两个程序 minu 和 mins 使用了相同的代码,它们都必须找到两个输入的最小整数。 mins 接受两个 8 位无符号整数,而 mins 接受两个 16 位有符号整数。

程序 1(分钟):

uint8_t a = 5
uint8_t b = 10
//result should equal 5

程序 2(分钟)

int16_t = -10
int16_t = 10
//result should be -10

ARM7 机器代码: 程序1:

minu:
  CMP R0,R1
  BGE end
  BX LR 
end: 
  MOV R0,R1
  BX LR

程序2:

mins: 
  CMP R0,R1 
  BGE end
  BX LR
end:
  MOV R0,R1
  BX LR

I used the same code for two programs minu and mins which both have to find the smallest integer of the two inputs. minu takes in two 8 bit unsigned integers wheras mins takes in two 16 bit signed integers.

program 1 (minu):

uint8_t a = 5
uint8_t b = 10
//result should equal 5

program 2 (mins)

int16_t = -10
int16_t = 10
//result should be -10

ARM7 machine code:
program 1:

minu:
  CMP R0,R1
  BGE end
  BX LR 
end: 
  MOV R0,R1
  BX LR

program2:

mins: 
  CMP R0,R1 
  BGE end
  BX LR
end:
  MOV R0,R1
  BX LR

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乖乖哒 2025-01-19 14:40:27

您已在 32 位寄存器上使用了有符号比较操作。

如果调用者使用正确的加载指令从内存中获取 8 位/16 位数据并将其转换到正在比较的寄存器中(或者以其他方式管理从 8 位/16 位到 32 位的转换),那么这是可以的。 -少量)。

对于无符号 8 位代码,您需要使用 LDRB,对于有符号 16 位代码,您需要使用 LDRSH

在无符号情况下,使用 LDRB 将从内存加载的 8 位无符号数据通过零扩展转换为 32 位 - 寄存器的高 24 位为零,低 8 位为值凭记忆。无论在 32 位中解释为有符号还是无符号,LDRB 之后寄存器中的最终值始终 >= 0,因为寄存器中的符号位将为零。是否考虑有符号或无符号的 32 位值取决于您,因为我们知道它始终是正数(非负数),因此从某种意义上来说这并不重要。 C 语言会说它是 int,这是一种有符号数据类型,因为它倾向于 int 并且 int 可以轻松地保存来自 a 的任何值。有符号字节和/或无符号字节。

在有符号情况下,使用 LDRSH 将从内存加载的 16 位有符号数据转换为寄存器中的 32 位有符号值。它通过将符号位从 16 位存储器半字的第 15 位传播到寄存器上半部分的所有位来实现此目的。在这种情况下,考虑具有有符号数据类型的 32 位寄存器非常重要,因为我们可能有负值或正值。


当内存中有 32 位值时,它们既不能进行符号扩展,也不能进行零扩展以适合 32 位寄存器,因为所有位都已从内存中指定。因此,对于无符号,您将必须使用 unsigned >= 条件 - 这意味着分支指令必须更改为BHS(分支更高或相同),而对于有符号,您将必须使用有符号 >= 条件 (BGE)1

因为据说您的代码使用较小的数据类型(小于32 位:8 位无符号和 16 位有符号)您可以对这两种类型使用有符号比较 - 只要这些值正确并相应地扩展到寄存器中的 32 位。

如果将值从内存加载到具有不正确的符号/零扩展名的寄存器中,则必须在使用比较操作之前使用其他指令修复该问题。然而,在 x86 上,由于其部分寄存器功能,您可以仅比较寄存器的低 8 位或低 16 位。在使用较小尺寸比较的 x86 处理器上,上述关于完整 32 位值的注释1也适用。

You have used signed comparison operations, and on 32-bit registers.

This is ok, if the caller used the proper load instructions to fetch and convert the 8-bit/16-bit data from memory into registers that are being compared (or else otherwise managed the conversion from 8-bit/16-bit to 32-bit).

For the unsigned 8-bit code you would need to use LDRB and for signed 16-bit, LDRSH.

In the unsigned case, using LDRB will convert 8-bit unsigned data loaded from memory to 32-bit by zero extension — the top 24 bits of the register get zero, and the lower order 8 bits the value from memory.  This final value in the register after LDRB is always going to be >= 0 whether interpreted as signed or unsigned in 32-bits, since the sign bit in the registers will be zeros.  It is up to you whether to consider the 32-bit value signed or unsigned, because we know that it will always be positive (non-negative) so in a sense it doesn't matter.  The C language would say it is int which is a signed data type, because it gravitates toward int and int can easily hold any value from a signed byte and/or an unsigned byte.

In the signed case, using LDRSH will convert 16-bit signed data loaded from memory to 32-bit signed value in the register.  It does this by propagating the sign bit from bit 15 of the 16-bit memory halfword into all the bits of the upper half of the register.  It is important in this case to consider the 32-bit register with the signed data type, since we may have negative or positive values.


When you have 32-bit values in memory, they can neither be sign extended nor zero extended to fit in a 32-bit register, as all the bits are already specified from memory.  As a result, for unsigned you would have to use unsigned >= conditions — meaning the branch instruction would have to change to BHS (branch higher or same), whereas for signed you would have to use signed >= conditions (BGE).1

Because your code is said to use smaller data types (smaller than 32-bits: 8-bit unsigned, and 16-bit signed) you can get away with using signed comparison for both types — provided the values are properly and accordingly extended to 32-bits into the registers.

If the values were loaded from memory into registers with an incorrect sign/zero extension, you would have to fix that with other instructions before using the comparison operations.  On x86, however, you can compare just the lower 8-bits or just the lower 16-bits of the registers with each other due to its partial register feature.  On the x86 processor using smaller size compares, the above comments about the full 32-bit value made above1 would also apply.

隐诗 2025-01-19 14:40:27

如果您的调用者遵循标准调用约定并根据参数的符号将 args 零扩展或符号扩展为 32 位传递,那么这种情况就会起作用。与 uint32_t 不同,所有 uint8_t 值都可以表示为非负符号 int(32 位),因此像 BGE 这样的带符号比较将给出正确的结果。

即使用将它们都视为 32 位有符号 int 的函数;这就是此调用约定规则的要点,因为 ARM 没有只能比较输入寄存器的低 8 位或 16 位的 cmp 指令。

但这不是编译器所做的,但是,它们使用 cmp / movlo r0, r1movlt r0, r1 来表示无符号 LOWer 和有符号 LEss -比。 (如果谓词为 true,则为 MOV,否则作为 NOP 运行)。

请参阅 Godbolt 编译器浏览器。

That will happen to work if your caller follows the standard calling convention and passes args zero-extended or sign-extended to 32-bit according to their signedness. Unlike uint32_t, all uint8_t values can be represented as non-negative signed int (32-bit), so signed comparison like BGE will give the correct result.

i.e. using a function that treats them both as 32-bit signed int works; that's the point of this calling convention rule, because ARM doesn't have a cmp instruction that could compare only the low 8 or 16 bits of input registers.

That's not what compilers do, though, they use cmp / movlo r0, r1 vs. movlt r0, r1 for unsigned LOwer vs. signed LEss-than. (MOV if the predicate is true, otherwise it runs as a NOP).

See it on the Godbolt compiler explorer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文