call
强迫你假设大多数寄存器都被破坏的开销是相当高的。为了获得高性能,您需要手动将函数内联到 asm 中,这样您就可以完全优化所有内容。
让编译器发出一个独立的定义并调用它应该只考虑用于不是性能关键的代码。你没有说你在 asm 中写了什么,或者为什么,但我假设它对性能至关重要。否则,您只需用 C 语言编写它(我猜,对于任何特殊指令,都使用内联汇编?)。
如果您不想手动内联,并且想在循环中使用这些小的内联 C 函数,那么用 C 编写整个内容可能会获得更好的性能。这将使编译器优化更多代码.
用于 x86-64 的 register-arg 调用约定很好,但是有很多寄存器是 call-clobbered 的,所以计算中间的调用会阻止你将尽可能多的数据保存在寄存器中。
无论如何,汇编代码可以使用内联吗?我的意思是在 .S 文件中,而不是内联汇编。
不,没有与 inline-asm 相反的语法。如果有,它将类似于:您告诉编译器输入在哪些寄存器中,您希望在哪些寄存器中输出,以及允许破坏哪些寄存器。
如果没有真正理解手写 asm 或将其视为源代码然后发出优化版本的编译器,则手写 asm 和编译器输出之间的公共子表达式消除和其他重要优化是不可能的整件事情。
编译器输出到 asm 的最佳内联通常需要对 asm 进行调整,这就是为什么没有任何程序可以做到这一点的原因。
有没有更好的方法来实现我一直在尝试做的事情?
现在您已经在评论中解释了您的目标是什么:在 C 中为您想要使用的特殊指令制作小型包装器,而不是相反。
#include <stdint.h>
struct __attribute__((packed)) lgdt_arg {
uint16_t limit;
void * base; // FIXME: always 64bit in long mode, including the x32 ABI where pointers and uintptr_t are 32bit.
// In 16bit mode, base is 24bit (not 32), so I guess be careful with that too
// you could just make this a uint64_t, since x86 is little-endian.
// The trailing bytes don't matter since the instruction just uses a pointer to the struct.
};
inline void lgdt (const struct lgdt_arg *p) {
asm volatile ("lgdt %0" : : "m"(*p) : "memory");
}
// Or this kind of construct sometimes gets used to make doubly sure compile-time reordering doesn't happen:
inline void lgdt_v2 (struct lgdt_arg *p) {
asm volatile ("lgdt %0" : "+m"(*(volatile struct lgdt_arg *)p) :: "memory");
}
// that puts the asm statement into the dependency chain of things affecting the contents of the pointed-to struct, so the compiler is forced to order it correctly.
void set_gdt(unsigned size, char *table) {
struct lgdt_arg tmp = { size, table };
lgdt (&tmp);
}
set_gdt
编译为(-O3
godbolt 上的 gcc 5.3):
movw %di, -24(%rsp)
movq %rsi, -22(%rsp)
lgdt -24(%rsp)
ret
我从来没有写过涉及lgdt
. 像我一样使用“内存”破坏器可能是一个好主意,以确保在编译时不会重新排序任何加载/存储。这将确保它指向的 GDT 在运行之前完全初始化LGDT
。(对于 相同LIDT
)。编译器可能会注意到它base
为内联汇编提供了对 GDT 的引用,并确保其内容是同步的,但我不确定。在这里只使用“内存”破坏器应该几乎没有缺点。
Linux(内核)在所有地方都使用这种包装器围绕一两条指令,在 asm 中编写尽可能少的代码。如果你愿意,可以在那里寻找灵感。
回复:你的评论:是的,你会想用 asm 编写你的引导扇区,也许还有一些其他的 16 位代码,因为 gcc 的 -m16 代码很傻(基本上仍然是 32 位代码)。
不,除了手动之外,没有办法将 C 编译器输出内联到 asm 中。这是正常和预期的,出于同样的原因,没有优化组装的程序。(即读取 asm 源、优化、编写不同的 asm 源)。
想想这样一个程序必须做什么:它必须理解手写 asm 才能知道它可以改变什么而不破坏手写 asm。Asm 作为一种源语言并没有给优化器太多的工作。