Beispiel #1
0
void TryCatchFinallyScopes::runCleanups(CleanupCursor sourceScope,
                                        CleanupCursor targetScope,
                                        llvm::BasicBlock *continueWith) {
#if LDC_LLVM_VER >= 308
  if (useMSVCEH()) {
    runCleanupCopies(sourceScope, targetScope, continueWith);
    return;
  }
#endif

  assert(targetScope <= sourceScope);

  if (targetScope == sourceScope) {
    // No cleanups to run, just branch to the next block.
    irs.ir->CreateBr(continueWith);
    return;
  }

  // Insert the unconditional branch to the first cleanup block.
  irs.ir->CreateBr(cleanupScopes[sourceScope - 1].beginBlock());

  // Update all the control flow in the cleanups to make sure we end up where
  // we want.
  for (CleanupCursor i = sourceScope; i-- > targetScope;) {
    llvm::BasicBlock *nextBlock =
        (i > targetScope) ? cleanupScopes[i - 1].beginBlock() : continueWith;
    cleanupScopes[i].run(irs, irs.scopebb(), nextBlock);
  }
}
Beispiel #2
0
void TryCatchFinallyScopes::popTryCatch() {
  tryCatchScopes.pop_back();
  if (useMSVCEH()) {
#if LDC_LLVM_VER >= 308
    assert(isCatchSwitchBlock(cleanupScopes.back().beginBlock()));
#endif
    popCleanups(currentCleanupScope() - 1);
  } else {
    landingPadsPerCleanupScope[currentCleanupScope()].pop_back();
  }
}
Beispiel #3
0
void TryCatchFinallyScopes::pushTryCatch(TryCatchStatement *stmt,
                                         llvm::BasicBlock *endbb) {
  TryCatchScope scope(irs, getOrCreateEhPtrSlot(), stmt, endbb);
  // Only after emitting all the catch bodies, register the catch scopes.
  // This is so that (re)throwing inside a catch does not match later
  // catches.
  tryCatchScopes.push_back(scope);

  if (!useMSVCEH())
    landingPadsPerCleanupScope[currentCleanupScope()].push_back(nullptr);
}
Beispiel #4
0
CleanupScope::CleanupScope(llvm::BasicBlock *beginBlock,
                           llvm::BasicBlock *endBlock) {
#if LDC_LLVM_VER >= 308
  if (useMSVCEH()) {
    findSuccessors(blocks, beginBlock, endBlock);
    return;
  }
#endif
  blocks.push_back(beginBlock);
  if (endBlock != beginBlock)
    blocks.push_back(endBlock);
}
Beispiel #5
0
TryCatchScope::TryCatchScope(IRState &irs, llvm::Value *ehPtrSlot,
                             TryCatchStatement *stmt, llvm::BasicBlock *endbb)
    : stmt(stmt), endbb(endbb) {
  assert(stmt->catches);

  cleanupScope = irs.funcGen().scopes.currentCleanupScope();
  catchesNonExceptions =
      std::any_of(stmt->catches->begin(), stmt->catches->end(), [](Catch *c) {
        for (auto cd = c->type->toBasetype()->isClassHandle(); cd;
             cd = isClassDeclarationOrNull(cd->baseClass)) { // CALYPSO
          if (cd == ClassDeclaration::exception)
            return false;
        }
        return true;
      });

#if LDC_LLVM_VER >= 308
  if (useMSVCEH()) {
    emitCatchBodiesMSVC(irs, ehPtrSlot);
    return;
  }
#endif
  emitCatchBodies(irs, ehPtrSlot);
}
Beispiel #6
0
llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() {
#if LDC_LLVM_VER >= 308
  if (useMSVCEH()) {
    assert(currentCleanupScope() > 0);
    return emitLandingPadMSVC(currentCleanupScope() - 1);
  }
#endif

  // save and rewrite scope
  IRScope savedIRScope = irs.scope();

  // insert landing pads at the end of the function, in emission order,
  // to improve human-readability of the IR
  llvm::BasicBlock *beginBB = irs.insertBBBefore(nullptr, "landingPad");
  irs.scope() = IRScope(beginBB);

  llvm::LandingPadInst *landingPad = createLandingPadInst(irs);

  // Stash away the exception object pointer and selector value into their
  // stack slots.
  llvm::Value *ehPtr = DtoExtractValue(landingPad, 0);
  irs.ir->CreateStore(ehPtr, getOrCreateEhPtrSlot());

  llvm::Value *ehSelector = DtoExtractValue(landingPad, 1);
  if (!ehSelectorSlot)
    ehSelectorSlot = DtoRawAlloca(ehSelector->getType(), 0, "eh.selector");
  irs.ir->CreateStore(ehSelector, ehSelectorSlot);

  // Add landingpad clauses, emit finallys and 'if' chain to catch the
  // exception.
  CleanupCursor lastCleanup = currentCleanupScope();
  for (auto it = tryCatchScopes.rbegin(), end = tryCatchScopes.rend();
       it != end; ++it) {
    const auto &tryCatchScope = *it;

    // Insert any cleanups in between the previous (inner-more) try-catch scope
    // and this one.
    const auto newCleanup = tryCatchScope.getCleanupScope();
    assert(lastCleanup >= newCleanup);
    if (lastCleanup > newCleanup) {
      landingPad->setCleanup(true);
      llvm::BasicBlock *afterCleanupBB =
          irs.insertBB(beginBB->getName() + llvm::Twine(".after.cleanup"));
      runCleanups(lastCleanup, newCleanup, afterCleanupBB);
      irs.scope() = IRScope(afterCleanupBB);
      lastCleanup = newCleanup;
    }

    for (const auto &cb : tryCatchScope.getCatchBlocks()) {
      // Add the ClassInfo reference to the landingpad instruction so it is
      // emitted to the EH tables.
      landingPad->addClause(cb.classInfoPtr);

      llvm::BasicBlock *mismatchBB =
          irs.insertBB(beginBB->getName() + llvm::Twine(".mismatch"));

      // "Call" llvm.eh.typeid.for, which gives us the eh selector value to
      // compare the landing pad selector value with.
      llvm::Value *ehTypeId =
          irs.ir->CreateCall(GET_INTRINSIC_DECL(eh_typeid_for),
                             DtoBitCast(cb.classInfoPtr, getVoidPtrType()));

      // Compare the selector value from the unwinder against the expected
      // one and branch accordingly.
      irs.ir->CreateCondBr(
          irs.ir->CreateICmpEQ(irs.ir->CreateLoad(ehSelectorSlot), ehTypeId),
          cb.bodyBB, mismatchBB, cb.branchWeights);
      irs.scope() = IRScope(mismatchBB);
    }
  }

  // No catch matched. Execute all finallys and resume unwinding.
  auto resumeUnwindBlock = getOrCreateResumeUnwindBlock();
  if (lastCleanup > 0) {
    landingPad->setCleanup(true);
    runCleanups(lastCleanup, 0, resumeUnwindBlock);
  } else if (!tryCatchScopes.empty()) {
    // Directly convert the last mismatch branch into a branch to the
    // unwind resume block.
    irs.scopebb()->replaceAllUsesWith(resumeUnwindBlock);
    irs.scopebb()->eraseFromParent();
  } else {
    irs.ir->CreateBr(resumeUnwindBlock);
  }

  irs.scope() = savedIRScope;
  return beginBB;
}
Beispiel #7
0
llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock,
                                    llvm::BasicBlock *continueWith) {
#if LDC_LLVM_VER >= 308
  if (useMSVCEH())
    return runCopying(irs, sourceBlock, continueWith);
#endif

  if (exitTargets.empty() || (exitTargets.size() == 1 &&
                              exitTargets[0].branchTarget == continueWith)) {
    // We didn't need a branch selector before and still don't need one.
    assert(!branchSelector);

    // Set up the unconditional branch at the end of the cleanup if we have
    // not done so already.
    if (exitTargets.empty()) {
      exitTargets.emplace_back(continueWith);
      llvm::BranchInst::Create(continueWith, endBlock());
    }
    exitTargets.front().sourceBlocks.push_back(sourceBlock);
    return beginBlock();
  }

  // We need a branch selector if we are here...
  if (!branchSelector) {
    // ... and have not created one yet, so do so now.
    branchSelector = new llvm::AllocaInst(llvm::Type::getInt32Ty(irs.context()),
#if LDC_LLVM_VER >= 500
                                          irs.module.getDataLayout().getAllocaAddrSpace(),
#endif
                                          llvm::Twine("branchsel.") +
                                              beginBlock()->getName(),
                                          irs.topallocapoint());

    // Now we also need to store 0 to it to keep the paths that go to the
    // only existing branch target the same.
    for (auto bb : exitTargets.front().sourceBlocks) {
      new llvm::StoreInst(DtoConstUint(0), branchSelector, bb->getTerminator());
    }

    // And convert the BranchInst to the existing branch target to a
    // SelectInst so we can append the other cases to it.
    endBlock()->getTerminator()->eraseFromParent();
    llvm::Value *sel = new llvm::LoadInst(branchSelector, "", endBlock());
    llvm::SwitchInst::Create(
        sel, exitTargets[0].branchTarget,
        1, // Expected number of branches, only for pre-allocating.
        endBlock());
  }

  // If we already know this branch target, figure out the branch selector
  // value and simply insert the store into the source block (prior to the
  // last instruction, which is the branch to the first cleanup).
  for (unsigned i = 0; i < exitTargets.size(); ++i) {
    CleanupExitTarget &t = exitTargets[i];
    if (t.branchTarget == continueWith) {
      new llvm::StoreInst(DtoConstUint(i), branchSelector,
                          sourceBlock->getTerminator());

      // Note: Strictly speaking, keeping this up to date would not be
      // needed right now, because we never to any optimizations that
      // require changes to the source blocks after the initial conversion
      // from one to two branch targets. Keeping this around for now to
      // ease future development, but may be removed to save some work.
      t.sourceBlocks.push_back(sourceBlock);

      return beginBlock();
    }
  }

  // We don't know this branch target yet, so add it to the SwitchInst...
  llvm::ConstantInt *const selectorVal = DtoConstUint(exitTargets.size());
  llvm::cast<llvm::SwitchInst>(endBlock()->getTerminator())
      ->addCase(selectorVal, continueWith);

  // ... insert the store into the source block...
  new llvm::StoreInst(selectorVal, branchSelector,
                      sourceBlock->getTerminator());

  // ... and keep track of it (again, this is unnecessary right now as
  // discussed in the above note).
  exitTargets.emplace_back(continueWith);
  exitTargets.back().sourceBlocks.push_back(sourceBlock);

  return beginBlock();
}
Beispiel #8
0
static void buildRuntimeModule() {
  Logger::println("building runtime module");
  M = new llvm::Module("ldc internal runtime", gIR->context());

  Type *voidTy = Type::tvoid;
  Type *boolTy = Type::tbool;
  Type *ubyteTy = Type::tuns8;
  Type *intTy = Type::tint32;
  Type *uintTy = Type::tuns32;
  Type *ulongTy = Type::tuns64;
  Type *sizeTy = Type::tsize_t;
  Type *dcharTy = Type::tdchar;

  Type *voidPtrTy = Type::tvoidptr;
  Type *voidArrayTy = Type::tvoid->arrayOf();
  Type *voidArrayPtrTy = voidArrayTy->pointerTo();
  Type *stringTy = Type::tchar->arrayOf();
  Type *wstringTy = Type::twchar->arrayOf();
  Type *dstringTy = Type::tdchar->arrayOf();

  // Ensure that the declarations exist before creating llvm types for them.
  ensureDecl(ClassDeclaration::object, "Object");
  ensureDecl(Type::typeinfoclass, "TypeInfo_Class");
  ensureDecl(Type::dtypeinfo, "DTypeInfo");
  ensureDecl(Type::typeinfoassociativearray, "TypeInfo_AssociativeArray");
  ensureDecl(Module::moduleinfo, "ModuleInfo");

  Type *objectTy = ClassDeclaration::object->type;
  Type *classInfoTy = Type::typeinfoclass->type;
  Type *typeInfoTy = Type::dtypeinfo->type;
  Type *aaTypeInfoTy = Type::typeinfoassociativearray->type;
  Type *moduleInfoPtrTy = Module::moduleinfo->type->pointerTo();
  // The AA type is a struct that only contains a ptr
  Type *aaTy = voidPtrTy;

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // Construct some attribute lists used below (possibly multiple times)
  AttrSet NoAttrs, Attr_NoAlias(NoAttrs, llvm::AttributeSet::ReturnIndex,
                                llvm::Attribute::NoAlias),
      Attr_NoUnwind(NoAttrs, llvm::AttributeSet::FunctionIndex,
                    llvm::Attribute::NoUnwind),
      Attr_ReadOnly(NoAttrs, llvm::AttributeSet::FunctionIndex,
                    llvm::Attribute::ReadOnly),
      Attr_Cold(NoAttrs, llvm::AttributeSet::FunctionIndex,
                llvm::Attribute::Cold),
      Attr_Cold_NoReturn(Attr_Cold, llvm::AttributeSet::FunctionIndex,
                         llvm::Attribute::NoReturn),
      Attr_ReadOnly_NoUnwind(Attr_ReadOnly, llvm::AttributeSet::FunctionIndex,
                             llvm::Attribute::NoUnwind),
      Attr_ReadOnly_1_NoCapture(Attr_ReadOnly, 1, llvm::Attribute::NoCapture),
      Attr_ReadOnly_1_3_NoCapture(Attr_ReadOnly_1_NoCapture, 3,
                                  llvm::Attribute::NoCapture),
      Attr_ReadOnly_NoUnwind_1_NoCapture(Attr_ReadOnly_1_NoCapture, ~0U,
                                         llvm::Attribute::NoUnwind),
      Attr_ReadNone(NoAttrs, ~0U, llvm::Attribute::ReadNone),
      Attr_1_NoCapture(NoAttrs, 1, llvm::Attribute::NoCapture),
      Attr_NoAlias_1_NoCapture(Attr_1_NoCapture, 0, llvm::Attribute::NoAlias),
      Attr_1_2_NoCapture(Attr_1_NoCapture, 2, llvm::Attribute::NoCapture),
      Attr_1_3_NoCapture(Attr_1_NoCapture, 3, llvm::Attribute::NoCapture),
      Attr_1_4_NoCapture(Attr_1_NoCapture, 4, llvm::Attribute::NoCapture);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // void _d_assert(string file, uint line)
  // void _d_arraybounds(string file, uint line)
  createFwdDecl(LINKc, Type::tvoid, {"_d_assert", "_d_arraybounds"},
                {stringTy, uintTy}, {}, Attr_Cold_NoReturn);

  // void _d_assert_msg(string msg, string file, uint line)
  createFwdDecl(LINKc, voidTy, {"_d_assert_msg"}, {stringTy, stringTy, uintTy},
                {}, Attr_Cold_NoReturn);

  // void _d_assertm(immutable(ModuleInfo)* m, uint line)
  // void _d_array_bounds(immutable(ModuleInfo)* m, uint line)
  // void _d_switch_error(immutable(ModuleInfo)* m, uint line)
  createFwdDecl(
      LINKc, voidTy, {"_d_assertm", "_d_array_bounds", "_d_switch_error"},
      {moduleInfoPtrTy, uintTy}, {STCimmutable, 0}, Attr_Cold_NoReturn);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // void* _d_allocmemory(size_t sz)
  createFwdDecl(LINKc, voidPtrTy, {"_d_allocmemory"}, {sizeTy}, {},
                Attr_NoAlias);

  // void* _d_allocmemoryT(TypeInfo ti)
  createFwdDecl(LINKc, voidPtrTy, {"_d_allocmemoryT"}, {typeInfoTy}, {},
                Attr_NoAlias);

  // void[] _d_newarrayT (const TypeInfo ti, size_t length)
  // void[] _d_newarrayiT(const TypeInfo ti, size_t length)
  // void[] _d_newarrayU (const TypeInfo ti, size_t length)
  createFwdDecl(LINKc, voidArrayTy,
                {"_d_newarrayT", "_d_newarrayiT", "_d_newarrayU"},
                {typeInfoTy, sizeTy}, {STCconst, 0});

  // void[] _d_newarraymTX (const TypeInfo ti, size_t[] dims)
  // void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims)
  createFwdDecl(LINKc, voidArrayTy, {"_d_newarraymTX", "_d_newarraymiTX"},
                {typeInfoTy, sizeTy->arrayOf()}, {STCconst, 0});

  // void[] _d_arraysetlengthT (const TypeInfo ti, size_t newlength, void[]* p)
  // void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p)
  createFwdDecl(LINKc, voidArrayTy,
                {"_d_arraysetlengthT", "_d_arraysetlengthiT"},
                {typeInfoTy, sizeTy, voidArrayPtrTy}, {STCconst, 0, 0});

  // byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arrayappendcTX"},
                {typeInfoTy, voidArrayTy, sizeTy}, {STCconst, STCref, 0});

  // void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arrayappendT"},
                {typeInfoTy, voidArrayTy, voidArrayTy}, {STCconst, STCref, 0});

  // void[] _d_arrayappendcd(ref byte[] x, dchar c)
  // void[] _d_arrayappendwd(ref byte[] x, dchar c)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arrayappendcd", "_d_arrayappendwd"},
                {voidArrayTy, dcharTy}, {STCref, 0});

  // byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arraycatT"},
                {typeInfoTy, voidArrayTy, voidArrayTy}, {STCconst, 0, 0});

  // void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arraycatnTX"},
                {typeInfoTy, voidArrayTy->arrayOf()}, {STCconst, 0});

  // Object _d_newclass(const ClassInfo ci)
  createFwdDecl(LINKc, objectTy, {"_d_newclass"}, {classInfoTy}, {STCconst},
                Attr_NoAlias);

  // void* _d_newitemT (TypeInfo ti)
  // void* _d_newitemiT(TypeInfo ti)
  createFwdDecl(LINKc, voidPtrTy, {"_d_newitemT", "_d_newitemiT"}, {typeInfoTy},
                {0}, Attr_NoAlias);

  // void _d_delarray_t(void[]* p, const TypeInfo_Struct ti)
  createFwdDecl(LINKc, voidTy, {"_d_delarray_t"},
                {voidArrayPtrTy, Type::typeinfostruct->type}, {0, STCconst});

  // void _d_delmemory(void** p)
  // void _d_delinterface(void** p)
  createFwdDecl(LINKc, voidTy, {"_d_delmemory", "_d_delinterface"},
                {voidPtrTy->pointerTo()});

  // void _d_callfinalizer(void* p)
  createFwdDecl(LINKc, voidTy, {"_d_callfinalizer"}, {voidPtrTy});

  // D2: void _d_delclass(Object* p)
  createFwdDecl(LINKc, voidTy, {"_d_delclass"}, {objectTy->pointerTo()});

  // void _d_delstruct(void** p, TypeInfo_Struct inf)
  createFwdDecl(LINKc, voidTy, {"_d_delstruct"},
                {voidPtrTy->pointerTo(), Type::typeinfostruct->type});

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // array slice copy when assertions are on!
  // void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t
  // srclen)
  createFwdDecl(LINKc, voidTy, {"_d_array_slice_copy"},
                {voidPtrTy, sizeTy, voidPtrTy, sizeTy}, {}, Attr_1_3_NoCapture);

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// int _aApplycd1(in char[] aa, dg_t dg)
// int _aApplyRcd1(in char[] aa, dg_t dg)
#define STR_APPLY1(TY, a, b)                                                   \
  {                                                                            \
    const std::string prefix = "_aApply";                                      \
    std::string fname1 = prefix + (a) + '1', fname2 = prefix + (b) + '1',      \
                fname3 = prefix + 'R' + (a) + '1',                             \
                fname4 = prefix + 'R' + (b) + '1';                             \
    createFwdDecl(LINKc, sizeTy, {fname1, fname2, fname3, fname4},             \
                  {TY, rt_dg1()});                                             \
  }
  STR_APPLY1(stringTy, "cw", "cd")
  STR_APPLY1(wstringTy, "wc", "wd")
  STR_APPLY1(dstringTy, "dc", "dw")
