x86 中哪些 MOV 指令未使用或最少使用,可用于自定义 MOV 扩展
which MOV instructions in the x86 are not used or the least used, and can be used for a custom MOV extension
我正在 gem5 模拟器中对 X86 架构中的自定义 MOV 指令进行建模,为了测试它在模拟器上的实现,我需要使用内联汇编来编译我的 C 代码以创建一个二进制文件。但由于它是 GCC 编译器尚未实现的自定义指令,因此编译器会抛出错误。我知道一种方法是扩展 GCC 编译器以接受我的自定义 X86 指令,但我不想这样做,因为它更耗时(但以后会这样做)。
作为临时 hack(只是为了检查我的实施是否值得)。我想在模拟器中更改其基础 "micro ops" 的同时编辑已经存在的 MOV 指令,以欺骗 GCC 接受我的 "custom" 指令并进行编译。
因为它们有许多类型的 MOV 指令,可在 x86 架构中使用。因为它们是86架构中的各种MOV指令reference。
因此我的问题是,哪个 MOV 指令使用最少,我可以编辑其底层微操作。假设我的工作量只包括整数,即很可能不会使用 xmm 和 mmx 寄存器,并且我的指令反映了 MOV 指令的相同实现。
您最好的选择是常规 mov
,带有 GCC 永远不会自行发出的前缀。即创建一个新的 mov
编码,在任何其他 mov
前面包含一个强制性前缀。比如 lzcnt
是 rep bsr
.
或者如果你正在修改 GCC 和 as
,你可以添加一个新的助记符,它只使用 otherwise-invalid(在 64 位模式下)memory-source 的单字节操作码, memory-dest,以及 mov
的 immediate-source 版本。 AMD64 释放了几个操作码,包括像 AAM 这样的 BCD 指令,以及 push/pop 大多数段寄存器。 (x86-64 仍然可以 mov
to/from Sreg,但每个方向只有 1 个操作码,而不是每个 Sreg 2 个用于推送 ds/pop ds 等)
Assuming my workload just includes integers i.e. most probably wont be using the xmm and mmx registers
XMM 的错误假设:GCC 积极使用 16 字节 movaps
/ movups
而不是一次复制 4 或 8 字节的结构。作为 small known-length memcpy
或 struct / array init 的内联扩展的一部分,在标量整数代码中找到矢量 mov 指令并不少见。此外,这些 mov
指令至少有 2 个字节的操作码(SSE1 0F 28 movaps
,因此普通 mov
前面的前缀与您的想法相同)。
但是,关于 MMX 规则,您是对的。我认为现代 GCC 永远不会发出 movq mm0, mm1
或根本不使用 MMX,除非您使用 MMX 内在函数。绝对不是针对 64 位代码。
此外 mov
to/from 控制寄存器 (0f 21/23 /r
) or debug registers (0f 20/22 /r
) 都是 mov
助记符,但 gcc 绝对不会单独发出任何一个。仅适用于 GP 寄存器操作数作为非调试或控制寄存器的操作数。所以这在技术上就是你标题问题的答案,但可能不是你真正想要的。
GCC 不解析其内联 asm 模板字符串,它只是将其包含在其 asm 文本输出中以在替换 %number
操作数后提供给汇编程序。所以 GCC 本身并不是使用内联 asm 发出任意 asm 文本的障碍。
并且您可以使用 .byte
发出任意机器代码。
也许一个好的选择是使用 0E
字节作为您将要进行 GEM 解码的特殊 mov
编码的前缀。 0E
is push CS
in 32-bit mode,64位模式下无效。 GCC 也永远不会发出。
或者只是一个 F2 repne
前缀; GCC 永远不会在 mov
操作码(不适用的地方)前发出 repne
,只会发出 movs
。 (F3 rep
/ repe
在 memory-destination 指令上使用时表示 xrelease,所以不要使用它。https://www.felixcloutier.com/x86/xacquire:xrelease 表示 F2 repne 是与 [ 一起使用时的 xacquire 前缀=44=]ed 指令,它不包含 mov
到内存中,所以它会被默默地忽略。)
像往常一样,不适用的前缀没有记录在案的行为,但实际上 CPU 不理解 rep
/ repne
的人会忽略它。一些未来的 CPU 可能会理解它意味着一些特殊的东西,而这正是你在 GEM.
上所做的
选择 .byte 0x0e;
而不是 repne;
可能是更好的选择,如果你想 防止不小心将这些前缀留在你 运行 的构建中CPU。 (在 64 位模式下它会 #UD -> SIGILL,或者在 32 位模式下通常会因为弄乱堆栈而崩溃。)但是如果你 do 希望能够 运行 真正的 CPU 上完全相同的二进制文件,具有相同的代码对齐和所有内容,然后忽略 REP 前缀是理想的。
在标准 mov
指令前使用前缀的优点是让汇编程序为您编码操作数:
template<class T>
void fancymov(T& dst, T src) {
// fixme: imm -> mem needs a size suffix, defeating template
// unless you use Intel-syntax where the operand includes "dword ptr"
asm("repne; movl %1, %0"
#if 1
: "=m"(dst)
: "ri" (src)
#else
: "=g,r"(dst)
: "ri,rmi" (src)
#endif
: // no clobbers
);
}
void test(int *dst, long src) {
fancymov(*dst, (int)src);
fancymov(dst[1], 123);
}
(Multi-alternative 约束让编译器选择 reg/mem 目标或 reg/mem 源。实际上它更喜欢寄存器目标,即使这将花费另一条指令来执行它自己的存储,所以很糟糕。)
On the Godbolt compiler explorer,对于只允许memory-destination的版本:
test(int*, long):
repne; movl %esi, (%rdi) # F2 E9 37
repne; movl 3, 4(%rdi) # F2 C7 47 04 7B 00 00 00
ret
如果您希望它可用于加载,我认为您必须制作该函数的 2 个单独版本并在适当的情况下手动使用加载版本或存储版本,因为 GCC 似乎想要使用 reg ,尽可能注册。
或允许寄存器输出的版本(或另一个版本 returns 结果为 T
,参见 Godbolt link):
test2(int*, long):
repne; mov %esi, %esi
repne; mov 3, %eax
movl %esi, (%rdi)
movl %eax, 4(%rdi)
ret
我正在 gem5 模拟器中对 X86 架构中的自定义 MOV 指令进行建模,为了测试它在模拟器上的实现,我需要使用内联汇编来编译我的 C 代码以创建一个二进制文件。但由于它是 GCC 编译器尚未实现的自定义指令,因此编译器会抛出错误。我知道一种方法是扩展 GCC 编译器以接受我的自定义 X86 指令,但我不想这样做,因为它更耗时(但以后会这样做)。
作为临时 hack(只是为了检查我的实施是否值得)。我想在模拟器中更改其基础 "micro ops" 的同时编辑已经存在的 MOV 指令,以欺骗 GCC 接受我的 "custom" 指令并进行编译。
因为它们有许多类型的 MOV 指令,可在 x86 架构中使用。因为它们是86架构中的各种MOV指令reference。
因此我的问题是,哪个 MOV 指令使用最少,我可以编辑其底层微操作。假设我的工作量只包括整数,即很可能不会使用 xmm 和 mmx 寄存器,并且我的指令反映了 MOV 指令的相同实现。
您最好的选择是常规 mov
,带有 GCC 永远不会自行发出的前缀。即创建一个新的 mov
编码,在任何其他 mov
前面包含一个强制性前缀。比如 lzcnt
是 rep bsr
.
或者如果你正在修改 GCC 和 as
,你可以添加一个新的助记符,它只使用 otherwise-invalid(在 64 位模式下)memory-source 的单字节操作码, memory-dest,以及 mov
的 immediate-source 版本。 AMD64 释放了几个操作码,包括像 AAM 这样的 BCD 指令,以及 push/pop 大多数段寄存器。 (x86-64 仍然可以 mov
to/from Sreg,但每个方向只有 1 个操作码,而不是每个 Sreg 2 个用于推送 ds/pop ds 等)
Assuming my workload just includes integers i.e. most probably wont be using the xmm and mmx registers
XMM 的错误假设:GCC 积极使用 16 字节 movaps
/ movups
而不是一次复制 4 或 8 字节的结构。作为 small known-length memcpy
或 struct / array init 的内联扩展的一部分,在标量整数代码中找到矢量 mov 指令并不少见。此外,这些 mov
指令至少有 2 个字节的操作码(SSE1 0F 28 movaps
,因此普通 mov
前面的前缀与您的想法相同)。
但是,关于 MMX 规则,您是对的。我认为现代 GCC 永远不会发出 movq mm0, mm1
或根本不使用 MMX,除非您使用 MMX 内在函数。绝对不是针对 64 位代码。
此外 mov
to/from 控制寄存器 (0f 21/23 /r
) or debug registers (0f 20/22 /r
) 都是 mov
助记符,但 gcc 绝对不会单独发出任何一个。仅适用于 GP 寄存器操作数作为非调试或控制寄存器的操作数。所以这在技术上就是你标题问题的答案,但可能不是你真正想要的。
GCC 不解析其内联 asm 模板字符串,它只是将其包含在其 asm 文本输出中以在替换 %number
操作数后提供给汇编程序。所以 GCC 本身并不是使用内联 asm 发出任意 asm 文本的障碍。
并且您可以使用 .byte
发出任意机器代码。
也许一个好的选择是使用 0E
字节作为您将要进行 GEM 解码的特殊 mov
编码的前缀。 0E
is push CS
in 32-bit mode,64位模式下无效。 GCC 也永远不会发出。
或者只是一个 F2 repne
前缀; GCC 永远不会在 mov
操作码(不适用的地方)前发出 repne
,只会发出 movs
。 (F3 rep
/ repe
在 memory-destination 指令上使用时表示 xrelease,所以不要使用它。https://www.felixcloutier.com/x86/xacquire:xrelease 表示 F2 repne 是与 [ 一起使用时的 xacquire 前缀=44=]ed 指令,它不包含 mov
到内存中,所以它会被默默地忽略。)
像往常一样,不适用的前缀没有记录在案的行为,但实际上 CPU 不理解 rep
/ repne
的人会忽略它。一些未来的 CPU 可能会理解它意味着一些特殊的东西,而这正是你在 GEM.
选择 .byte 0x0e;
而不是 repne;
可能是更好的选择,如果你想 防止不小心将这些前缀留在你 运行 的构建中CPU。 (在 64 位模式下它会 #UD -> SIGILL,或者在 32 位模式下通常会因为弄乱堆栈而崩溃。)但是如果你 do 希望能够 运行 真正的 CPU 上完全相同的二进制文件,具有相同的代码对齐和所有内容,然后忽略 REP 前缀是理想的。
在标准 mov
指令前使用前缀的优点是让汇编程序为您编码操作数:
template<class T>
void fancymov(T& dst, T src) {
// fixme: imm -> mem needs a size suffix, defeating template
// unless you use Intel-syntax where the operand includes "dword ptr"
asm("repne; movl %1, %0"
#if 1
: "=m"(dst)
: "ri" (src)
#else
: "=g,r"(dst)
: "ri,rmi" (src)
#endif
: // no clobbers
);
}
void test(int *dst, long src) {
fancymov(*dst, (int)src);
fancymov(dst[1], 123);
}
(Multi-alternative 约束让编译器选择 reg/mem 目标或 reg/mem 源。实际上它更喜欢寄存器目标,即使这将花费另一条指令来执行它自己的存储,所以很糟糕。)
On the Godbolt compiler explorer,对于只允许memory-destination的版本:
test(int*, long):
repne; movl %esi, (%rdi) # F2 E9 37
repne; movl 3, 4(%rdi) # F2 C7 47 04 7B 00 00 00
ret
如果您希望它可用于加载,我认为您必须制作该函数的 2 个单独版本并在适当的情况下手动使用加载版本或存储版本,因为 GCC 似乎想要使用 reg ,尽可能注册。
或允许寄存器输出的版本(或另一个版本 returns 结果为 T
,参见 Godbolt link):
test2(int*, long):
repne; mov %esi, %esi
repne; mov 3, %eax
movl %esi, (%rdi)
movl %eax, 4(%rdi)
ret