0

我正在尝试在我的汇编代码中使用 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, #-

如何声明宏

4

2 回答 2

2

你还没有说你正在为哪个处理器生成代码......我假设它是一台 RISC 机器,因为你的减法指令有 3 个操作数,但你没有说是哪一个。我将向您展示 x86 的外观,因为我知道答案是正确的。

我还假设您使用的asm volatile是遵循gcc 标准的编译器。

无论如何,假设你有一个这样的结构:

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);
}

编译器不会为此生成特别好的代码——它有七条指令,我们可以做得更好,因为我们知道一条指令可以同时测试“& 1”和右移。

要指定一个寄存器,你可以使用r“register”,我相信你知道。要指定输入/输出寄存器,请使用+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让内联汇编器输出 a2而不是$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

强制Godbolt:为ARM正确编译和组装。)
强制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 宏。

此外,"memory"您可以传递一个虚拟输入操作数来告诉编译器该c字段是一个输入,而不是使用 ,但实际上您仍然可以自己形成寻址模式;有关这方面的更多信息,请参阅 如何指示可以使用内联 ASM 参数*指向*的内存?

于 2021-04-30T02:40:21.310 回答
0

我认为这行不通。

offsetof运算符是编译时的东西,它不被预处理器评估。这几乎是神奇的,因为预处理器不解析 C,它如何计算结构偏移量?这样做需要大量特定于机器的信息,因此很大程度上属于编译器的职责范围。预处理器只是按摩文本。

虽然典型的文档调用offsetof宏,但这并不意味着它是由预处理器评估的。这可能只是意味着它是一个宏,可以评估为一些特定于编译器的魔法。

例如对于 gcc,它可以这样定义:

#define offsetof(type, member)  __builtin_offsetof (type, member)

__builtin_offsetof()真正执行计算的神奇的编译器特定函数。在您的汇编源代码需要文字偏移量的地方留下调用它当然不是解决方案。

于 2013-10-08T10:23:40.160 回答