new*_*www 2 x86 gcc machine-code inline-assembly gem5
我正在 gem5 模拟器中的 X86 架构中对自定义 MOV 指令进行建模,为了在模拟器上测试其实现,我需要使用内联汇编来编译 C 代码以创建二进制文件。但由于它是一条自定义指令,尚未在 GCC 编译器中实现,因此编译器会抛出错误。我知道一种方法是扩展 GCC 编译器以接受我的自定义 X86 指令,但我不想这样做,因为它更耗时(但稍后会这样做)。
作为临时黑客(只是为了检查我的实现是否值得)。我想编辑已经存在的 MOV 指令,同时更改模拟器中的底层“微操作”,以便欺骗 GCC 接受我的“自定义”指令并进行编译。
因为 x86 架构中有多种类型的 MOV 指令。因为它们是86架构参考中的各种MOV指令。
因此,我的问题是,哪条 MOV 指令使用最少,我可以编辑其底层微操作。假设我的工作负载仅包括整数,即很可能不会使用 xmm 和 mmx 寄存器,并且我的指令反映了 MOV 指令的相同实现。
最好的选择是mov使用常规前缀,GCC 永远不会自行发出。即创建一个新的mov编码,其中在任何其他mov. 就像怎么lzcnt样rep bsr。
或者,如果您正在修改 GCC 和as,则可以添加一个新的助记符,该助记符仅使用 的内存源、内存目标和直接源版本的无效(在 64 位模式下)单字节操作码mov。AMD64 释放了多个操作码,包括 AAM 等 BCD 指令,以及压入/弹出大多数段寄存器。(x86-64 仍然可以mov往返 Sreg,但每个方向只有 1 个操作码,而不是每个 Sreg 有 2 个操作码用于推送 ds/弹出 ds 等。)
假设我的工作负载仅包括整数,即很可能不会使用 xmm 和 mmx 寄存器
XMM 的错误假设:GCC 积极使用 16 字节movaps/movups而不是一次复制 4 或 8 字节的结构。在标量整数代码中找到向量 mov 指令作为小已知长度或结构/数组 init 的内联扩展的一部分并不罕见memcpy。此外,这些mov指令至少有 2 字节操作码( SSE1 0F 28movaps,因此 plain 前面的前缀mov与您的想法的大小相同)。
不过,您对 MMX 规则的看法是正确的。我认为现代 GCC 根本不会发出movq mm0, mm1或使用 MMX,除非您使用 MMX 内在函数。当针对 64 位代码时绝对不行。
另外mov,往返控制寄存器 ( 0f 21/23 /r) 或调试寄存器 ( 0f 20/22 /r) 都是助记符mov,但 gcc 绝对不会自行发出任何一个。仅适用于 GP 寄存器操作数作为非调试或控制寄存器的操作数。因此,从技术上讲,这就是您标题问题的答案,但可能不是您真正想要的。
GCC 不会解析其内联 asm 模板字符串,它只是将其包含在其 asm 文本输出中,以在替换%number操作数后提供给汇编器。因此,GCC 本身并不是使用内联 asm 发出任意 asm 文本的障碍。
您可以使用它.byte来发出任意机器代码。
也许一个好的选择是使用一个0E字节作为mov您要专门进行 GEM 解码的特殊编码的前缀。 32位模式下0E有效push CS,64位模式下无效。GCC 也永远不会发出。
或者只是 F2repne前缀;GCC 永远不会repne在操作码前面发出mov(它不适用的地方),只会发出movs. (F3 rep/repe表示在内存目标指令上使用时 xrelease,因此不要使用它。https: //www.felixcloutier.com/x86/xacquire:xrelease 表示 F2 repne 与locked 指令一起使用时是 xacquire 前缀,这不包含mov到内存中,因此它将被默默地忽略。)
像往常一样,不适用的前缀没有记录的行为,但实际上,不理解rep/ 的CPUrepne会忽略它。未来的某些 CPU 可能会将其理解为特殊的含义,而这正是您使用 GEM 所做的事情。
如果您想防止在真实 CPU 上运行的构建中意外留下这些前缀,那么选择 代替.byte 0x0e;可能是更好的选择。(它会在 64 位模式下 #UD -> SIGILL,或者通常会因在 32 位模式下搞乱堆栈而崩溃。)但是,如果您确实希望能够在真正的 CPU 上运行完全相同的二进制文件,请使用相同的代码对齐和一切,那么忽略 REP 前缀是理想的。repne;
在标准指令前面使用前缀的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);
}
Run Code Online (Sandbox Code Playgroud)
(多重替代约束让编译器选择 reg/mem 目标或 reg/mem 源。实际上,它更喜欢寄存器目标,即使这会花费另一条指令来执行自己的存储,所以这很糟糕。)
在 Godbolt 编译器资源管理器上,对于仅允许内存目标的版本:
test(int*, long):
repne; movl %esi, (%rdi) # F2 E9 37
repne; movl $123, 4(%rdi) # F2 C7 47 04 7B 00 00 00
ret
Run Code Online (Sandbox Code Playgroud)
如果你希望它可用于加载,我认为你必须制作该函数的 2 个单独版本,并在适当的情况下手动使用加载版本或存储版本,因为 GCC 似乎希望尽可能使用 reg,reg 。
或者使用允许寄存器输出的版本(或以 a 形式返回结果的另一个版本T,请参阅 Godbolt 链接):
test2(int*, long):
repne; mov %esi, %esi
repne; mov $123, %eax
movl %esi, (%rdi)
movl %eax, 4(%rdi)
ret
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1044 次 |
| 最近记录: |