gpt4 book ai didi

c++ - 跨平台汇编 ((x64 || x86) && (Microsoft x64 || SystemV))

转载 作者:行者123 更新时间:2023-11-28 04:23:51 27 4
gpt4 key购买 nike

我正在尝试编写一些代码以了解有关汇编和 JIT 编译器等内容的更多信息。到目前为止,我已经能够想出一个 XOR 函数,理论上应该可以在 Windows 和 Linux 环境中的 x86 或 x64 机器上运行。

假设我理解正确,[RE]AX 寄存器用于保存整数返回值,而 [RE]DX 是用于传递的可用寄存器之一函数之间的整数。我选择不严格遵循 ABI 并使用 [RE]AX 传递第一个参数,因为它节省了 MOV 指令而不影响结果。

是否有更好(更优雅或更高效)的方式来发出跨平台程序集,或者我在开发此程序时是否犯过任何错误?

#include <cstdint>
#include <iostream>

template<typename TInput>
static auto Xor(TInput const highPart, TInput const lowPart) {
constexpr bool is16Bit = (std::is_same<TInput, int16_t>::value || std::is_same<TInput, uint16_t>::value);
constexpr bool is32Bit = (std::is_same<TInput, int32_t>::value || std::is_same<TInput, uint32_t>::value);
static_assert(is16Bit || is32Bit, "type must be a member of the type family: [u]int{16, 32}_t");

if constexpr (is16Bit) {
uint16_t result;

#if (defined(__linux__) || defined(__unix__) || defined(_WIN32))
asm volatile ("xorw %%dx, %%ax;" : "=a" (result) : "a" (highPart), "d" (lowPart));
#else
#error "Unsupported platform detected."
#endif

return result;
}
else if constexpr (is32Bit) {
uint32_t result;

#if (defined(__linux__) || defined(__unix__) || defined(_WIN32))
asm volatile ("xorl %%edx, %%eax;" : "=a" (result) : "a" (highPart), "d" (lowPart));
#else
#error "Unsupported platform detected."
#endif

return result;
}
}

#define HIGH_PART 4;
#define LOW_PART 8;

int main() {
int16_t const a = HIGH_PART;
int16_t const b = LOW_PART;
int16_t const c = Xor(a, b);

uint32_t const x = HIGH_PART;
uint32_t const y = LOW_PART;
uint32_t const z = Xor(x, y);

std::cout << c << "\n";
std::cout << z << "\n";
getchar();

return 0;
}

以下是如何改进的示例;通过“提升”result 变量和 if defined(...) 检查高于 constexpr 检查,我们可以使事情更通用。

template<typename T>
static auto Xor(T const highPart, T const lowPart) {
constexpr bool is16Bit = (std::is_same<T, int16_t>::value || std::is_same<T, uint16_t>::value);
constexpr bool is32Bit = (std::is_same<T, int32_t>::value || std::is_same<T, uint32_t>::value);
static_assert(is16Bit || is32Bit, "type must be a member of the type family: [u]int{16, 32}_t");

#if !(defined(__linux__) || defined(__unix__) || defined(_WIN32))
#error "Unsupported platform detected."
#endif

T result;

if constexpr (is16Bit) {
asm volatile ("xorw %%dx, %%ax;" : "=a" (result) : "a" (highPart), "d" (lowPart));
}
else if constexpr (is32Bit) {
asm volatile ("xorl %%edx, %%eax;" : "=a" (result) : "a" (highPart), "d" (lowPart));
}

return result;
}

最佳答案

您不能让编译器在 64 位模式下在 EAX/RAX 中传递函数 arg。在 32 位模式下,您可以使用 gcc "regparm"调用约定,如 __attribute__((regparm(3))) int my_func(int,int); 在 EAX、ECX 中传递参数, EDX 的顺序。 (所以编译器将在内联 asm 之前需要一个 mov,它在 EAX 中有一个函数 arg)。

或者您可以使用 __attribute__((sysv_abi)) 声明您的函数以始终使用 SysV ABI,即使在 Windows 上编译时也是如此。但这只有在所有调用者都由 GCC/clang/ICC 而不是 MSVC 编译时才有效。在 32 位模式下更糟; i386 System V 调用约定很糟糕:在堆栈上传递所有参数,并且在 edx:eax 中仅返回 int64_t,而不是 2 成员 64 位结构。

调用 sysv_abi 函数可能也会调用 ms_abi 函数来保存/恢复所有 xmm6..15,除非 sysv_abi 函数调用可以内联并优化掉。因此,如果函数尚未大量使用 XMM regs 并保存/恢复其中的大部分,那么总的来说这可能是一个糟糕的计划。


使用固定寄存器输入/输出约束通常没有用,除非您使用带有隐式寄存器的指令(如 cl 中的移位计数,如果您不能使用 BMI2 shlx/shr​​x).

让编译器使用"r""+r" 约束进行寄存器分配。 (或 "=r""0" 匹配约束)因此无论值在哪里,您的函数都可以有效地内联。对于可以是寄存器或 32 位立即数的输入,也可以使用 "re"。甚至 "rem" 也可以是内存输入。但是,如果您重复使用输入,最好让编译器在 asm 之前为您加载它。

另见 https://stackoverflow.com/tags/inline-assembly/info

对寄存器分配进行硬编码部分违背了使用内联 asm 而不是编译器必须调用而不是内联的独立 asm 函数的目的。

查看编译器为您的代码生成的 asm,了解它生成的周围代码,以及它如何通过选择操作数填充模板。

另请注意,"r" 为 16 位类型选择 16 位寄存器,为 32 位类型选择 32 位寄存器,因此所有这些调整类型大小的东西基本上都是不必要的。 (尽管取决于输入的写入方式,使用 32 位 xor 可能比 16 位 xor 更好,如果稍后读取完整的 32 位或 64 位寄存器,可能会避免部分寄存器停顿。但是如果您的输入寄存器是用 16 位操作数大小编写的,那么在 P6 系列 CPU 上,一个 32 位异或将创建一个部分寄存器停顿。)您可以覆盖为 "xor %0 填充的大小" 模板替换为 "%k0" 32 位大小等。参见 x86 Operand Modifiers in the GCC manual .

关于c++ - 跨平台汇编 ((x64 || x86) && (Microsoft x64 || SystemV)),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54872021/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com