gpt4 book ai didi

assembly - 在MIPS中使用addi vs addiu的特定情况

转载 作者:行者123 更新时间:2023-12-02 19:13:31 25 4
gpt4 key购买 nike

我已经阅读了有关addiu的几篇文章,我认为了解一些特定情况/案例的示例对我有帮助,这时使用addiu而不是addi更好(反之亦然)。

这篇文章指出处理地址和添加地址,使用addiu防止溢出“陷阱”很重要。但是然后似乎可以说陷阱根本没有真正的好处,那么为什么我应该完全使用addi而不是addiu? Why would we use addiu instead of addi?

我的教授似乎只是在互换使用它们,以提醒我们阿迪乌存在吗???但这最终使我感到困惑,有时不知道“在这种情况下我是否真的需要使用addiu还是只是将其用于'fun'?”

最佳答案

那么,为什么我要完全使用addi而不是addiu?


通常,您不应该这样做,除了assert()之外,加法运算中没有2的补码符号溢出。如果您希望签名的值不会溢出,那么您可能会希望这样做,尤其是在手工编写asm时。 (编译器从不使用add,始终使用addu。)

除了包括addiu的16位立即数的符号扩展之外,指令在字面上完全相同。 (与ori和其他零扩展的布尔值不同。MIPS的某些指令确实需要零扩展,但是addiu的符号扩展意味着它也不需要subiu操作码来减去小整数。)

在您自己使用的程序中,让程序在错误的输入上出错可能比让某些不应该包装的东西更好。如果您是为Linux MIPS而不是MARS编写的,则可以为SIGFPE(算术异常)安装信号处理程序,这样至少可以打印出人类友好的错误消息,例如“检测到签名溢出,异常中止”。在MARS中,我假设您只是被放到调试器中。



完全存在addaddi的唯一原因(而不是普通的adduaddiu,它们执行大多数ISA(如x86和ARM调用add的操作))是整数溢出检测。