#undef STR_APPLY1

// int _aApplycd2(in char[] aa, dg2_t dg)
// int _aApplyRcd2(in char[] aa, dg2_t dg)
#define STR_APPLY2(TY, a, b)                                                   \
  {                                                                            \
    const std::string prefix = "_aApply";                                      \
    std::string fname1 = prefix + (a) + '2', fname2 = prefix + (b) + '2',      \
                fname3 = prefix + 'R' + (a) + '2',                             \
                fname4 = prefix + 'R' + (b) + '2';                             \
    createFwdDecl(LINKc, sizeTy, {fname1, fname2, fname3, fname4},             \
                  {TY, rt_dg2()});                                             \
  }
  STR_APPLY2(stringTy, "cw", "cd")
  STR_APPLY2(wstringTy, "wc", "wd")
  STR_APPLY2(dstringTy, "dc", "dw")
#undef STR_APPLY2

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // fixes the length for dynamic array casts
  // size_t _d_array_cast_len(size_t len, size_t elemsz, size_t newelemsz)
  createFwdDecl(LINKc, sizeTy, {"_d_array_cast_len"}, {sizeTy, sizeTy, sizeTy},
                {}, Attr_ReadNone);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp)
  // void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arrayassign_l", "_d_arrayassign_r"},
                {typeInfoTy, voidArrayTy, voidArrayTy, voidPtrTy});

  // void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to)
  createFwdDecl(LINKc, voidArrayTy, {"_d_arrayctor"},
                {typeInfoTy, voidArrayTy, voidArrayTy});

  // void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti)
  // void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti)
  createFwdDecl(LINKc, voidPtrTy, {"_d_arraysetassign", "_d_arraysetctor"},
                {voidPtrTy, voidPtrTy, intTy, typeInfoTy}, {}, Attr_NoAlias);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // cast interface
  // void* _d_interface_cast(void* p, ClassInfo c)
  createFwdDecl(LINKc, voidPtrTy, {"_d_interface_cast"},
                {voidPtrTy, classInfoTy}, {}, Attr_ReadOnly_NoUnwind);

  // dynamic cast
  // void* _d_dynamic_cast(Object o, ClassInfo c)
  createFwdDecl(LINKc, voidPtrTy, {"_d_dynamic_cast"}, {objectTy, classInfoTy},
                {}, Attr_ReadOnly_NoUnwind);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // char[] _adReverseChar(char[] a)
  // char[] _adSortChar(char[] a)
  createFwdDecl(LINKc, stringTy, {"_adReverseChar", "_adSortChar"}, {stringTy});

  // wchar[] _adReverseWchar(wchar[] a)
  // wchar[] _adSortWchar(wchar[] a)
  createFwdDecl(LINKc, wstringTy, {"_adReverseWchar", "_adSortWchar"},
                {wstringTy});

  // void[] _adReverse(void[] a, size_t szelem)
  createFwdDecl(LINKc, wstringTy, {"_adReverse"}, {voidArrayTy, sizeTy}, {},
                Attr_NoUnwind);

  // int _adEq2(void[] a1, void[] a2, TypeInfo ti)
  // int _adCmp2(void[] a1, void[] a2, TypeInfo ti)
  createFwdDecl(LINKc, intTy, {"_adEq2", "_adCmp2"},
                {voidArrayTy, voidArrayTy, typeInfoTy}, {}, Attr_ReadOnly);

  // int _adCmpChar(void[] a1, void[] a2)
  createFwdDecl(LINKc, intTy, {"_adCmpChar"}, {voidArrayTy, voidArrayTy}, {},
                Attr_ReadOnly_NoUnwind);

  // void[] _adSort(void[] a, TypeInfo ti)
  createFwdDecl(LINKc, voidArrayTy, {"_adSort"}, {voidArrayTy, typeInfoTy});

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // size_t _aaLen(in AA aa)
  createFwdDecl(LINKc, sizeTy, {"_aaLen"}, {aaTy}, {STCin},
                Attr_ReadOnly_NoUnwind_1_NoCapture);

  // void* _aaGetY(AA* aa, const TypeInfo aati, in size_t valuesize,
  //               in void* pkey)
  createFwdDecl(LINKc, voidPtrTy, {"_aaGetY"},
                {aaTy->pointerTo(), aaTypeInfoTy, sizeTy, voidPtrTy},
                {0, STCconst, STCin, STCin}, Attr_1_4_NoCapture);

  // inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey)
  // FIXME: "inout" storageclass is not applied to return type
  createFwdDecl(LINKc, voidPtrTy, {"_aaInX"}, {aaTy, typeInfoTy, voidPtrTy},
                {STCin | STCout, STCin, STCin}, Attr_ReadOnly_1_3_NoCapture);

  // bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey)
  createFwdDecl(LINKc, boolTy, {"_aaDelX"}, {aaTy, typeInfoTy, voidPtrTy},
                {0, STCin, STCin}, Attr_1_3_NoCapture);

  // inout(void[]) _aaValues(inout AA aa, in size_t keysize,
  //                         in size_t valuesize, const TypeInfo tiValueArray)
  createFwdDecl(
      LINKc, voidArrayTy, {"_aaValues"}, {aaTy, sizeTy, sizeTy, typeInfoTy},
      {STCin | STCout, STCin, STCin, STCconst}, Attr_ReadOnly_1_3_NoCapture);

  // void* _aaRehash(AA* paa, in TypeInfo keyti)
  createFwdDecl(LINKc, voidPtrTy, {"_aaRehash"},
                {aaTy->pointerTo(), typeInfoTy}, {0, STCin});

  // inout(void[]) _aaKeys(inout AA aa, in size_t keysize,
  //                       const TypeInfo tiKeyArray)
  createFwdDecl(LINKc, voidArrayTy, {"_aaKeys"}, {aaTy, sizeTy, typeInfoTy},
                {STCin | STCout, STCin, STCconst}, Attr_NoAlias_1_NoCapture);

  // int _aaApply(AA aa, in size_t keysize, dg_t dg)
  createFwdDecl(LINKc, intTy, {"_aaApply"}, {aaTy, sizeTy, rt_dg1()},
                {0, STCin, 0}, Attr_1_NoCapture);

  // int _aaApply2(AA aa, in size_t keysize, dg2_t dg)
  createFwdDecl(LINKc, intTy, {"_aaApply2"}, {aaTy, sizeTy, rt_dg2()},
                {0, STCin, 0}, Attr_1_NoCapture);

  // int _aaEqual(in TypeInfo tiRaw, in AA e1, in AA e2)
  createFwdDecl(LINKc, intTy, {"_aaEqual"}, {typeInfoTy, aaTy, aaTy},
                {STCin, STCin, STCin}, Attr_1_2_NoCapture);

  // AA _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti,
  //                           void[] keys, void[] values)
  createFwdDecl(LINKc, aaTy, {"_d_assocarrayliteralTX"},
                {aaTypeInfoTy, voidArrayTy, voidArrayTy}, {STCconst, 0, 0});

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // void _moduleCtor()
  // void _moduleDtor()
  createFwdDecl(LINKc, voidTy, {"_moduleCtor", "_moduleDtor"}, {});

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // void _d_throw_exception(Object e)
  createFwdDecl(LINKc, voidTy, {"_d_throw_exception"}, {objectTy});

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // int _d_switch_string(char[][] table, char[] ca)
  createFwdDecl(LINKc, intTy, {"_d_switch_string"},
                {stringTy->arrayOf(), stringTy}, {}, Attr_ReadOnly);

  // int _d_switch_ustring(wchar[][] table, wchar[] ca)
  createFwdDecl(LINKc, intTy, {"_d_switch_ustring"},
                {wstringTy->arrayOf(), wstringTy}, {}, Attr_ReadOnly);

  // int _d_switch_dstring(dchar[][] table, dchar[] ca)
  createFwdDecl(LINKc, intTy, {"_d_switch_dstring"},
                {dstringTy->arrayOf(), dstringTy}, {}, Attr_ReadOnly);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // int _d_eh_personality(...)
  {
    if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
      const char *fname =
          useMSVCEH() ? "__CxxFrameHandler3" : "_d_eh_personality";
      // (ptr ExceptionRecord, ptr EstablisherFrame, ptr ContextRecord,
      //  ptr DispatcherContext)
      createFwdDecl(LINKc, intTy, {fname},
                    {voidPtrTy, voidPtrTy, voidPtrTy, voidPtrTy});
    } else if (global.params.targetTriple->getArch() == llvm::Triple::arm) {
      // (int state, ptr ucb, ptr context)
      createFwdDecl(LINKc, intTy, {"_d_eh_personality"},
                    {intTy, voidPtrTy, voidPtrTy});
    } else {
      // (int ver, int actions, ulong eh_class, ptr eh_info, ptr context)
      createFwdDecl(LINKc, intTy, {"_d_eh_personality"},
                    {intTy, intTy, ulongTy, voidPtrTy, voidPtrTy});
    }
  }

  if (useMSVCEH()) {
    // _d_enter_cleanup(ptr frame)
    createFwdDecl(LINKc, boolTy, {"_d_enter_cleanup"}, {voidPtrTy});

    // _d_leave_cleanup(ptr frame)
    createFwdDecl(LINKc, voidTy, {"_d_leave_cleanup"}, {voidPtrTy});

    // Object _d_eh_enter_catch(ptr exception, ClassInfo catchType)
    createFwdDecl(LINKc, objectTy, {"_d_eh_enter_catch"},
                  {voidPtrTy, classInfoTy}, {});
  } else {

    // void _d_eh_resume_unwind(ptr)
    createFwdDecl(LINKc, voidTy, {"_d_eh_resume_unwind"}, {voidPtrTy});

    // Object _d_eh_enter_catch(ptr)
    createFwdDecl(LINKc, objectTy, {"_d_eh_enter_catch"}, {voidPtrTy}, {},
                  Attr_NoUnwind);
  }

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // void invariant._d_invariant(Object o)
  createFwdDecl(
      LINKd, voidTy,
      {gABI->mangleFunctionForLLVM("_D9invariant12_d_invariantFC6ObjectZv", LINKd)},
      {objectTy});

  // void _d_dso_registry(CompilerDSOData* data)
  llvm::StringRef fname("_d_dso_registry");

  LLType *LLvoidTy = LLType::getVoidTy(gIR->context());
  LLType *LLvoidPtrPtrTy = getPtrToType(getPtrToType(LLvoidTy));
  LLType *moduleInfoPtrPtrTy =
      getPtrToType(getPtrToType(DtoType(Module::moduleinfo->type)));

  llvm::StructType *dsoDataTy =
      llvm::StructType::get(DtoSize_t(),        // version
                            LLvoidPtrPtrTy,     // slot
                            moduleInfoPtrPtrTy, // _minfo_beg
                            moduleInfoPtrPtrTy, // _minfo_end
                            NULL);

  llvm::Type *types[] = {getPtrToType(dsoDataTy)};
  llvm::FunctionType *fty = llvm::FunctionType::get(LLvoidTy, types, false);
  llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);

  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  // extern (C) void _d_cover_register2(string filename, size_t[] valid,
  //                                    uint[] data, ubyte minPercent)
  if (global.params.cov) {
    createFwdDecl(LINKc, voidTy, {"_d_cover_register2"},
                  {stringTy, sizeTy->arrayOf(), uintTy->arrayOf(), ubyteTy});
  }

  if (global.params.hasObjectiveC) {
    assert(global.params.targetTriple->isOSDarwin());

    // The types of these functions don't really matter because they are always
    // bitcast to correct signature before calling.
    Type* objectPtrTy = voidPtrTy;
    Type* selectorPtrTy = voidPtrTy;
    Type* realTy = Type::tfloat80;

    // id objc_msgSend(id self, SEL op, ...)
    // Function called early and/or often, so lazy binding isn't worthwhile.
    createFwdDecl(LINKc, objectPtrTy, {"objc_msgSend"},
                  {objectPtrTy, selectorPtrTy}, {},
                  AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));

    switch (global.params.targetTriple->getArch()) {
    case llvm::Triple::x86_64:
      // creal objc_msgSend_fp2ret(id self, SEL op, ...)
      createFwdDecl(LINKc, Type::tcomplex80, {"objc_msgSend_fp2ret"},
                    {objectPtrTy, selectorPtrTy});
      // fall-thru
    case llvm::Triple::x86:
      // x86_64 real return only,  x86 float, double, real return
      // real objc_msgSend_fpret(id self, SEL op, ...)
      createFwdDecl(LINKc, realTy, {"objc_msgSend_fpret"},
                    {objectPtrTy, selectorPtrTy});
      // fall-thru
    case llvm::Triple::arm:
    case llvm::Triple::thumb:
      // used when return value is aggregate via a hidden sret arg
      // void objc_msgSend_stret(T *sret_arg, id self, SEL op, ...)
      createFwdDecl(LINKc, voidTy, {"objc_msgSend_stret"},
                    {objectPtrTy, selectorPtrTy});
      break;
    default:
      break;
    }
  }
}