- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我已经阅读了有关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中,我假设您只是被放到调试器中。
完全存在add
和addi
的唯一原因(而不是普通的addu
和addiu
,它们执行大多数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编译器永远不会使用add
或addi
:他们不想编写有错误的代码。签名溢出是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
/ addi
比addu
/ addiu
慢。可能不是;任何lw
或sw
都可能根据寄存器输入而发生故障,因此MIPS管道需要能够有效处理可能存在故障的指令。
在通常情况下,它们永远不会出错。您为此优化了管道,而采取故障的效率到底有多低几乎无关紧要。 (或者,在具有软件TLB缺失处理的MIPS上,这确实很重要;这可能相当频繁地发生,比页面错误要严重得多)。
关于assembly - 在MIPS中使用addi vs addiu的特定情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58519363/
我被告知“汇编”是您在文件中编写的内容,让您的“汇编程序”将其转换为二进制代码。 但我看到这两个术语在各种作品中混合搭配。我什至听说你编写了“汇编器”,然后“汇编器”使其可执行。 正确的用词是什么?
我在正确终止用 Assembly 编写的 16 位 DOS 程序时遇到问题。这是部分代码: .386P .model flat stack_s segment stack 'stack'
我需要多少档才能正确执行以下指令。我对我所做的事情有些困惑,所以我在这里看到专家的答案。 lw $1,0($2); beq $1,$2,Label; 请注意,检查是否会发生分支将在解码阶段完成。但是在
我正在尝试在汇编中进行简单的乘法运算,但是由于某些原因,当标记了MUL函数时,我看不到寄存器会发生变化。 mov bx, 5 mov cx, 10 mul cx 最佳答案 这些称为指令,它们指定
我正在尝试在 Assembly 中实现递归斐波那契程序。但是,我的程序崩溃了,出现了未处理的异常,我似乎无法找出问题所在。我不怀疑这涉及我对堆栈的不当使用,但我似乎无法指出哪里...... .386
我编写了以下代码: .386 .model small .stack 100h .data text db "Paper",0 .code start : lea dx ,
我有一个用汇编语言编写的裸机 ARM 的启动代码,我正在尝试了解它是如何工作的。该二进制文件被写入一些外部闪存中,并在启动时将其自身的一部分复制到 RAM 中。尽管我读过这篇文章wikipedia e
我在数据部分定义了一个二维数组和两个一维数组(一个用于列总和,一个用于行总和),并且我编写了一个函数,将二维数组求和到一维数组中。我使用 eax 和 ebx 作为二维数组的索引,但是当 eax 或 e
我正在开始组装,我正在使用 nasm 来组装代码,我正在尝试处理驻留在内存中的字符串并更改它,我想检查一个字节是否在某个范围内(ascii),这样我就可以决定如何处理它,我似乎不知道如何检查一个值是否
虽然您通常不希望将一个整体程序集用于小型项目以外的任何事情,但可能会将事物分离得太多。 组装分离过多的迹象/气味是什么? 最佳答案 第一个(明显的)是:在一个有很多项目的解决方案中,其中只有少数(比如
我正在尝试编写斐波那契的汇编代码版本,它给出第 n 个斐波那契数并返回它。 出于某种原因,它在存储斐波那契数的返回值和添加它们时遇到问题。 我希望它打印第 n 个斐波那契数。 我对我的代码做了一些修改
我有一个最小的、可重现的示例有两个问题,该示例具有三个针对 .NET Core 3.1 的项目。但我也想以 .NET Standard 2.0 为目标。 该示例适用于需要在运行时加载程序集并使用提供的
: 运算符在汇编中做什么?代码如下:DS:DX我还没有找到该运算符(operator)的任何文档。(我正在使用 NASM) 最佳答案 那实际上只是一个寄存器分隔符,而不是运算符。这意味着使用 DX 寄
我在哪里可以找到为 gmp-5.0.0 编写的程序的汇编代码我正在使用 UBUNTU 和 G++ 编译器..编译代码的命令是“g++ test.cc -o outp -lgmp” 实际上我想知道在 1
我是组装新手,我有一个关于如何表示负数的问题 我有三个 DWORDS 变量,比如说: result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式:result = i -
我想编写我的第一个汇编程序。我在论文上做了一些程序,但这是我第一次使用编译器。我正在使用 ideone .我的程序很简单, 翻译 A = 5 - A到 assembly NEG A ADD A, 5
程序集,masm 嘿,我写了宏来打印存储在 dane1 段中的 1 字节值。 我将值除以 16,然后将提醒推送到堆栈,直到值==0。然后我弹出提醒将它们转换为 ASCII 码,并打印它们。 有人可以看
我正在研究 nasm 的一个大学项目。唯一的问题是我无法生成 162 和 278 之间的偶数随机数。我尝试了很多算法,但似乎无法限制范围内的数字。 是否有一个小技巧或调整来获得所需的范围内的数字?目的
终于在无数次错误的漫长 session 之后,希望这是最后一个。 没有编译或运行时错误,只是一个逻辑错误。 编辑:(固定伪代码) 我的伪代码: first = 1; second = 1; thir
我知道在程序集r0中调用函数时,包含第一个参数,直到r3是第四个。我知道,当它超过四个时,将使用堆栈指针,但是我不太确定具体细节。 r0-r3仍然保持前四个,其余的进入堆栈吗?我正在看下面的程序集,试
我是一名优秀的程序员,十分优秀!