哪些未定义的行为允许这种优化?

Flo*_*sch 3 c++ undefined-behavior

我正在开发一个虚拟机,它使用典型的 Smi(小整数)编码,其中整数表示为标记指针。更准确地说,指针被标记,而整数只是被移动。

这与 V8 和 Dart 采取的方法相同: https: //github.com/v8/v8/blob/main/src/objects/smi.h#L17

在我们的实现中,我们有以下 Smi 代码:

// In smi.h

#include <stdint.h>

class Object {
 public:
  bool is_smi() const { return (reinterpret_cast<uintptr_t>(this) & 0x1) == 0; }
};

class Smi : public Object {
 public:
  intptr_t value() const { return reinterpret_cast<intptr_t>(this) >> 1; }
  static Smi* from(intptr_t value) { return reinterpret_cast<Smi*>(value << 1); }
  static Smi* cast(Object* obj) { return static_cast<Smi*>(obj); }
};
Run Code Online (Sandbox Code Playgroud)

通过此设置,gcc 12.1.0 优化了以下函数,因此当Smi 值为 0-O3时,永远不会采用“if” 。o

// bad_optim.cc
#include "smi.h"

void bad_optim(Object* o) {
  if (!o->is_smi() || o == Smi::from(0)) {
    printf("in if\n");
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我用以下代码替换“if”行,则检查有效:

  if (!o->is_smi() || Smi::cast(o)->value() == 0) {
Run Code Online (Sandbox Code Playgroud)

我猜我们遇到了一种未定义的行为,但我不清楚是哪一种。

此外,最好知道是否有一个警告此行为的标志。或者,也许有一个标志可以禁用此优化。

为了完整起见,这里是main触发该行为的。(注意bad_optimmain函数必须单独编译)。

// main.cc
#include "smi.h"

void bad_optim(Object* o);

int main() {
  Smi* o = Smi::from(0);
  bad_optim(o);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*Cat 9

很简单:取消引用 invalid 或 nullo会导致 UB,因此取消引用后,o应该不能为 null。

调用is_smi()算作解引用,即使它实际上不访问内存。

创建is_smi()一个自由函数(因为这只适用于this,而不适用于指针参数)。我还会创建Object一个不透明的结构(已声明但未定义)。

  • 对于那些没有立即看到它的人,比如我自己,要补充一点: `Smi::from(0)` 给我们 null,所以我们将 `null` 与一个永远不可能为 null 的值进行比较,因此编译器完全消除了该检查,因为它永远无法通过。 (2认同)