我正在尝试在汇编代码中使用offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define DEFINE(sym, val) asm volatile("\n->" #sym " %0 " #val : : "i" (val))
并说结构是
struct mystruct {
int a;
int b;
int c;
}
在我的汇编代码中,我必须这样做 SUB sp,sp,# -
如何声明宏
答案 0 :(得分:2)
你还没有说你为哪个处理器生成代码...我假设它是一台 RISC 机器,因为你的减法指令有 3 个操作数,但你没有说哪个。我将向您展示这对 x86 的影响,因为我知道答案是正确的。
我还假设您使用 asm volatile
表示您使用的是遵循 gcc's standard 的编译器。
无论如何,假设你有一个这样的结构:
struct mystruct {
unsigned char a;
const char *b;
int c;
};
int a_or_c(mystruct *str) {
int a_val = str->a;
return a_val & 1 ? str->c : (a_val >> 1);
}
编译器并没有为此生成特别好的代码 - 它有 7 条指令,我们可以做得更好,因为我们知道一条指令可以同时测试“& 1”和右移。
要指定寄存器,请使用 r
表示“寄存器”,我确定您知道。要指定输入/输出寄存器,请使用 +r
。要指定像结构偏移这样的常量,请使用 i
表示“立即”。那么汇编语法将是,例如:
int a_or_c_asm(mystruct *str) {
int a_val = str->a;
asm("shr $1,%0\n\t"
"cmovcl %c2(%1), %0"
: "+r"(a_val)
: "r"(str), "i"(offsetof(mystruct, c))
: "memory" // tell the compiler that our code reads from memory
);
return a_val;
}
这里的技巧是你必须使用 %c2
而不仅仅是 %2
来让内联汇编器输出一个 2
而不是 $2
,因为 x86 汇编器使用寻址模式中偏移量的语法与立即操作数不同。 x86 中的减法指令如下所示,例如:
asm("subq %0, %%rsp"
"... other instructions ..."
: // no output operands
: "i"(offsetof(mystruct, c)));
// expands to subq $16, %rsp for x86-64
根据您的评论,我假设您需要 ARM32 语法。为此,您的减法指令如下所示:
asm("sub sp, %0"
"... other instructions ..."
: // no output operands
: "i"(offsetof(mystruct, c)));
// expands to sub sp, #8 for ARM
(Obligatory Godbolt: 为 ARM 正确编译和汇编。)
(Obligatory Godbolt:为 x86 正确编译和汇编。)
请注意,gcc 假定堆栈指针在任何汇编块的末尾与在开始时相同;汇编块的其余部分必须包含一条将 sp 恢复为其原始值的指令。
对于 ARM32,我的示例如下所示 - 请注意在 ARM32 中使用寄存器加偏移寻址模式的不同语法:
int a_or_c_asm(mystruct *str) {
int a_val = str->a;
asm("lsrs %0, #1\t\n" // shift and set flags
"ldrcs %0, [%1, %2]" // load (predicated on Carry Set)
: "+r"(a_val)
: "r"(str), "i"(offsetof(mystruct, c))
: "memory" // we access memory that isn't a declared input.
);
return a_val;
}
当然,这一切都是为了展示如何将已知常量值传递给 gcc 的内联 asm 语法。更常见的替代方法是使用 "m"(str->c)
输入操作数用于 ldrcs %0, %1
.,这样您就不必使用 offsetof 宏。
此外,您可以传递一个 dummy 输入操作数来告诉编译器 "memory"
字段是一个输入,而不是使用 c
,但实际上您仍然形成了自己寻址模式;有关这方面的更多信息,请参阅 How can I indicate that the memory *pointed* to by an inline ASM argument may be used?
答案 1 :(得分:0)
我认为这不起作用。
offsetof
运算符是编译时的事情,它不由预处理器评估。这几乎是神奇的,因为预处理器不解析C,它如何计算结构偏移?这样做需要大量特定于机器的信息,因此很大程度上是编译器的责任范围。预处理器只是按摩文本。
虽然typical documentation调用offsetof
宏,但这并不意味着它由预处理器进行评估。它可能只是意味着它是一个宏,可以评估一些特定于编译器的魔法。
例如for gcc,可以这样定义:
#define offsetof(type, member) __builtin_offsetof (type, member)
这里,__builtin_offsetof()
是神奇的编译器特定函数,真正进行计算。在汇编器源需要字面偏移量的情况下调用它当然不是解决方案。