我正在用gem5模拟器在X86架构中对自定义MOV指令进行建模,以测试其在模拟器上的实现,我需要使用内联汇编来编译我的C代码以创建一个二进制文件。但是由于它是GCC编译器中尚未实现的自定义指令,因此编译器将抛出错误。我知道一种方法是扩展GCC编译器以接受我的自定义X86指令,但是我不想这样做,因为它比较耗时(但是以后会这样做)。
作为一个临时的技巧(只是检查我的实现是否值得)。我想在模拟器中更改其基础的“微操作”时编辑已经是MOV的指令,以欺骗GCC接受我的“自定义”指令并进行编译。
因为它们是x86体系结构中可用的许多类型的MOV指令。由于它们是86体系结构reference中的各种MOV指令。
因此,我的问题是,哪种MOV指令使用最少,我可以编辑其底层微操作。假设我的工作量仅包含整数,即很可能不会使用xmm和mmx寄存器,并且我的指令镜像了MOV指令的相同实现。
mov
,其前缀是GCC永远不会自己发出的。%number
操作数之后将其提供给汇编器。因此,GCC本身并不是使用内联汇编发送任意汇编文本的障碍。并且您可以使用.byte
发出任意机器代码。
也许一个不错的选择是将0E
字节用作特殊的mov
编码的前缀,而您将要特别进行GEM解码。 0E
is push CS
in 32-bit mode,在64位模式下无效。 GCC永远也不会发射。
或者只是一个F2 0E
前缀; GCC永远不会在push CS
操作码(不适用的地方)前仅发出repne
。 (F3repne
/ mov
在用于内存目标指令时表示xrelease,因此请勿使用。movs
说F2 repne是与rep
ed指令一起使用时的xacquire前缀,但不将repe
包含到内存中,以便在那里被静默忽略。)
和往常一样,不适用的前缀没有记录的行为,但是实际上,不理解https://www.felixcloutier.com/x86/xacquire:xrelease / lock
的CPU会忽略它。将来的某些CPU可能会理解它的特殊含义,而这正是您使用GEM所做的。
如果您想
防止在实际CPU上运行的版本中意外保留这些前缀,则选择 在标准mov
而不是rep
是更好的选择。> (它会在64位模式下#UD-> SIGILL,或者通常会因在32位模式下弄乱堆栈而崩溃。)但是,如果您do希望能够在实际环境中运行完全相同的二进制文件CPU,具有相同的代码对齐方式以及所有内容,因此理想的是忽略REP前缀。repne
指令之前使用前缀的优点是让汇编程序为您编码操作数:.byte 0x0e;
((多种替代约束使编译器可以选择reg / mem目标或reg / mem源。实际上,它更喜欢寄存器目标,即使这样做会使它花费另一条指令来进行自己的存储,也很糟。)
repne;
,对于仅允许内存目标的版本:
mov
如果您希望此功能可用于加载,我认为您必须制作该函数的2个独立版本,并在适当的情况下手动使用加载版本或存储版本,因为GCC似乎想在任何时候使用reg,reg可以。
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);
}
返回的版本,请参见Godbolt链接):