整数溢出检测是一个难题。 (另请参见https://en.wikipedia.org/wiki/Integer_overflow)。 ISA在为它提供硬件支持方面进行了各种尝试,这将使软件(尤其是高级编译语言)可以例行使用它。 MIPS的add / sub指令就是这样的一种尝试,但不涵盖左移之类的情况。因此,想要对每个操作提供溢出检查的高级语言将需要单独的机制。或者它们可能仅使用其他机制,而根本不使用add / sub

与MIPS相比,请考虑使用x86或ARM之类的ISA:它们具有带有标志溢出位的FLAGS或状态寄存器,并且如果最后一条指令将该位设置为1,则可以执行条件转移。 x86甚至有一个into(如果在FLAGS中设置了OF则为陷阱)指令。

但是MIPS根本没有标志寄存器,只有整数寄存器。因此,除了通过add之类的加法和分支指令之外,没有地方可以记录加法运算溢出的事实。无需花费任何比特来为溢出情况编码分支目标,唯一的选择就是陷阱/异常。

(好吧,另一个易于使用的选择是可以设置的特殊寄存器,但是上下文切换将不得不保存/恢复它。它必须像JAL一样工作,以记录添加了错误的错误。这可能要求使用MIPS避免的微码,或者至少对这种特殊情况进行更复杂的处理,因为它就像一个异常,但不是异常,并且不能像beq这样的早期条件被检测到,因为它确实需要完整的32-位加结果。)



为什么MIPS的架构师将普通加法指令命名为addu

我不知道,也许他们是硬件人员,而不是软件人员,并且过分乐观,他们将迎来一种新的检查算术时代,并使许多整数溢出错误成为过去。

从现在起,将add助记符赋予编译器始终使用的标准非陷阱版本将是最有意义的。然后,您需要为陷印版本使用另一个名称。可能是addt(陷印)或addc(已添加检查)。但是addc太糟糕了,因为与其他ISA(通常是adc)上的“随身携带”相混淆。 adds(加号)是可能的。命名很难!



基础知识:在某些明显的情况下,您需要使用addu

对于(可能)大的无符号整数,使用addu显然很重要。 0x7fffffff + 1对于未签名没有什么特别的。但是从INT_MAX到INT_MIN是2的补码符号溢出,因此add会被捕获。

对于指针,使用addu可能很重要。除非您使用的是“高半内核”内存模型,否则一个数组不可能从0x80000000开始并在其之上结束。



下面是我回答这个问题的第一个版本。以上部分是多余的。但我认为并非全部。 (待办事项:请完成编辑;我现在没有时间,但是我认为现在发帖要比等到稍后再讲。)


何时在addi上使用addiu更好,反之亦然(以及为什么更好)。


仅当您特别希望机器在2的补码有符号溢出(例如像INT_MAX +1变成INT_MIN的环绕)时捕获(aka错误,引发异常)时,才使用add / addi

在大多数情况下,没有人希望这样做,但是如果引发异常比继续使用错误的值更好,那么也许如果您正在手工编写asm并希望防御某些整数溢出错误,则可以使用add。当然,这仅适用于add,不能左移超过1个或其他指令。因此,如果您确实想在一些防御性代码中进行全面的整数溢出检查,则仍然需要另一种机制。

为了使之成为一个好主意,您可能希望为该硬件异常安装一个中断处理程序。 (或者在OS下的用户空间中,SIGFPE的信号处理程序,算术异常的POSIX信号。)

或者在开发期间,也许您希望整数溢出突然停止在您期望不会溢出的添加指令上。即像assert。编译器没有,因为有时只进行检查而不对所有内容都进行整数运算会很奇怪。但是用手工可能总比没有好。

整数溢出错误经常发生的地方之一是内存分配大小的计算。 (例如,导致分配量少,程序结束时可能是DOS错误或取决于分配程序的内存覆盖错误)。但是那些经常使用无符号整数。对unsigned执行0x7fffffff + 1 = 0x80000000并不是错误。

否则请使用addu / addiu

addu是MIPS的常规添加指令。

正如Difference between add and addu所指出的,这些名称具有误导性。 u可能与带符号的溢出在C中是未定义的行为相对应,但是带符号的溢出被明确定义为环绕。但您知道,2的补码加法与普通无符号二进制加法相同。 addi仅检查签名的溢出。

(并不是说C编译器永远不会使用addaddi:他们不想编写有错误的代码。签名溢出是UB,但这并不意味着它们必须做任何特别的事情。而且,在优化之后构造具有C抽象机中不存在的临时值的asm并不罕见,即使(w+x)+(y+z)没有,将w + x + y + z用作(((w+x)+y)+z)也可能会签名溢出;二进制加法的确是关联,而不考虑签名溢出。)



MIPS32r6甚至删除了addi,仅保留了无故障的addiu。 (因此,如果要捕获带符号的溢出,仍可以将操作数放入寄存器中并使用add。)在已删除的不常用指令的项目符号列表on Wikipedia中列出了该操作数,将其作为带有16的整数溢出捕获指令。位立即数应该可以告诉您有关addi实际使用量的信息。




我的教授似乎只是在互换使用它们,以提醒我们阿迪乌存在吗???


很难从中判断这是否真的是真的,或者它们是否有您尚未接受的微妙原因。例如在绝对安全的情况下使用addi

还是每当他们考虑将整数视为带符号时,即使代码中仅包含非负值?例如就C抽象机而言,for(int i=0 ; i<100; i++)signed int

在已知不可能发生签名溢出的情况下(例如在lui之后),这实际上并不重要。但是,IMO除非需要,否则始终始终使用addiu。对我而言,作为人类阅读代码,理想情况下看到add / addi表示我们有意进行签名溢出检查。但是在SO问题中,更常见的是初学者只是使用add,因为他们不考虑甚至不了解addu。因此,它迫使您寻找“误报”陷阱错误的风险:add在您不希望出现错误的情况下可能会出错。



我认为没有任何真正的MIPS CPU add / addiaddu / addiu慢。可能不是;任何lwsw都可能根据寄存器输入而发生故障,因此MIPS管道需要能够有效处理可能存在故障的指令。

在通常情况下,它们永远不会出错。您为此优化了管道,而采取故障的效率到底有多低几乎无关紧要。 (或者,在具有软件TLB缺失处理的MIPS上,这确实很重要;这可能相当频繁地发生,比页面错误要严重得多)。

关于assembly - 在MIPS中使用addi vs addiu的特定情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58519363/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com