- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
这是复制(转换)无符号寄存器的指令:http://www.felixcloutier.com/x86/MOVZX.html
基本上,该指令具有8-> 16、8-> 32、8-> 64、16-> 32和16-> 64。
32-> 64转换在哪里?我必须使用签名版本吗?
如果是这样,如何将全64位用于无符号整数?
最佳答案
简短答案
如果您不能保证RDI的高位全部为零,请使用mov eax, edi
将EDI零扩展到RAX。参见:Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register?
最好使用不同的源/目标寄存器,因为在Intel和AMD CPU上均使用mov-elimination fails for mov eax,eax
。当转移到另一个寄存器时,不需要任何执行单元就可以实现零延迟。 (gcc显然不知道这一点,通常零扩展。)但是,不要花费额外的指令来实现这一目标。
长答案
使用32位源的movzx没有编码的机器代码原因
摘要:movzx和movsx的每个不同的源宽度都需要不同的操作码。目标宽度由前缀控制。由于mov
可以完成这项工作,因此movzx dst, r/m32
的新操作码将是多余的。
在设计AMD64汇编器语法时,AMD选择不使movzx rax, edx
作为mov eax, edx
的伪指令。这可能是一件好事,因为知道编写32位寄存器会将高字节清零对于为x86-64编写高效代码非常重要。
AMD64确实需要使用32位源操作数进行符号扩展的新操作码。他们出于某种原因将助记符命名为movsxd
,而不是将其作为movsx
助记符的第3个操作码。英特尔将它们全部记录在one ISA ref manual entry中。他们重新调整了32位模式下ARPL
的1字节操作码的用途,因此实际上movsxd
比8或16位源中的movsx
短1字节(假设您仍然需要REX前缀以扩展到64)位)。
不同的目标大小将相同的操作码用于不同的操作数大小1。 (66
或REX.W
前缀为16位或64位,而不是默认的32位。)例如movsx eax, bl
和movsx rax, bl
仅REX前缀有所不同;相同的操作码。 (movsx ax, bl
也相同,但是前缀为66,以使操作数大小为16位。)
在AMD64之前,不需要读取32位源的操作码,因为最大目标宽度是32位,并且“符号扩展”到相同大小只是一个副本。请注意,movsxd eax, eax
is legal but not recommended。您甚至可以使用66
前缀对其进行编码,以读取32位源并写入16位destination2。
不建议在64位模式下使用不带REX.W的MOVSXD。应该使用常规的MOV代替没有REX.W的MOVSXD。
可以使用cdq
完成32-> 64位符号扩展,以将EAX符号扩展到EDX:EAX(例如32位idiv
之前)。这是x86-64之前的唯一方法(当然,除了复制和使用算术右移之外,还必须广播符号位)。
但是,AMD64已经可以通过任何写入32位寄存器的指令免费将32从零扩展到64。 This avoids false dependencies用于乱序执行,这就是为什么AMD打破了8086/386的传统,即在编写部分寄存器时不影响高位字节的传统。 (Why doesn't GCC use partial registers?)
由于每个源宽度需要不同的操作码,因此没有任何前缀可以使两个movzx
操作码中的任何一个读取32位源。
有时您确实需要花费一条指令对某些内容进行零扩展。在小型函数的编译器输出中很常见,因为x86-64 SysV和Windows x64调用约定允许args和返回值中有大量垃圾。
像往常一样,询问编译器是否想知道如何在asm中执行某些操作,尤其是在没有看到所需指令的情况下。我在每个函数末尾都省略了ret
。
Source + asm from the Godbolt compiler explorer, for the System V calling convention (args in RDI, RSI, RDX, ...):
#include <stdint.h>
uint64_t zext(uint32_t a) { return a; }
uint64_t extract_low(uint64_t a) { return a & 0xFFFFFFFF; }
# both compile to
mov eax, edi
int use_as_index(int *p, unsigned a) { return p[a]; }
# gcc
mov esi, esi # missed optimization: mov same,same can't be eliminated on Intel
mov eax, DWORD PTR [rdi+rsi*4]
# clang
mov eax, esi # with signed int a, we'd get movsxd
mov eax, dword ptr [rdi + 4*rax]
uint64_t zext_load(uint32_t *p) { return *p; }
mov eax, DWORD PTR [rdi]
uint64_t zext_add_result(unsigned a, unsigned b) { return a+b; }
lea eax, [rdi+rsi]
lea eax, [edi+esi]
相比,它节省了一个字节,该字节需要67个地址大小的前缀,但每个输入都得到相同的结果。当然,
add edi, esi
将在RDI中产生零扩展的结果。
uint64_t zext_mul_result(unsigned a, unsigned b) { return a*b; }
# gcc8.1
mov eax, edi
imul eax, esi
# clang6.0
imul edi, esi
mov rax, rdi # silly: mov eax,edi would save a byte here
mov
的结果,释放
mov
消除所占用的微体系结构资源,并释放
increasing the success-rate of mov
-elimination (which isn't 100% on Sandybridge-family, unlike AMD Ryzen)。 GCC选择
mov
/
imul
是最好的。
mov
可能不在关键路径上(即,如果关键路径经过了未得到
mov
的输入) )。但是
mov
之后的
imul
取决于两个输入,因此它始终位于关键路径上。
unsigned
与
size_t
或
uint64_t
混合使用时,源代码很草率,则可能会迫使编译器发出指令以截断64位值。 (查看编译器的asm输出是捕获该错误并弄清楚如何调整源代码以使编译器保存指令的好方法。)
movswl
(符号扩展字-> long(dword)或
movzbl
等不同的助记符))可以从寄存器中推断出目标大小,例如
movzb %al, %ecx
,但是
won't assemble movz %al, %ecx
even though there's no ambiguity因此,它把
movzb
当作自己的助记符,通常的操作数大小后缀可以推断或显式表示,这意味着每个不同的操作码在AT&T语法中都有自己的助记符。
movsxd ax, [rsi]
的愚蠢计算机技巧:
movsxd eax, eax
或
movsxd ax, eax
,但是可以对其进行手动编码。
ndisasm
甚至都没有反汇编(只是
db 0x63
),但是GNU
objdump
可以反汇编。实际的CPU也会对其进行解码。我尝试在Skylake上只是为了确保:
; NASM source ; register value after stepi in GDB
mov rdx, 0x8081828384858687
movsxd rax, edx ; RAX = 0xffffffff84858687
db 0x63, 0xc2 ;movsxd eax, edx ; RAX = 0x0000000084858687
xor eax,eax ; RAX = 0
db 0x66, 0x63, 0xc2 ;movsxd ax, edx ; RAX = 0x0000000000008687
63 /r
MOVSXD r16, r/m16
,因此
movsxd ax, [unmapped_page - 2]
不会出错。 (但是它错误地记录了非REX格式在兼容/旧版模式下有效;实际上
0x63
在那里被解码为ARPL。这不是Intel手册中的第一个错误。)
mov r16, r/m16
或
mov r32, r/m32
相同的uop。或不!
movsxd eax,edx
(但不是
movsxd rax, edx
)对目标寄存器具有输出依赖性,就像它正在合并到目标中一样!包含
times 4
db 0x63, 0xc2 ; movsx eax, edx
的循环在每次迭代中以4个时钟运行(每个
movsxd
1个循环,因此有1个周期延迟)。微指令相当均匀地分布到所有4个整数ALU执行端口。具有
movsxd eax,edx
/
movsxd ebx,edx
/ 2个其他目标的循环在每次迭代中以〜1.4个时钟运行(如果使用普通的4x
mov eax, edx
或4x
movsxd rax, edx
,则略小于每个迭代前端瓶颈1.25个时钟)。在i7-6700k的Linux上使用
perf
计时。
movsxd eax, edx
会将RAX的高位清零,因此实际上并没有使用它正在等待的目标寄存器中的任何位,而是大概在内部对16位和32位进行了类似的处理,从而简化了解码,并简化了这种特殊情况编码的处理没有人应该使用。 16位格式始终必须实际上合并到目标中,因此它确实对输出reg有真正的依赖性。 (Skylake不会将全名寄存器单独重命名16位reg。)
4000c8: 66 63 c2 movsxd ax,edx
4000cb: 66 63 06 movsxd ax,DWORD PTR [rsi]
4000c8: 66 63 c2 movsxd ax,dx
4000cb: 66 63 06 movsxd ax,WORD PTR [rsi]
movslq
。因此,我想它会将其视为一个整体助记符,而不是具有
movsl
操作数大小的
q
指令。或这仅仅是因为没人关心气体不会聚集的特殊情况(它拒绝
movsll
并检查
movslq
的寄存器宽度)的结果。
section .bss
align 4096
resb 4096
unmapped_page:
; When built into a static executable, this page is followed by an unmapped page on my system,
; so I didn't have to do anything more complicated like call mmap
...
_start:
lea rsi, [unmapped_page-2]
db 0x66, 0x63, 0x06 ;movsxd ax, [rsi]. Runs without faulting on Skylake! Hardware only does a 2-byte load
o16 movsxd rax, dword [rsi] ; REX.W prefix takes precedence over o16 (0x66 prefix); this faults
mov eax, [rsi] ; definitely faults if [rsi+2] isn't readable
movsx al, ax
是不可能的:字节操作数大小需要单独的操作码。前缀仅在32(默认),16位(0x66)和长模式64位(REX.W)之间选择。从386开始,就可以使用
movs/zx ax, word [mem]
了,但是读取比目标更宽的源代码是x86-64中的一个特例,仅用于符号扩展。 (事实证明,16位目标编码实际上仅读取16位源。)
movzxd
而不是
movsxd
。我认为,将位域打包到更宽的寄存器中时,此设计的主要缺点是需要额外的说明。例如,在写有
shl rax,32
和
or rax, rdx
的
rdtsc
之后,
edx
/
eax
的自由零扩展很方便。如果是符号扩展名,则需要一条指令将
rdx
之前的
or
高字节清零。
addu
)要求其输入正确进行符号扩展,并产生符号扩展的输出。 (由于运行移位是特殊的,因此在运行传统的32位代码而没有意识到更宽的寄存器时,所有方法都适用。)
ADDU rd, rs, rt
(
from the MIPS III manual, page A-31)
if (NotWordValue(GPR[rs]) or NotWordValue(GPR[rt])) then UndefinedResult() endif
temp ←GPR[rs] + GPR[rt]
GPR[rd]← sign_extend(temp31..0)
addu
中无符号的U确实是一个错误的名词。除非确实希望
add
捕获有符号的溢出,否则也将其用于有符号的算术。)
DADDU
指令,它可以完成您所期望的。类似地,DDIV / DMULT / DSUBU和DSLL等移位。
s ← sa
temp ← GPR[rt] (31-s)..0 || 0 s
GPR[rd]← sign_extend(temp)
(a & 0x80000000) +- 12315
for int a
(使用
-fwrapv
(因此,编译器不能假定
a
是非负的,因为有符号溢出的UB))显示了用于PowerPC64的clang维护或重做符号扩展,然后对ccc
-target sparc64
进行“与”运算,然后进行“或”运算以确保只有低位32的右位被置1,再次保持符号扩展。将返回类型或arg类型更改为
long
或在AND掩码常量上添加
L
后缀会导致MIPS64和PowerPC64,有时甚至是SPARC64的代码有所不同。也许只有MIPS64实际上会在输入未正确符号扩展的32位指令上出错,而在其他情况下,这仅仅是软件调用约定要求。
w0..31
寄存器是
x0..31
的下半部分,并且指令有两种操作数大小。
dext
来从32位零扩展到64位。但是直到mips64r2才添加该指令。对于
-march=mips3
,用于无符号
return p[a]
的
a
必须使用两个双字移位(左移然后右移32位)以零扩展!它还需要一条额外的指令来对添加结果进行零扩展,即实现从无符号到
uint64_t
的转换。
关于assembly - MOVZX缺少32位寄存器到64位寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51387571/
我正在尝试在 map 上绘制一些疾病事件数据的位置。 我用它来导入数据: ByTown% addProviderTiles("CartoDB.Positron")%>% addPolygons
我有一个文件调用 find.js,我使用 node find.js 运行,我的节点是版本 10 我不知道为什么我无法使用 async await。 const axios = require("axi
我有一个项目作为引用添加到 System.Web。 但是,它似乎无法获取 HttpContext。这样做: Imports System.Web _ApplicationBase = HttpCont
在互联网上找到这段代码,出于某种原因它缺少 while 循环逻辑“while(i....)”,虽然我找到了 PigLatin* 问题的其他可行解决方案,但我真的很想了解这个正在工作。 *PigLati
我工作了一整天来运行 Xampp 并在其上安装 TYPO3。现在我登录到后端,但没有显示许多管理模块,例如模板、访问等。 - 一定是我做错了什么,但我不知道。 these are the module
你好 我有编译这个问题 \begin{equation} J = \sum_{j=1}^{C} \end{equation} 我不断收到错误 missing $ inserted 这很奇怪,因
我正在尝试使用 SQLite CLI,但无法获得 generate_series功能来工作。我可以按照文档中的建议使用递归 CTE 对其进行模拟,但我似乎无法获得该链接中的任何示例。这是我的 sess
我目前正在开发我想要的软件,而软件正在安装,它可以在后台为软件创建 native 图像。 我正在考虑使用 NGEN 并将进程优先级设置为低,因为我不希望它消耗 100% CPU。但是我发现我的计算机上
我想使用 Xcodes Instruments 进行 UI 自动化测试。但似乎缺少“自动化”。我怎样才能添加这个? 最佳答案 如果您想使用自动化仪器,请使用 Xcode 7.3。 Apple 在 Xc
我目前在 JS 开发中迈出了一小步,并编写了以下链接添加器: const button = document.getElementById('button') const listdiv = docu
此代码有什么问题: NSError *error = nil; [SFHFKeychainUtils deleteItemForUsername:@"IAPNoob01" andServiceName
出于某种原因,在安装和配置(我认为)一切之后,com.adobe.utils.AGALMiniAssembler 不见了,其他一切正常。 我认为我已尽一切努力让孵化器正常工作,但显然我错过了一步。 如
我有一个名为 new 的方法。调用 new 时,我传递了一个参数,但是当我运行应用程序时,出现没有参数或参数为空的错误。 StepReader.pm package StepReader; use s
安装 gtk 1.2(包名 gtk1)和 macports chokes 在最终的 make 中,在 libintl.h 的第 440 行。 extern locale_t libintl_newlo
我用按钮创建表格。 这是javascript代码: function layersListTable(layers) { var content =''; $.each($(layer
我在使用此 javascript 时遇到此错误,任何人都可以帮我弄清楚我做错了什么吗? $(this).prepend('Check availability »'); 它给我错误 mis
我有一个独立的工具链 NDK13b、api19、llvm 3.8 编译器、arm 32 位、带有 libcpp(llvm C++ 库) 我想避免依赖 libgcc,所以我构建了 compiler-rt
我按照一些教程使用 phonegap 的条形码扫描器插件。但是当我从现有源创建一个新的 android 项目来创建条码库时 (step 6 in this page)我收到错误:“AndroidMan
我现在尝试在 Eclipse 中打开我的布局 xml 文件。我只得到错误 No XML content. Please add a root view or layout to your docume
我的 android-sdk-windows\tools 目录中缺少层次结构查看器工具。 工具链接: http://developer.android.com/guide/developing/too
我是一名优秀的程序员,十分优秀!