首页 > LLVM 中 CreatePHI 时,报错

LLVM 中 CreatePHI 时,报错

错误信息:
Assertion failed: HasHungOffUses && "alloc must have hung off uses", file I:\GitHub\Def\llvm\lib\IR\User.cpp, line 44

有人踩过这个坑没?
内存监视什么的都用了,debug了三天,LLVM 文档也翻遍了,找不到问题所在啊摔!

源代码:

/**
 * if 条件分支
 */
Value* ASTIf::codegen(Gen & gen)
{
    // if 节点返回值
    Value *v_ret(nullptr);
    auto *thefunc = gen.builder.GetInsertBlock()->getParent();

    Value *v_cond = cond->codegen(gen);
    auto *b_then = BasicBlock::Create(gen.context, "then", thefunc);
    auto *b_else = BasicBlock::Create(gen.context, "else", thefunc);
    auto *b_merge = BasicBlock::Create(gen.context, "ifcont", thefunc);
    
    // 跳转分支
    gen.builder.CreateCondBr(v_cond, b_then, b_else);
    
    // then block
    gen.builder.SetInsertPoint(b_then);
    Value *v_then = pthen->codegen(gen);
    gen.builder.CreateBr(b_merge);
    b_then = gen.builder.GetInsertBlock();
    
    // else block
    gen.builder.SetInsertPoint(b_else);
    Value *v_else = pelse ? pelse->codegen(gen) : nullptr;
    gen.builder.CreateBr(b_merge);
    b_else = gen.builder.GetInsertBlock();
    
    // merge block
    gen.builder.SetInsertPoint(b_merge);
    // if 分支类型一致 HasHungOffUses
    if (canphi) {
        PHINode *phi = gen.builder.CreatePHI( // 【【【报错行!!!】】】
            v_then->getType(), 2, "iftmp");
        phi->addIncoming(v_then, b_then);
        phi->addIncoming(v_else, b_else);
        v_ret = phi;
    }
    
    return v_ret;
}

如果实在不行就关掉Assertion,LLVM默认编译是Release+Assert版本的,有一个选项“--disable-assert”什么,具体记不太清楚了,编个纯realse版本的,就没有assert失败了,哈哈哈


TR;DL:
请关闭VS的sdl检查
这个assert检查的是一个在operator new里设置的内存标志位HasHungOffUses,sdl检查会导致默认构造函数的行为改变,清零new出来的内存空间导致标志位错误。

我在VS2015U2下手动创建项目、设置依赖试了一下,不仅仅是Def的有问题,连Kaleidoscope-Ch8(LLVM的官方示例)调用这个都会出问题。然而我注意到LLVM项目自带的example项目里编译出来的是没问题的。我马上想到了编译选项的问题,经过漫长的测试后得出的结论是:
gen.builder.CreatePHI最终调用了User::operator new,

void *User::operator new(size_t Size) {
  // Allocate space for a single Use*
  void *Storage = ::operator new(Size + sizeof(Use *));
  Use **HungOffOperandList = static_cast<Use **>(Storage);
  User *Obj = reinterpret_cast<User *>(HungOffOperandList + 1);
  Obj->NumUserOperands = 0;
  Obj->HasHungOffUses = true;
  Obj->HasDescriptor = false;
  *HungOffOperandList = nullptr;
  return Obj;
}

Obj->HasHungOffUses = true;就是设置了这个标志位,用来标志内存空间的分配情况。
而PHINode的构造函数中,调用了allocHungoffUses,在User::allocHungoffUses中就会检查这个标志位来确认内存的分配

  explicit PHINode(Type *Ty, unsigned NumReservedValues,
                   const Twine &NameStr = "",
                   Instruction *InsertBefore = nullptr)
    : Instruction(Ty, Instruction::PHI, nullptr, 0, InsertBefore),
      ReservedSpace(NumReservedValues) {
    setName(NameStr);
    allocHungoffUses(ReservedSpace);
  }
//………………
void User::allocHungoffUses(unsigned N, bool IsPhi) {
  assert(HasHungOffUses && "alloc must have hung off uses");

  static_assert(AlignOf<Use>::Alignment >= AlignOf<Use::UserRef>::Alignment,
                "Alignment is insufficient for 'hung-off-uses' pieces");
  static_assert(AlignOf<Use::UserRef>::Alignment >=
                    AlignOf<BasicBlock *>::Alignment,
                "Alignment is insufficient for 'hung-off-uses' pieces");

  // Allocate the array of Uses, followed by a pointer (with bottom bit set) to
  // the User.
  size_t size = N * sizeof(Use) + sizeof(Use::UserRef);
  if (IsPhi)
    size += N * sizeof(BasicBlock *);
  Use *Begin = static_cast<Use*>(::operator new(size));
  Use *End = Begin + N;
  (void) new(End) Use::UserRef(const_cast<User*>(this), 1);
  setOperandList(Use::initTags(Begin, End));
}

如果开启了sdl,调用构造函数前该对象的内存空间疑似会被清零,导致HasHungOffUses的值错误的设置成false,assert失败。

【热门文章】
【热门文章】