From 76a6e71173874253bdc09e99f4628fe690a0da9a Mon Sep 17 00:00:00 2001 From: Silvan Calarco Date: Sat, 6 Jan 2024 03:35:05 +0100 Subject: [PATCH] update to 1.23.0 [release 1.23.0-1mamba;Thu Oct 22 2020] --- ldc-1.23.0-llvm-11.patch | 6728 ++++++++++++++++++++++++++++++++++++++ ldc.spec | 9 +- 2 files changed, 6735 insertions(+), 2 deletions(-) create mode 100644 ldc-1.23.0-llvm-11.patch diff --git a/ldc-1.23.0-llvm-11.patch b/ldc-1.23.0-llvm-11.patch new file mode 100644 index 0000000..05edec6 --- /dev/null +++ b/ldc-1.23.0-llvm-11.patch @@ -0,0 +1,6728 @@ +From 69269f3bd968dbb90de52b245827bd61b4f6afe0 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Sun, 23 Aug 2020 17:25:10 +0200 +Subject: [PATCH 01/16] Refactor IRScope handling + +Replace the stack of IRScopes, each with its own IRBuilder, by directly +tampering with the state of a single IRBuilder. + +This seems like the most feasible way to account for a breaking change +in LLVM 11, disallowing IRBuilder from being copied. +--- + gen/aa.cpp | 4 +- + gen/arrays.cpp | 14 +++--- + gen/asmstmt.cpp | 4 +- + gen/classes.cpp | 4 +- + gen/funcgenstate.cpp | 2 +- + gen/functions.cpp | 25 +++++----- + gen/irstate.cpp | 69 ++++++++++++++++++--------- + gen/irstate.h | 37 +++++++++++---- + gen/statements.cpp | 100 ++++++++++++++++++++-------------------- + gen/toir.cpp | 34 +++++++------- + gen/trycatchfinally.cpp | 32 ++++++------- + ir/irclass.cpp | 8 ++-- + 12 files changed, 186 insertions(+), 147 deletions(-) + +diff --git a/gen/aa.cpp b/gen/aa.cpp +index 64766066..5344dc33 100644 +--- a/gen/aa.cpp ++++ b/gen/aa.cpp +@@ -91,12 +91,12 @@ DLValue *DtoAAIndex(Loc &loc, Type *type, DValue *aa, DValue *key, + + // set up failbb to call the array bounds error runtime function + +- gIR->scope() = IRScope(failbb); ++ gIR->ir->SetInsertPoint(failbb); + + DtoBoundsCheckFailCall(gIR, loc); + + // if ok, proceed in okbb +- gIR->scope() = IRScope(okbb); ++ gIR->ir->SetInsertPoint(okbb); + } + return new DLValue(type, ret); + } +diff --git a/gen/arrays.cpp b/gen/arrays.cpp +index b9d22f01..0d867b07 100644 +--- a/gen/arrays.cpp ++++ b/gen/arrays.cpp +@@ -144,7 +144,7 @@ static void DtoArrayInit(Loc &loc, LLValue *ptr, LLValue *length, + llvm::BranchInst::Create(condbb, gIR->scopebb()); + + // replace current scope +- gIR->scope() = IRScope(condbb); ++ gIR->ir->SetInsertPoint(condbb); + + // create the condition + LLValue *cond_val = +@@ -155,7 +155,7 @@ static void DtoArrayInit(Loc &loc, LLValue *ptr, LLValue *length, + llvm::BranchInst::Create(bodybb, endbb, cond_val, gIR->scopebb()); + + // rewrite scope +- gIR->scope() = IRScope(bodybb); ++ gIR->ir->SetInsertPoint(bodybb); + + LLValue *itr_val = DtoLoad(itr); + // assign array element value +@@ -171,7 +171,7 @@ static void DtoArrayInit(Loc &loc, LLValue *ptr, LLValue *length, + llvm::BranchInst::Create(condbb, gIR->scopebb()); + + // rewrite the scope +- gIR->scope() = IRScope(endbb); ++ gIR->ir->SetInsertPoint(endbb); + } + + //////////////////////////////////////////////////////////////////////////////// +@@ -1118,12 +1118,12 @@ LLValue *DtoArrayEqCmp_memcmp(Loc &loc, DValue *l, DValue *r, IRState &irs) { + // Note: no extra null checks are needed before passing the pointers to memcmp. + // The array comparison is UB for non-zero length, and memcmp will correctly + // return 0 (equality) when the length is zero. +- irs.scope() = IRScope(memcmpBB); ++ irs.ir->SetInsertPoint(memcmpBB); + auto memcmpAnswer = callMemcmp(loc, irs, l_ptr, r_ptr, l_length); + irs.ir->CreateBr(memcmpEndBB); + + // Merge the result of length check and memcmp call into a phi node. +- irs.scope() = IRScope(memcmpEndBB); ++ irs.ir->SetInsertPoint(memcmpEndBB); + llvm::PHINode *phi = + irs.ir->CreatePHI(LLType::getInt32Ty(gIR->context()), 2, "cmp_result"); + phi->addIncoming(DtoConstInt(1), incomingBB); +@@ -1348,11 +1348,11 @@ void DtoIndexBoundsCheck(Loc &loc, DValue *arr, DValue *index) { + gIR->ir->CreateCondBr(cond, okbb, failbb); + + // set up failbb to call the array bounds error runtime function +- gIR->scope() = IRScope(failbb); ++ gIR->ir->SetInsertPoint(failbb); + DtoBoundsCheckFailCall(gIR, loc); + + // if ok, proceed in okbb +- gIR->scope() = IRScope(okbb); ++ gIR->ir->SetInsertPoint(okbb); + } + + void DtoBoundsCheckFailCall(IRState *irs, Loc &loc) { +diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp +index be2f090b..d4c76b87 100644 +--- a/gen/asmstmt.cpp ++++ b/gen/asmstmt.cpp +@@ -752,11 +752,11 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { + pair.second), + casebb); + +- p->scope() = IRScope(casebb); ++ p->ir->SetInsertPoint(casebb); + DtoGoto(stmt->loc, pair.first); + } + +- p->scope() = IRScope(bb); ++ p->ir->SetInsertPoint(bb); + } + } + +diff --git a/gen/classes.cpp b/gen/classes.cpp +index 037ef923..0777ce02 100644 +--- a/gen/classes.cpp ++++ b/gen/classes.cpp +@@ -221,11 +221,11 @@ void DtoFinalizeScopeClass(Loc &loc, LLValue *inst, bool hasDtor) { + getNullValue(monitor->getType()), ".hasMonitor"); + llvm::BranchInst::Create(ifbb, endbb, hasMonitor, gIR->scopebb()); + +- gIR->scope() = IRScope(ifbb); ++ gIR->ir->SetInsertPoint(ifbb); + DtoFinalizeClass(loc, inst); + gIR->ir->CreateBr(endbb); + +- gIR->scope() = IRScope(endbb); ++ gIR->ir->SetInsertPoint(endbb); + } + + //////////////////////////////////////////////////////////////////////////////// +diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp +index ca61adb7..e2c16958 100644 +--- a/gen/funcgenstate.cpp ++++ b/gen/funcgenstate.cpp +@@ -140,6 +140,6 @@ llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee, + invoke->setAttributes(calleeFn->getAttributes()); + } + +- irs.scope() = IRScope(postinvoke); ++ irs.ir->SetInsertPoint(postinvoke); + return invoke; + } +diff --git a/gen/functions.cpp b/gen/functions.cpp +index c8c0ec3d..554102ca 100644 +--- a/gen/functions.cpp ++++ b/gen/functions.cpp +@@ -883,12 +883,12 @@ void emitDMDStyleFunctionTrace(IRState &irs, FuncDeclaration *fd, + // Push cleanup block that calls _c_trace_epi at function exit. + { + auto traceEpilogBB = irs.insertBB("trace_epi"); +- auto saveScope = irs.scope(); +- irs.scope() = IRScope(traceEpilogBB); ++ const auto savedInsertPoint = irs.getInsertPoint(); ++ irs.ir->SetInsertPoint(traceEpilogBB); + irs.ir->CreateCall( + getRuntimeFunction(fd->endloc, irs.module, "_c_trace_epi")); + funcGen.scopes.pushCleanup(traceEpilogBB, irs.scopebb()); +- irs.scope() = saveScope; ++ irs.setInsertPoint(savedInsertPoint); + } + } + +@@ -1165,13 +1165,10 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + llvm::BasicBlock *beginbb = + llvm::BasicBlock::Create(gIR->context(), "", func); + +- gIR->scopes.push_back(IRScope(beginbb)); +- SCOPE_EXIT { +- gIR->scopes.pop_back(); +- }; +- +- // Set the FastMath options for this function scope. +- gIR->scopes.back().builder.setFastMathFlags(irFunc->FMF); ++ // set up the IRBuilder scope for the function ++ FunctionIRBuilderScope irBuilderScope(*gIR); ++ gIR->setInsertPoint(beginbb); ++ gIR->ir->setFastMathFlags(irFunc->FMF); + + // @naked: emit body and return, no prologue/epilogue + if (func->hasFnAttribute(llvm::Attribute::Naked)) { +@@ -1277,11 +1274,11 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + { + auto *vaendBB = + llvm::BasicBlock::Create(gIR->context(), "vaend", gIR->topfunc()); +- IRScope saveScope = gIR->scope(); +- gIR->scope() = IRScope(vaendBB); ++ const auto savedInsertPoint = gIR->getInsertPoint(); ++ gIR->ir->SetInsertPoint(vaendBB); + gIR->ir->CreateCall(GET_INTRINSIC_DECL(vaend), llAp); + funcGen.scopes.pushCleanup(vaendBB, gIR->scopebb()); +- gIR->scope() = saveScope; ++ gIR->setInsertPoint(savedInsertPoint); + } + } + +@@ -1297,7 +1294,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + if (!funcGen.retBlock) + funcGen.retBlock = gIR->insertBB("return"); + funcGen.scopes.runCleanups(0, funcGen.retBlock); +- gIR->scope() = IRScope(funcGen.retBlock); ++ gIR->ir->SetInsertPoint(funcGen.retBlock); + } + funcGen.scopes.popCleanups(0); + } +diff --git a/gen/irstate.cpp b/gen/irstate.cpp +index f5e3c299..0ac6ddc3 100644 +--- a/gen/irstate.cpp ++++ b/gen/irstate.cpp +@@ -26,20 +26,9 @@ llvm::TargetMachine *gTargetMachine = nullptr; + const llvm::DataLayout *gDataLayout = nullptr; + TargetABI *gABI = nullptr; + +-//////////////////////////////////////////////////////////////////////////////// +-IRScope::IRScope() : builder(gIR->context()) { begin = nullptr; } +- +-IRScope::IRScope(llvm::BasicBlock *b) : begin(b), builder(b) {} +- +-IRScope &IRScope::operator=(const IRScope &rhs) { +- begin = rhs.begin; +- builder.SetInsertPoint(begin); +- return *this; +-} +- + //////////////////////////////////////////////////////////////////////////////// + IRState::IRState(const char *name, llvm::LLVMContext &context) +- : module(name, context), objc(module), DBuilder(this) { ++ : builder(context), module(name, context), objc(module), DBuilder(this) { + ir.state = this; + mem.addRange(&inlineAsmLocs, sizeof(inlineAsmLocs)); + } +@@ -57,20 +46,41 @@ llvm::Function *IRState::topfunc() { return func()->getLLVMFunc(); } + + llvm::Instruction *IRState::topallocapoint() { return funcGen().allocapoint; } + +-IRScope &IRState::scope() { +- assert(!scopes.empty()); +- return scopes.back(); ++InsertionPoint IRState::getInsertPoint() { ++ auto bb = builder.GetInsertBlock(); ++ if (!bb) ++ return {nullptr, llvm::None}; ++ ++ llvm::Optional point = builder.GetInsertPoint(); ++ if (point == bb->end()) ++ point = llvm::None; ++ ++ return {bb, point}; ++} ++ ++void IRState::setInsertPoint(llvm::BasicBlock *insertAtEnd) { ++ builder.SetInsertPoint(insertAtEnd); ++} ++ ++void IRState::setInsertPoint(InsertionPoint point) { ++ if (!point.bb) { ++ builder.ClearInsertionPoint(); ++ } else if (!point.point.hasValue()) { ++ builder.SetInsertPoint(point.bb); ++ } else { ++ builder.SetInsertPoint(point.bb, point.point.getValue()); ++ } + } + + llvm::BasicBlock *IRState::scopebb() { +- IRScope &s = scope(); +- assert(s.begin); +- return s.begin; ++ auto bb = ir->GetInsertBlock(); ++ assert(bb); ++ return bb; + } + + bool IRState::scopereturned() { +- // return scope().returned; +- return !scopebb()->empty() && scopebb()->back().isTerminator(); ++ auto bb = scopebb(); ++ return !bb->empty() && bb->back().isTerminator(); + } + + llvm::BasicBlock *IRState::insertBBBefore(llvm::BasicBlock *successor, +@@ -283,8 +293,25 @@ const Loc &IRState::getInlineAsmSrcLoc(unsigned srcLocCookie) const { + + //////////////////////////////////////////////////////////////////////////////// + ++FunctionIRBuilderScope::FunctionIRBuilderScope(IRState &state) ++ : state(state), previousInsertionPoint(state.getInsertPoint()), ++ previousFMF(state.builder.getFastMathFlags()), ++ previousDebugLoc(state.builder.getCurrentDebugLocation()) { ++ state.builder.ClearInsertionPoint(); ++ state.builder.clearFastMathFlags(); ++ state.builder.SetCurrentDebugLocation({}); ++} ++ ++FunctionIRBuilderScope::~FunctionIRBuilderScope() { ++ state.setInsertPoint(previousInsertionPoint); ++ state.builder.setFastMathFlags(previousFMF); ++ state.builder.SetCurrentDebugLocation(previousDebugLoc); ++} ++ ++//////////////////////////////////////////////////////////////////////////////// ++ + IRBuilder<> *IRBuilderHelper::operator->() { +- IRBuilder<> &b = state->scope().builder; ++ IRBuilder<> &b = state->builder; + assert(b.GetInsertBlock() != NULL); + return &b; + } +diff --git a/gen/irstate.h b/gen/irstate.h +index a47c276c..340bfb57 100644 +--- a/gen/irstate.h ++++ b/gen/irstate.h +@@ -59,16 +59,28 @@ class StructLiteralExp; + struct IrFunction; + struct IrModule; + +-// represents a scope +-struct IRScope { +- llvm::BasicBlock *begin; +- IRBuilder<> builder; ++// insertion point for IRBuilder ++struct InsertionPoint { ++ llvm::BasicBlock *bb; // can be null ++ llvm::Optional point; // bb end if not set ++ ++ InsertionPoint(llvm::BasicBlock *bb, ++ llvm::Optional point) ++ : bb(bb), point(std::move(point)) {} ++}; + +- IRScope(); +- IRScope(const IRScope &) = default; +- explicit IRScope(llvm::BasicBlock *b); ++// Resets the IRBuilder for a new function and restores its previous state on ++// destruction. ++struct FunctionIRBuilderScope { ++private: ++ IRState &state; ++ InsertionPoint previousInsertionPoint; ++ llvm::FastMathFlags previousFMF; ++ llvm::DebugLoc previousDebugLoc; + +- IRScope &operator=(const IRScope &rhs); ++public: ++ FunctionIRBuilderScope(IRState &state); ++ ~FunctionIRBuilderScope(); + }; + + struct IRBuilderHelper { +@@ -111,6 +123,10 @@ struct IRAsmBlock { + // represents the LLVM module (object file) + struct IRState { + private: ++ IRBuilder<> builder; ++ friend struct FunctionIRBuilderScope; ++ friend struct IRBuilderHelper; ++ + std::vector> + globalsToReplace; + Array inlineAsmLocs; // tracked by GC +@@ -155,8 +171,9 @@ public: + llvm::Instruction *topallocapoint(); + + // basic block scopes +- std::vector scopes; +- IRScope &scope(); ++ InsertionPoint getInsertPoint(); ++ void setInsertPoint(llvm::BasicBlock *insertAtEnd); ++ void setInsertPoint(InsertionPoint point); + llvm::BasicBlock *scopebb(); + bool scopereturned(); + +diff --git a/gen/statements.cpp b/gen/statements.cpp +index f7e4bbce..34266b4c 100644 +--- a/gen/statements.cpp ++++ b/gen/statements.cpp +@@ -276,7 +276,7 @@ public: + // Pop the cleanups pushed during evaluation of the return expression. + funcGen.scopes.popCleanups(cleanupScopeBeforeExpression); + +- irs->scope() = IRScope(funcGen.retBlock); ++ irs->ir->SetInsertPoint(funcGen.retBlock); + } + + // If we need to emit the actual return instruction, do so. +@@ -299,7 +299,7 @@ public: + // Finally, create a new predecessor-less dummy bb as the current IRScope + // to make sure we do not emit any extra instructions after the terminating + // instruction (ret or branch to return bb), which would be illegal IR. +- irs->scope() = IRScope(irs->insertBB("dummy.afterreturn")); ++ irs->ir->SetInsertPoint(irs->insertBB("dummy.afterreturn")); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -426,7 +426,7 @@ public: + PGO.addBranchWeights(brinstr, brweights); + + // replace current scope +- irs->scope() = IRScope(ifbb); ++ irs->ir->SetInsertPoint(ifbb); + + // do scoped statements + +@@ -441,7 +441,7 @@ public: + } + + if (stmt->elsebody) { +- irs->scope() = IRScope(elsebb); ++ irs->ir->SetInsertPoint(elsebb); + irs->DBuilder.EmitBlockStart(stmt->elsebody->loc); + stmt->elsebody->accept(this); + if (!irs->scopereturned()) { +@@ -454,7 +454,7 @@ public: + irs->DBuilder.EmitBlockEnd(); + + // rewrite the scope +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -495,7 +495,7 @@ public: + irs->ir->CreateBr(whilebb); + + // replace current scope +- irs->scope() = IRScope(whilebb); ++ irs->ir->SetInsertPoint(whilebb); + + // create the condition + emitCoverageLinecountInc(stmt->condition->loc); +@@ -514,7 +514,7 @@ public: + } + + // rewrite scope +- irs->scope() = IRScope(whilebodybb); ++ irs->ir->SetInsertPoint(whilebodybb); + + // while body code + irs->funcGen().jumpTargets.pushLoopTarget(stmt, whilebb, endbb); +@@ -530,7 +530,7 @@ public: + } + + // rewrite the scope +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); +@@ -558,7 +558,7 @@ public: + llvm::BranchInst::Create(dowhilebb, irs->scopebb()); + + // replace current scope +- irs->scope() = IRScope(dowhilebb); ++ irs->ir->SetInsertPoint(dowhilebb); + + // do-while body code + irs->funcGen().jumpTargets.pushLoopTarget(stmt, condbb, endbb); +@@ -570,7 +570,7 @@ public: + + // branch to condition block + llvm::BranchInst::Create(condbb, irs->scopebb()); +- irs->scope() = IRScope(condbb); ++ irs->ir->SetInsertPoint(condbb); + + // create the condition + emitCoverageLinecountInc(stmt->condition->loc); +@@ -592,7 +592,7 @@ public: + } + + // rewrite the scope +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); +@@ -635,7 +635,7 @@ public: + irs->funcGen().jumpTargets.pushLoopTarget(scopeStart, forincbb, endbb); + + // replace current scope +- irs->scope() = IRScope(forbb); ++ irs->ir->SetInsertPoint(forbb); + + // create the condition + llvm::Value *cond_val; +@@ -658,7 +658,7 @@ public: + } + + // rewrite scope +- irs->scope() = IRScope(forbodybb); ++ irs->ir->SetInsertPoint(forbodybb); + + // do for body code + PGO.emitCounterIncrement(stmt); +@@ -670,7 +670,7 @@ public: + if (!irs->scopereturned()) { + llvm::BranchInst::Create(forincbb, irs->scopebb()); + } +- irs->scope() = IRScope(forincbb); ++ irs->ir->SetInsertPoint(forincbb); + + // increment + if (stmt->increment) { +@@ -687,7 +687,7 @@ public: + irs->funcGen().jumpTargets.popLoopTarget(); + + // rewrite the scope +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + + // end the dwarf lexical block + irs->DBuilder.EmitBlockEnd(); +@@ -731,7 +731,7 @@ public: + + // the break terminated this basicblock, start a new one + llvm::BasicBlock *bb = irs->insertBB("afterbreak"); +- irs->scope() = IRScope(bb); ++ irs->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -766,7 +766,7 @@ public: + + // the continue terminated this basicblock, start a new one + llvm::BasicBlock *bb = irs->insertBB("aftercontinue"); +- irs->scope() = IRScope(bb); ++ irs->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -818,7 +818,7 @@ public: + : irs->insertBBAfter(finallybb, "try.success"); + + // Emit the finally block and set up the cleanup scope for it. +- irs->scope() = IRScope(finallybb); ++ irs->ir->SetInsertPoint(finallybb); + irs->DBuilder.EmitBlockStart(stmt->finalbody->loc); + stmt->finalbody->accept(this); + irs->DBuilder.EmitBlockEnd(); +@@ -832,7 +832,7 @@ public: + irs->funcGen().scopes.pushCleanup(finallybb, irs->scopebb()); + } + // Emit the try block. +- irs->scope() = IRScope(trybb); ++ irs->ir->SetInsertPoint(trybb); + + assert(stmt->_body); + irs->DBuilder.EmitBlockStart(stmt->_body->loc); +@@ -842,7 +842,7 @@ public: + if (successbb) { + if (!computeCode) + irs->funcGen().scopes.runCleanups(cleanupBefore, successbb); +- irs->scope() = IRScope(successbb); ++ irs->ir->SetInsertPoint(successbb); + // PGO counter tracks the continuation of the try-finally statement + PGO.emitCounterIncrement(stmt); + } +@@ -874,7 +874,7 @@ public: + irs->funcGen().scopes.pushTryCatch(stmt, endbb); + + // Emit the try block. +- irs->scope() = IRScope(trybb); ++ irs->ir->SetInsertPoint(trybb); + + assert(stmt->_body); + irs->DBuilder.EmitBlockStart(stmt->_body->loc); +@@ -886,7 +886,7 @@ public: + + irs->funcGen().scopes.popTryCatch(); + +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + + // PGO counter tracks the continuation of the try statement + PGO.emitCounterIncrement(stmt); +@@ -920,7 +920,7 @@ public: + + // TODO: Should not be needed. + llvm::BasicBlock *bb = irs->insertBB("afterthrow"); +- irs->scope() = IRScope(bb); ++ irs->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -986,7 +986,7 @@ public: + + // do switch body + assert(stmt->_body); +- irs->scope() = IRScope(bodybb); ++ irs->ir->SetInsertPoint(bodybb); + funcGen.jumpTargets.pushBreakTarget(stmt, endbb); + stmt->_body->accept(this); + funcGen.jumpTargets.popBreakTarget(); +@@ -994,7 +994,7 @@ public: + llvm::BranchInst::Create(endbb, irs->scopebb()); + } + +- irs->scope() = IRScope(oldbb); ++ irs->ir->SetInsertPoint(oldbb); + if (useSwitchInst) { + // The case index value. + LLValue *condVal = DtoRVal(toElemDtor(stmt->condition)); +@@ -1018,7 +1018,7 @@ public: + { + llvm::BasicBlock *defaultcntr = + irs->insertBBBefore(defaultTargetBB, "defaultcntr"); +- irs->scope() = IRScope(defaultcntr); ++ irs->ir->SetInsertPoint(defaultcntr); + if (stmt->sdefault) + PGO.emitCounterIncrement(stmt->sdefault); + llvm::BranchInst::Create(defaultTargetBB, defaultcntr); +@@ -1033,7 +1033,7 @@ public: + const auto body = funcGen.switchTargets.get(cs); + + auto casecntr = irs->insertBBBefore(body, "casecntr"); +- irs->scope() = IRScope(casecntr); ++ irs->ir->SetInsertPoint(casecntr); + PGO.emitCounterIncrement(cs); + llvm::BranchInst::Create(body, casecntr); + si->addCase(isaConstantInt(indices[i]), casecntr); +@@ -1068,13 +1068,13 @@ public: + // Prepend extra BB to "default:" to increment profiling counter. + llvm::BasicBlock *defaultcntr = + irs->insertBBBefore(defaultTargetBB, "defaultcntr"); +- irs->scope() = IRScope(defaultcntr); ++ irs->ir->SetInsertPoint(defaultcntr); + PGO.emitCounterIncrement(stmt->sdefault); + llvm::BranchInst::Create(defaultTargetBB, defaultcntr); + defaultTargetBB = defaultcntr; + } + +- irs->scope() = IRScope(nextbb); ++ irs->ir->SetInsertPoint(nextbb); + auto failedCompareCount = incomingPGORegionCount; + for (size_t i = 0; i < caseCount; ++i) { + LLValue *cmp = irs->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, indices[i], +@@ -1087,11 +1087,11 @@ public: + if (PGO.emitsInstrumentation()) { + llvm::BasicBlock *casecntr = + irs->insertBBBefore(casejumptargetbb, "casecntr"); +- auto savedbb = irs->scope(); +- irs->scope() = IRScope(casecntr); ++ const auto savedInsertPoint = irs->getInsertPoint(); ++ irs->ir->SetInsertPoint(casecntr); + PGO.emitCounterIncrement(cs); + llvm::BranchInst::Create(casejumptargetbb, casecntr); +- irs->scope() = savedbb; ++ irs->setInsertPoint(savedInsertPoint); + + casejumptargetbb = casecntr; + } +@@ -1111,13 +1111,13 @@ public: + PGO.addBranchWeights(branchinst, brweights); + } + +- irs->scope() = IRScope(nextbb); ++ irs->ir->SetInsertPoint(nextbb); + } + + llvm::BranchInst::Create(defaultTargetBB, irs->scopebb()); + } + +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + // PGO counter tracks exit point of switch statement: + PGO.emitCounterIncrement(stmt); + } +@@ -1141,7 +1141,7 @@ public: + llvm::BranchInst::Create(body, irs->scopebb()); + } + +- irs->scope() = IRScope(body); ++ irs->ir->SetInsertPoint(body); + + assert(stmt->statement); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); +@@ -1172,7 +1172,7 @@ public: + llvm::BranchInst::Create(body, irs->scopebb()); + } + +- irs->scope() = IRScope(body); ++ irs->ir->SetInsertPoint(body); + + assert(stmt->statement); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); +@@ -1231,7 +1231,7 @@ public: + llvm::BasicBlock *nextbb = (i + 1 == nstmt) ? endbb : blocks[i + 1]; + + // update scope +- irs->scope() = IRScope(thisbb); ++ irs->ir->SetInsertPoint(thisbb); + + // push loop scope + // continue goes to next statement, break goes to end +@@ -1251,7 +1251,7 @@ public: + } + } + +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + + // PGO counter tracks the continuation after the loop + PGO.emitCounterIncrement(stmt); +@@ -1334,7 +1334,7 @@ public: + llvm::BranchInst::Create(condbb, irs->scopebb()); + + // condition +- irs->scope() = IRScope(condbb); ++ irs->ir->SetInsertPoint(condbb); + + LLValue *done = nullptr; + LLValue *load = DtoLoad(keyvar); +@@ -1353,7 +1353,7 @@ public: + } + + // init body +- irs->scope() = IRScope(bodybb); ++ irs->ir->SetInsertPoint(bodybb); + PGO.emitCounterIncrement(stmt); + + // get value for this iteration +@@ -1383,7 +1383,7 @@ public: + } + + // next +- irs->scope() = IRScope(nextbb); ++ irs->ir->SetInsertPoint(nextbb); + if (stmt->op == TOKforeach) { + LLValue *load = DtoLoad(keyvar); + load = irs->ir->CreateAdd(load, LLConstantInt::get(keytype, 1, false)); +@@ -1395,7 +1395,7 @@ public: + irs->DBuilder.EmitBlockEnd(); + + // end +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -1438,7 +1438,7 @@ public: + llvm::BranchInst::Create(condbb, irs->scopebb()); + + // CONDITION +- irs->scope() = IRScope(condbb); ++ irs->ir->SetInsertPoint(condbb); + + // first we test that lwr < upr + lower = DtoLoad(keyval); +@@ -1462,7 +1462,7 @@ public: + } + + // BODY +- irs->scope() = IRScope(bodybb); ++ irs->ir->SetInsertPoint(bodybb); + PGO.emitCounterIncrement(stmt); + + // reverse foreach decrements here +@@ -1486,7 +1486,7 @@ public: + } + + // NEXT +- irs->scope() = IRScope(nextbb); ++ irs->ir->SetInsertPoint(nextbb); + + // forward foreach increments here + if (stmt->op == TOKforeach) { +@@ -1503,7 +1503,7 @@ public: + irs->DBuilder.EmitBlockEnd(); + + // END +- irs->scope() = IRScope(endbb); ++ irs->ir->SetInsertPoint(endbb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -1538,7 +1538,7 @@ public: + llvm::BranchInst::Create(labelBB, irs->scopebb()); + } + +- irs->scope() = IRScope(labelBB); ++ irs->ir->SetInsertPoint(labelBB); + } + + PGO.emitCounterIncrement(stmt); +@@ -1565,7 +1565,7 @@ public: + + // TODO: Should not be needed. + llvm::BasicBlock *bb = irs->insertBB("aftergoto"); +- irs->scope() = IRScope(bb); ++ irs->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -1590,7 +1590,7 @@ public: + + // TODO: Should not be needed. + llvm::BasicBlock *bb = irs->insertBB("aftergotodefault"); +- irs->scope() = IRScope(bb); ++ irs->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////// +@@ -1616,7 +1616,7 @@ public: + + // TODO: Should not be needed. + llvm::BasicBlock *bb = irs->insertBB("aftergotocase"); +- irs->scope() = IRScope(bb); ++ irs->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////// +diff --git a/gen/toir.cpp b/gen/toir.cpp +index d9353933..bbbd3783 100644 +--- a/gen/toir.cpp ++++ b/gen/toir.cpp +@@ -188,11 +188,11 @@ void pushVarDtorCleanup(IRState *p, VarDeclaration *vd) { + llvm::BasicBlock *beginBB = p->insertBB(llvm::Twine("dtor.") + vd->toChars()); + + // TODO: Clean this up with push/pop insertion point methods. +- IRScope oldScope = p->scope(); +- p->scope() = IRScope(beginBB); ++ const auto savedInsertPoint = p->getInsertPoint(); ++ p->ir->SetInsertPoint(beginBB); + toElemDtor(vd->edtor); + p->funcGen().scopes.pushCleanup(beginBB, p->scopebb()); +- p->scope() = oldScope; ++ p->setInsertPoint(savedInsertPoint); + } + } + +@@ -253,7 +253,7 @@ public: + llvm::BasicBlock *endbb = p->insertBB("toElem.success"); + p->funcGen().scopes.runCleanups(initialCleanupScope, endbb); + p->funcGen().scopes.popCleanups(initialCleanupScope); +- p->scope() = IRScope(endbb); ++ p->ir->SetInsertPoint(endbb); + + destructTemporaries = false; + } +@@ -1145,10 +1145,10 @@ public: + + p->ir->CreateCondBr(okCond, okbb, failbb); + +- p->scope() = IRScope(failbb); ++ p->ir->SetInsertPoint(failbb); + DtoBoundsCheckFailCall(p, e->loc); + +- p->scope() = IRScope(okbb); ++ p->ir->SetInsertPoint(okbb); + } + + // offset by lower +@@ -1267,19 +1267,19 @@ public: + lfptr, rfptr, ".fptreqcmp"); + llvm::BranchInst::Create(fptreq, fptrneq, fptreqcmp, p->scopebb()); + +- p->scope() = IRScope(fptreq); ++ p->ir->SetInsertPoint(fptreq); + llvm::Value *lctx = p->ir->CreateExtractValue(lhs, 0, ".lctx"); + llvm::Value *rctx = p->ir->CreateExtractValue(rhs, 0, ".rctx"); + llvm::Value *ctxcmp = + p->ir->CreateICmp(icmpPred, lctx, rctx, ".ctxcmp"); + llvm::BranchInst::Create(dgcmpend, p->scopebb()); + +- p->scope() = IRScope(fptrneq); ++ p->ir->SetInsertPoint(fptrneq); + llvm::Value *fptrcmp = + p->ir->CreateICmp(icmpPred, lfptr, rfptr, ".fptrcmp"); + llvm::BranchInst::Create(dgcmpend, p->scopebb()); + +- p->scope() = IRScope(dgcmpend); ++ p->ir->SetInsertPoint(dgcmpend); + llvm::PHINode *phi = p->ir->CreatePHI(ctxcmp->getType(), 2, ".dgcmp"); + phi->addIncoming(ctxcmp, fptreq); + phi->addIncoming(fptrcmp, fptrneq); +@@ -1655,7 +1655,7 @@ public: + // assign branch weights to this branch instruction. + + // failed: call assert runtime function +- p->scope() = IRScope(failedbb); ++ p->ir->SetInsertPoint(failedbb); + + if (global.params.checkAction == CHECKACTION_halt) { + p->ir->CreateCall(GET_INTRINSIC_DECL(trap), {}); +@@ -1679,7 +1679,7 @@ public: + } + + // passed: +- p->scope() = IRScope(passedbb); ++ p->ir->SetInsertPoint(passedbb); + + // class/struct invariants + if (global.params.useInvariants != CHECKENABLEon) +@@ -1764,7 +1764,7 @@ public: + p->ir->CreateCondBr(ubool, isAndAnd ? rhsBB : endBB, + isAndAnd ? endBB : rhsBB, branchweights); + +- p->scope() = IRScope(rhsBB); ++ p->ir->SetInsertPoint(rhsBB); + PGO.emitCounterIncrement(e); + emitCoverageLinecountInc(e->e2->loc); + DValue *v = toElemDtor(e->e2); +@@ -1776,7 +1776,7 @@ public: + + llvm::BasicBlock *newblock = p->scopebb(); + llvm::BranchInst::Create(endBB, p->scopebb()); +- p->scope() = IRScope(endBB); ++ p->ir->SetInsertPoint(endBB); + + // DMD allows stuff like `x == 0 && assert(false)` + if (e->type->toBasetype()->ty == Tvoid) { +@@ -1821,7 +1821,7 @@ public: + // this is sensible, since someone might goto behind the assert + // and prevents compiler errors if a terminator follows the assert + llvm::BasicBlock *bb = p->insertBB("afterhalt"); +- p->scope() = IRScope(bb); ++ p->ir->SetInsertPoint(bb); + } + + ////////////////////////////////////////////////////////////////////////////// +@@ -2003,7 +2003,7 @@ public: + auto branchweights = PGO.createProfileWeights(truecount, falsecount); + p->ir->CreateCondBr(cond_val, condtrue, condfalse, branchweights); + +- p->scope() = IRScope(condtrue); ++ p->ir->SetInsertPoint(condtrue); + PGO.emitCounterIncrement(e); + DValue *u = toElem(e->e1); + if (retPtr) { +@@ -2012,7 +2012,7 @@ public: + } + llvm::BranchInst::Create(condend, p->scopebb()); + +- p->scope() = IRScope(condfalse); ++ p->ir->SetInsertPoint(condfalse); + DValue *v = toElem(e->e2); + if (retPtr) { + LLValue *lval = makeLValue(e->loc, v); +@@ -2020,7 +2020,7 @@ public: + } + llvm::BranchInst::Create(condend, p->scopebb()); + +- p->scope() = IRScope(condend); ++ p->ir->SetInsertPoint(condend); + if (retPtr) + result = new DSpecialRefValue(e->type, retPtr); + } +diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp +index b06c4ae3..9a42de2e 100644 +--- a/gen/trycatchfinally.cpp ++++ b/gen/trycatchfinally.cpp +@@ -74,7 +74,7 @@ void TryCatchScope::emitCatchBodies(IRState &irs, llvm::Value *ehPtrSlot) { + for (auto c : *stmt->catches) { + auto catchBB = + irs.insertBBBefore(endbb, llvm::Twine("catch.") + c->type->toChars()); +- irs.scope() = IRScope(catchBB); ++ irs.ir->SetInsertPoint(catchBB); + irs.DBuilder.EmitBlockStart(c->loc); + PGO.emitCounterIncrement(c); + +@@ -218,9 +218,8 @@ void emitBeginCatchMSVC(IRState &irs, Catch *ctch, + + // redirect scope to avoid the generation of debug info before the + // catchpad +- IRScope save = irs.scope(); +- irs.scope() = IRScope(gIR->topallocapoint()->getParent()); +- irs.scope().builder.SetInsertPoint(gIR->topallocapoint()); ++ const auto savedInsertPoint = irs.getInsertPoint(); ++ irs.ir->SetInsertPoint(gIR->topallocapoint()); + DtoDeclarationExp(var); + + // catch handler will be outlined, so always treat as a nested reference +@@ -232,7 +231,7 @@ void emitBeginCatchMSVC(IRState &irs, Catch *ctch, + cpyObj = exnObj; + exnObj = DtoAlloca(var->type, "exnObj"); + } +- irs.scope() = save; ++ irs.setInsertPoint(savedInsertPoint); + irs.DBuilder.EmitStopPoint(ctch->loc); // re-set debug loc after the + // SetInsertPoint(allocaInst) call + } else if (ctch->type) { +@@ -275,7 +274,7 @@ void emitBeginCatchMSVC(IRState &irs, Catch *ctch, + // outside the catch funclet + llvm::BasicBlock *catchhandler = irs.insertBB("catchhandler"); + llvm::CatchReturnInst::Create(catchpad, catchhandler, irs.scopebb()); +- irs.scope() = IRScope(catchhandler); ++ irs.ir->SetInsertPoint(catchhandler); + irs.funcGen().pgo.emitCounterIncrement(ctch); + if (!isCPPclass) { + auto enterCatchFn = +@@ -302,7 +301,7 @@ void TryCatchScope::emitCatchBodiesMSVC(IRState &irs, llvm::Value *) { + auto catchBB = + irs.insertBBBefore(endbb, llvm::Twine("catch.") + c->type->toChars()); + +- irs.scope() = IRScope(catchBB); ++ irs.ir->SetInsertPoint(catchBB); + irs.DBuilder.EmitBlockStart(c->loc); + + emitBeginCatchMSVC(irs, c, catchSwitchInst); +@@ -689,12 +688,12 @@ llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() { + } + + // save and rewrite scope +- IRScope savedIRScope = irs.scope(); ++ const auto savedInsertPoint = irs.getInsertPoint(); + + // 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); ++ irs.ir->SetInsertPoint(beginBB); + + llvm::LandingPadInst *landingPad = createLandingPadInst(irs); + +@@ -724,7 +723,7 @@ llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() { + llvm::BasicBlock *afterCleanupBB = + irs.insertBB(beginBB->getName() + llvm::Twine(".after.cleanup")); + runCleanups(lastCleanup, newCleanup, afterCleanupBB); +- irs.scope() = IRScope(afterCleanupBB); ++ irs.ir->SetInsertPoint(afterCleanupBB); + lastCleanup = newCleanup; + } + +@@ -747,7 +746,7 @@ llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() { + irs.ir->CreateCondBr( + irs.ir->CreateICmpEQ(irs.ir->CreateLoad(ehSelectorSlot), ehTypeId), + cb.bodyBB, mismatchBB, cb.branchWeights); +- irs.scope() = IRScope(mismatchBB); ++ irs.ir->SetInsertPoint(mismatchBB); + } + } + +@@ -765,7 +764,7 @@ llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() { + irs.ir->CreateBr(resumeUnwindBlock); + } + +- irs.scope() = savedIRScope; ++ irs.setInsertPoint(savedInsertPoint); + return beginBB; + } + +@@ -780,13 +779,13 @@ llvm::BasicBlock *TryCatchFinallyScopes::getOrCreateResumeUnwindBlock() { + resumeUnwindBlock = irs.insertBB("eh.resume"); + + llvm::BasicBlock *oldBB = irs.scopebb(); +- irs.scope() = IRScope(resumeUnwindBlock); ++ irs.ir->SetInsertPoint(resumeUnwindBlock); + + llvm::Function *resumeFn = getUnwindResumeFunction(Loc(), irs.module); + irs.ir->CreateCall(resumeFn, DtoLoad(getOrCreateEhPtrSlot())); + irs.ir->CreateUnreachable(); + +- irs.scope() = IRScope(oldBB); ++ irs.ir->SetInsertPoint(oldBB); + } + return resumeUnwindBlock; + } +@@ -844,8 +843,7 @@ TryCatchFinallyScopes::runCleanupPad(CleanupCursor scope, + // can place an exception frame (but not done here) + auto frame = getNullPtr(getVoidPtrType()); + +- auto savedInsertBlock = irs.ir->GetInsertBlock(); +- auto savedInsertPoint = irs.ir->GetInsertPoint(); ++ auto savedInsertPoint = irs.getInsertPoint(); + auto savedDbgLoc = irs.DBuilder.GetCurrentLoc(); + + auto endFn = getRuntimeFunction(Loc(), irs.module, "_d_leave_cleanup"); +@@ -865,7 +863,7 @@ TryCatchFinallyScopes::runCleanupPad(CleanupCursor scope, + beginFn, frame, {llvm::OperandBundleDef("funclet", cleanuppad)}, ""); + llvm::BranchInst::Create(copybb, cleanupret, exec, cleanupbb); + +- irs.ir->SetInsertPoint(savedInsertBlock, savedInsertPoint); ++ irs.setInsertPoint(savedInsertPoint); + irs.DBuilder.EmitStopPoint(savedDbgLoc); + + return cleanupbb; +diff --git a/ir/irclass.cpp b/ir/irclass.cpp +index 9b315f2e..8eecc2cd 100644 +--- a/ir/irclass.cpp ++++ b/ir/irclass.cpp +@@ -560,7 +560,10 @@ void IrClass::defineInterfaceVtbl(BaseClass *b, bool new_instance, + // create entry and end blocks + llvm::BasicBlock *beginbb = + llvm::BasicBlock::Create(gIR->context(), "", thunk); +- gIR->scopes.push_back(IRScope(beginbb)); ++ ++ // set up the IRBuilder scope for the thunk ++ FunctionIRBuilderScope irBuilderScope(*gIR); ++ gIR->setInsertPoint(beginbb); + + gIR->DBuilder.EmitFuncStart(thunkFd); + +@@ -606,9 +609,6 @@ void IrClass::defineInterfaceVtbl(BaseClass *b, bool new_instance, + + gIR->DBuilder.EmitFuncEnd(thunkFd); + +- // clean up +- gIR->scopes.pop_back(); +- + gIR->funcGenStates.pop_back(); + } + +-- +2.29.0 + + +From 9c42dfd9d0c7ee2a8ef8896204fe7da7b39fc750 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Sun, 23 Aug 2020 17:29:12 +0200 +Subject: [PATCH 02/16] LLVM 11: Adapt debuginfos wrt. static array and vector + lengths + +To overcome a new assertion, complaining that subranges with lower +bounds (always 0) aren't supported for CodeView. +--- + gen/dibuilder.cpp | 27 +++++++++++++++++++-------- + 1 file changed, 19 insertions(+), 8 deletions(-) + +diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp +index 4675cc32..a5fdfceb 100644 +--- a/gen/dibuilder.cpp ++++ b/gen/dibuilder.cpp +@@ -385,14 +385,19 @@ DIType DIBuilder::CreateVectorType(Type *type) { + // translate void vectors to byte vectors + if (te->toBasetype()->ty == Tvoid) + te = Type::tuns8; +- int64_t Dim = tv->size(Loc()) / te->size(Loc()); +- LLMetadata *subscripts[] = {DBuilder.getOrCreateSubrange(0, Dim)}; ++ const auto dim = tv->size(Loc()) / te->size(Loc()); ++#if LDC_LLVM_VER >= 1100 ++ const auto Dim = llvm::ConstantAsMetadata::get(DtoConstSize_t(dim)); ++ auto subscript = DBuilder.getOrCreateSubrange(Dim, nullptr, nullptr, nullptr); ++#else ++ auto subscript = DBuilder.getOrCreateSubrange(0, dim); ++#endif + + return DBuilder.createVectorType( +- getTypeAllocSize(T) * 8, // size (bits) +- getABITypeAlign(T) * 8, // align (bits) +- CreateTypeDescription(te), // element type +- DBuilder.getOrCreateArray(subscripts) // subscripts ++ getTypeAllocSize(T) * 8, // size (bits) ++ getABITypeAlign(T) * 8, // align (bits) ++ CreateTypeDescription(te), // element type ++ DBuilder.getOrCreateArray({subscript}) // subscripts + ); + } + +@@ -681,8 +686,14 @@ DIType DIBuilder::CreateSArrayType(Type *type) { + llvm::SmallVector subscripts; + while (t->ty == Tsarray) { + TypeSArray *tsa = static_cast(t); +- int64_t Count = tsa->dim->toInteger(); +- auto subscript = DBuilder.getOrCreateSubrange(0, Count); ++ const auto count = tsa->dim->toInteger(); ++#if LDC_LLVM_VER >= 1100 ++ const auto Count = llvm::ConstantAsMetadata::get(DtoConstSize_t(count)); ++ const auto subscript = ++ DBuilder.getOrCreateSubrange(Count, nullptr, nullptr, nullptr); ++#else ++ const auto subscript = DBuilder.getOrCreateSubrange(0, count); ++#endif + subscripts.push_back(subscript); + t = t->nextOf(); + } +-- +2.29.0 + + +From 96b9cde4289bb82cc9100d9b52e70f7e4febfb1d Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Sun, 23 Aug 2020 14:13:13 +0200 +Subject: [PATCH 03/16] Add support for LLVM 11 + +One major change is the removal of llvm::CallSite, which I've replaced +by llvm::CallBase*. +--- + CMakeLists.txt | 7 +- + cmake/Modules/FindLLVM.cmake | 3 +- + dmd/root/longdouble.h | 2 + + driver/archiver.cpp | 3 + + driver/cache.cpp | 23 +- + driver/cl_options-llvm.cpp | 81 +- + driver/codegenerator.cpp | 13 +- + driver/ldmd.cpp | 4 + + driver/linker-gcc.cpp | 2 +- + driver/linker.cpp | 2 +- + gen/aa.cpp | 16 +- + gen/abi-generic.h | 7 +- + gen/arrays.cpp | 60 +- + gen/classes.cpp | 10 +- + gen/funcgenstate.cpp | 19 +- + gen/funcgenstate.h | 8 +- + gen/functions.cpp | 2 +- + gen/irstate.cpp | 41 +- + gen/irstate.h | 32 +- + gen/llvm.h | 9 +- + gen/llvmhelpers.cpp | 28 +- + gen/passes/GarbageCollect2Stack.cpp | 107 +- + gen/tocall.cpp | 26 +- + gen/toconstelem.cpp | 7 +- + gen/toir.cpp | 11 +- + gen/tollvm.cpp | 8 +- + gen/trycatchfinally.cpp | 10 +- + gen/uda.cpp | 6 + + ir/irtype.cpp | 7 +- + tests/lit.site.cfg.in | 2 +- + tools/ldc-profdata/llvm-profdata-11.0.cpp | 1344 +++++++++++++++++++++ + utils/FileCheck-11.0.cpp | 859 +++++++++++++ + utils/gen_gccbuiltins.cpp | 34 +- + 33 files changed, 2563 insertions(+), 230 deletions(-) + create mode 100644 tools/ldc-profdata/llvm-profdata-11.0.cpp + create mode 100644 utils/FileCheck-11.0.cpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index d6f7c596..46c2d3ae 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -450,7 +450,12 @@ include(HandleLTOPGOBuildOptions) + set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (ON|OFF). Enabled by default.") + option(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES "Use custom LDC passes in jit" ON) + if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO") +- set(LDC_DYNAMIC_COMPILE ON) ++ if(LDC_LLVM_VER LESS 1100) ++ set(LDC_DYNAMIC_COMPILE ON) ++ else() ++ # FIXME: support LLVM 11+ too ++ set(LDC_DYNAMIC_COMPILE OFF) ++ endif() + endif() + message(STATUS "Building LDC with dynamic compilation support: ${LDC_DYNAMIC_COMPILE} (LDC_DYNAMIC_COMPILE=${LDC_DYNAMIC_COMPILE})") + if(LDC_DYNAMIC_COMPILE) +diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake +index f02aa2c4..a192ccd3 100644 +--- a/cmake/Modules/FindLLVM.cmake ++++ b/cmake/Modules/FindLLVM.cmake +@@ -30,7 +30,8 @@ + # We also want an user-specified LLVM_ROOT_DIR to take precedence over the + # system default locations such as /usr/local/bin. Executing find_program() + # multiples times is the approach recommended in the docs. +-set(llvm_config_names llvm-config-10.0 llvm-config100 llvm-config-10 ++set(llvm_config_names llvm-config-11.0 llvm-config110 llvm-config-11 ++ llvm-config-10.0 llvm-config100 llvm-config-10 + llvm-config-9.0 llvm-config90 llvm-config-9 + llvm-config-8.0 llvm-config80 llvm-config-8 + llvm-config-7.0 llvm-config70 llvm-config-7 +diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h +index 7916d569..31282b63 100644 +--- a/dmd/root/longdouble.h ++++ b/dmd/root/longdouble.h +@@ -216,6 +216,7 @@ longdouble_soft ldexpl(longdouble_soft ldval, int exp); // see strtold + inline longdouble_soft fabs (longdouble_soft ld) { return fabsl(ld); } + inline longdouble_soft sqrt (longdouble_soft ld) { return sqrtl(ld); } + ++#if !IN_LLVM + #undef LDBL_DIG + #undef LDBL_MAX + #undef LDBL_MIN +@@ -235,6 +236,7 @@ inline longdouble_soft sqrt (longdouble_soft ld) { return sqrtl(ld); } + #define LDBL_MIN_EXP (-16381) + #define LDBL_MAX_10_EXP 4932 + #define LDBL_MIN_10_EXP (-4932) ++#endif // !IN_LLVM + + extern const longdouble_soft ld_qnan; + extern const longdouble_soft ld_inf; +diff --git a/driver/archiver.cpp b/driver/archiver.cpp +index c2c907b9..743afb42 100644 +--- a/driver/archiver.cpp ++++ b/driver/archiver.cpp +@@ -18,6 +18,9 @@ + #include "llvm/Object/MachO.h" + #include "llvm/Object/ObjectFile.h" + #include "llvm/Support/Errc.h" ++#if LDC_LLVM_VER >= 1100 ++#include "llvm/Support/Host.h" ++#endif + #include "llvm/Support/Path.h" + #include "llvm/Support/raw_ostream.h" + #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" +diff --git a/driver/cache.cpp b/driver/cache.cpp +index 77ddd3ff..bb0f26cb 100644 +--- a/driver/cache.cpp ++++ b/driver/cache.cpp +@@ -65,13 +65,22 @@ static bool createSymLink(const char *to, const char *from) { + #include + namespace llvm { + namespace sys { ++#if LDC_LLVM_VER >= 1100 ++namespace windows { ++// Fwd declaration to an internal LLVM function. ++std::error_code widenPath(const llvm::Twine &Path8, ++ llvm::SmallVectorImpl &Path16, ++ size_t MaxPathLen = MAX_PATH); ++} ++#else + namespace path { + // Fwd declaration to an internal LLVM function. + std::error_code widenPath(const llvm::Twine &Path8, + llvm::SmallVectorImpl &Path16); + } +-} +-} ++#endif // LDC_LLVM_VER < 1100 ++} // namespace sys ++} // namespace llvm + // Returns true upon error. + namespace { + template +@@ -83,11 +92,17 @@ bool createLink(FType f, const char *to, const char *from) { + // + //===----------------------------------------------------------------------===// + ++#if LDC_LLVM_VER >= 1100 ++ using llvm::sys::windows::widenPath; ++#else ++ using llvm::sys::path::widenPath; ++#endif ++ + llvm::SmallVector wide_from; + llvm::SmallVector wide_to; +- if (llvm::sys::path::widenPath(from, wide_from)) ++ if (widenPath(from, wide_from)) + return true; +- if (llvm::sys::path::widenPath(to, wide_to)) ++ if (widenPath(to, wide_to)) + return true; + + if (!(*f)(wide_from.begin(), wide_to.begin(), NULL)) +diff --git a/driver/cl_options-llvm.cpp b/driver/cl_options-llvm.cpp +index 4071074e..5071fd11 100644 +--- a/driver/cl_options-llvm.cpp ++++ b/driver/cl_options-llvm.cpp +@@ -11,7 +11,11 @@ + + // Pull in command-line options and helper functions from special LLVM header + // shared by multiple LLVM tools. +-#if LDC_LLVM_VER >= 700 ++#if LDC_LLVM_VER >= 1100 ++#include "llvm/CodeGen/CommandFlags.h" ++static llvm::codegen::RegisterCodeGenFlags CGF; ++using namespace llvm; ++#elif LDC_LLVM_VER >= 700 + #include "llvm/CodeGen/CommandFlags.inc" + #else + #include "llvm/CodeGen/CommandFlags.def" +@@ -21,7 +25,7 @@ static cl::opt + DisableRedZone("disable-red-zone", cl::ZeroOrMore, + cl::desc("Do not emit code that uses the red zone.")); + +-#if LDC_LLVM_VER >= 800 ++#if LDC_LLVM_VER >= 800 && LDC_LLVM_VER < 1100 + // legacy option + static cl::opt + disableFPElim("disable-fp-elim", cl::ZeroOrMore, cl::ReallyHidden, +@@ -32,19 +36,41 @@ static cl::opt + // in the opts namespace, including some additional helper functions. + namespace opts { + +-std::string getArchStr() { return ::MArch; } ++std::string getArchStr() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::getMArch(); ++#else ++ return ::MArch; ++#endif ++} + +-Optional getRelocModel() { return ::getRelocModel(); } ++Optional getRelocModel() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::getExplicitRelocModel(); ++#else ++ return ::getRelocModel(); ++#endif ++} + +-Optional getCodeModel() { return ::getCodeModel(); } ++Optional getCodeModel() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::getExplicitCodeModel(); ++#else ++ return ::getCodeModel(); ++#endif ++} + + #if LDC_LLVM_VER >= 800 + llvm::Optional framePointerUsage() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::getFramePointerUsage(); ++#else + if (::FramePointerUsage.getNumOccurrences() > 0) + return ::FramePointerUsage.getValue(); + if (disableFPElim.getNumOccurrences() > 0) + return disableFPElim ? llvm::FramePointer::All : llvm::FramePointer::None; + return llvm::None; ++#endif + } + #else + cl::boolOrDefault disableFPElim() { +@@ -57,6 +83,10 @@ cl::boolOrDefault disableFPElim() { + bool disableRedZone() { return ::DisableRedZone; } + + bool printTargetFeaturesHelp() { ++#if LDC_LLVM_VER >= 1100 ++ const auto MCPU = codegen::getMCPU(); ++ const auto MAttrs = codegen::getMAttrs(); ++#endif + if (MCPU == "help") + return true; + return std::any_of(MAttrs.begin(), MAttrs.end(), +@@ -64,11 +94,28 @@ bool printTargetFeaturesHelp() { + } + + TargetOptions InitTargetOptionsFromCodeGenFlags() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::InitTargetOptionsFromCodeGenFlags(); ++#else + return ::InitTargetOptionsFromCodeGenFlags(); ++#endif ++} ++ ++std::string getCPUStr() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::getCPUStr(); ++#else ++ return ::getCPUStr(); ++#endif + } + +-std::string getCPUStr() { return ::getCPUStr(); } +-std::string getFeaturesStr() { return ::getFeaturesStr(); } ++std::string getFeaturesStr() { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::getFeaturesStr(); ++#else ++ return ::getFeaturesStr(); ++#endif ++} + } // namespace opts + + #if LDC_WITH_LLD +@@ -81,30 +128,32 @@ TargetOptions initTargetOptionsFromCodeGenFlags() { + #else + TargetOptions InitTargetOptionsFromCodeGenFlags() { + #endif +- return ::InitTargetOptionsFromCodeGenFlags(); ++ return ::opts::InitTargetOptionsFromCodeGenFlags(); + } + + #if LDC_LLVM_VER >= 1000 +-Optional getRelocModelFromCMModel() { return ::getRelocModel(); } ++Optional getRelocModelFromCMModel() { ++ return ::opts::getRelocModel(); ++} + #endif + + #if LDC_LLVM_VER >= 900 + Optional getCodeModelFromCMModel() { +- return ::getCodeModel(); +-} + #else + Optional GetCodeModelFromCMModel() { +- return ::getCodeModel(); +-} + #endif ++ return ::opts::getCodeModel(); ++} + + #if LDC_LLVM_VER >= 900 +-std::string getCPUStr() { return ::getCPUStr(); } ++std::string getCPUStr() { return ::opts::getCPUStr(); } + #elif LDC_LLVM_VER >= 700 +-std::string GetCPUStr() { return ::getCPUStr(); } ++std::string GetCPUStr() { return ::opts::getCPUStr(); } + #endif + +-#if LDC_LLVM_VER >= 900 ++#if LDC_LLVM_VER >= 1100 ++std::vector getMAttrs() { return codegen::getMAttrs(); } ++#elif LDC_LLVM_VER >= 900 + std::vector getMAttrs() { return ::MAttrs; } + #elif LDC_LLVM_VER >= 800 + std::vector GetMAttrs() { return ::MAttrs; } +diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp +index 0833df46..9edbbcf9 100644 +--- a/driver/codegenerator.cpp ++++ b/driver/codegenerator.cpp +@@ -23,7 +23,9 @@ + #include "gen/logger.h" + #include "gen/modules.h" + #include "gen/runtime.h" +-#if LDC_LLVM_VER >= 900 ++#if LDC_LLVM_VER >= 1100 ++#include "llvm/IR/LLVMRemarkStreamer.h" ++#elif LDC_LLVM_VER >= 900 + #include "llvm/IR/RemarkStreamer.h" + #endif + #include "llvm/Support/FileSystem.h" +@@ -52,8 +54,13 @@ createAndSetDiagnosticsOutputFile(IRState &irs, llvm::LLVMContext &ctx, + const bool withHotness = opts::isUsingPGOProfile(); + + #if LDC_LLVM_VER >= 900 +- auto remarksFileOrError = llvm::setupOptimizationRemarks( +- ctx, diagnosticsFilename, "", "", withHotness); ++ auto remarksFileOrError = ++#if LDC_LLVM_VER >= 1100 ++ llvm::setupLLVMOptimizationRemarks( ++#else ++ llvm::setupOptimizationRemarks( ++#endif ++ ctx, diagnosticsFilename, "", "", withHotness); + if (llvm::Error e = remarksFileOrError.takeError()) { + irs.dmodule->error("Could not create file %s: %s", + diagnosticsFilename.c_str(), +diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp +index e4dc80d6..ef50e47f 100644 +--- a/driver/ldmd.cpp ++++ b/driver/ldmd.cpp +@@ -476,7 +476,11 @@ void translateArgs(const llvm::SmallVectorImpl &ldmdArgs, + else if (strcmp(p + 1, "gf") == 0) { + ldcArgs.push_back("-g"); + } else if (strcmp(p + 1, "gs") == 0) { ++#if LDC_LLVM_VER >= 1100 ++ ldcArgs.push_back("-frame-pointer=all"); ++#else + ldcArgs.push_back("-disable-fp-elim"); ++#endif + } else if (strcmp(p + 1, "gx") == 0) { + goto Lnot_in_ldc; + } else if (strcmp(p + 1, "gt") == 0) { +diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp +index 1b364fb7..e2a25231 100644 +--- a/driver/linker-gcc.cpp ++++ b/driver/linker-gcc.cpp +@@ -321,7 +321,7 @@ void ArgsBuilder::addSanitizerLinkFlags(const llvm::Triple &triple, + + // When we reach here, we did not find the sanitizer library. + // Fallback, requires Clang. +- args.push_back(fallbackFlag); ++ args.emplace_back(fallbackFlag); + } + + // Adds all required link flags for -fsanitize=fuzzer when libFuzzer library is +diff --git a/driver/linker.cpp b/driver/linker.cpp +index 3fc08ca9..ab3ab205 100644 +--- a/driver/linker.cpp ++++ b/driver/linker.cpp +@@ -145,7 +145,7 @@ static std::vector + parseLibNames(llvm::StringRef commaSeparatedList, llvm::StringRef suffix = {}) { + std::vector result; + +- std::stringstream list(commaSeparatedList); ++ std::stringstream list(commaSeparatedList.str()); + while (list.good()) { + std::string lib; + std::getline(list, lib, ','); +diff --git a/gen/aa.cpp b/gen/aa.cpp +index 5344dc33..0c011f87 100644 +--- a/gen/aa.cpp ++++ b/gen/aa.cpp +@@ -65,12 +65,10 @@ DLValue *DtoAAIndex(Loc &loc, Type *type, DValue *aa, DValue *key, + LLValue *castedAATI = DtoBitCast(rawAATI, funcTy->getParamType(1)); + LLValue *valsize = DtoConstSize_t(getTypeAllocSize(DtoType(type))); + ret = gIR->CreateCallOrInvoke(func, aaval, castedAATI, valsize, pkey, +- "aa.index") +- .getInstruction(); ++ "aa.index"); + } else { + LLValue *keyti = to_keyti(aa, funcTy->getParamType(1)); +- ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index") +- .getInstruction(); ++ ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index"); + } + + // cast return value +@@ -134,8 +132,7 @@ DValue *DtoAAIn(Loc &loc, Type *type, DValue *aa, DValue *key) { + pkey = DtoBitCast(pkey, getVoidPtrType()); + + // call runtime +- LLValue *ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in") +- .getInstruction(); ++ LLValue *ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in"); + + // cast return value + LLType *targettype = DtoType(type); +@@ -179,9 +176,9 @@ DValue *DtoAARemove(Loc &loc, DValue *aa, DValue *key) { + pkey = DtoBitCast(pkey, funcTy->getParamType(2)); + + // call runtime +- LLCallSite call = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey); ++ LLValue *res = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey); + +- return new DImValue(Type::tbool, call.getInstruction()); ++ return new DImValue(Type::tbool, res); + } + + //////////////////////////////////////////////////////////////////////////////// +@@ -197,8 +194,7 @@ LLValue *DtoAAEquals(Loc &loc, TOK op, DValue *l, DValue *r) { + LLValue *abval = DtoBitCast(DtoRVal(r), funcTy->getParamType(2)); + LLValue *aaTypeInfo = DtoTypeInfoOf(t); + LLValue *res = +- gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes") +- .getInstruction(); ++ gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes"); + + const auto predicate = eqTokToICmpPred(op, /* invert = */ true); + res = gIR->ir->CreateICmp(predicate, res, DtoConstInt(0)); +diff --git a/gen/abi-generic.h b/gen/abi-generic.h +index b4d4999e..f7c41f42 100644 +--- a/gen/abi-generic.h ++++ b/gen/abi-generic.h +@@ -36,7 +36,12 @@ struct LLTypeMemoryLayout { + const size_t sizeInBits = getTypeBitSize(type); + assert(sizeInBits % 8 == 0); + return llvm::VectorType::get(LLIntegerType::get(gIR->context(), 8), +- sizeInBits / 8); ++ sizeInBits / 8 ++#if LDC_LLVM_VER >= 1100 ++ , ++ /*Scalable=*/false ++#endif ++ ); + } + + if (LLStructType *structType = isaStruct(type)) { +diff --git a/gen/arrays.cpp b/gen/arrays.cpp +index 0d867b07..3d2a3f56 100644 +--- a/gen/arrays.cpp ++++ b/gen/arrays.cpp +@@ -198,7 +198,7 @@ static void copySlice(Loc &loc, LLValue *dstarr, LLValue *dstlen, LLValue *srcar + const bool checksEnabled = + global.params.useAssert == CHECKENABLEon || gIR->emitArrayBoundsChecks(); + if (checksEnabled && !knownInBounds) { +- LLValue *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy"); ++ LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy"); + gIR->CreateCallOrInvoke( + fn, {dstarr, dstlen, srcarr, srclen, DtoConstSize_t(elementSize)}, "", + /*isNothrow=*/true); +@@ -293,20 +293,18 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op, + } + } else if (isConstructing) { + LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayctor"); +- LLCallSite call = gIR->CreateCallOrInvoke(fn, DtoTypeInfoOf(elemType), +- DtoSlice(rhsPtr, rhsLength), +- DtoSlice(lhsPtr, lhsLength)); +- call.setCallingConv(llvm::CallingConv::C); ++ gIR->CreateCallOrInvoke(fn, DtoTypeInfoOf(elemType), ++ DtoSlice(rhsPtr, rhsLength), ++ DtoSlice(lhsPtr, lhsLength)); + } else // assigning + { + LLValue *tmpSwap = DtoAlloca(elemType, "arrayAssign.tmpSwap"); + LLFunction *fn = getRuntimeFunction( + loc, gIR->module, + !canSkipPostblit ? "_d_arrayassign_l" : "_d_arrayassign_r"); +- LLCallSite call = gIR->CreateCallOrInvoke( ++ gIR->CreateCallOrInvoke( + fn, DtoTypeInfoOf(elemType), DtoSlice(rhsPtr, rhsLength), + DtoSlice(lhsPtr, lhsLength), DtoBitCast(tmpSwap, getVoidPtrType())); +- call.setCallingConv(llvm::CallingConv::C); + } + } else { + // scalar rhs: +@@ -335,12 +333,11 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op, + LLFunction *fn = getRuntimeFunction(loc, gIR->module, + isConstructing ? "_d_arraysetctor" + : "_d_arraysetassign"); +- LLCallSite call = gIR->CreateCallOrInvoke( ++ gIR->CreateCallOrInvoke( + fn, lhsPtr, DtoBitCast(makeLValue(loc, rhs), getVoidPtrType()), + gIR->ir->CreateTruncOrBitCast(lhsLength, + LLType::getInt32Ty(gIR->context())), + DtoTypeInfoOf(stripModifiers(t2))); +- call.setCallingConv(llvm::CallingConv::C); + } + } + } +@@ -704,8 +701,7 @@ DSliceValue *DtoNewDynArray(Loc &loc, Type *arrayType, DValue *dim, + + // call allocator + LLValue *newArray = +- gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem") +- .getInstruction(); ++ gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem"); + + // return a DSliceValue with the well-known length for better optimizability + auto ptr = +@@ -774,8 +770,7 @@ DSliceValue *DtoNewMulDimDynArray(Loc &loc, Type *arrayType, DValue **dims, + + // call allocator + LLValue *newptr = +- gIR->CreateCallOrInvoke(fn, arrayTypeInfo, DtoLoad(darray), ".gc_mem") +- .getInstruction(); ++ gIR->CreateCallOrInvoke(fn, arrayTypeInfo, DtoLoad(darray), ".gc_mem"); + + IF_LOG Logger::cout() << "final ptr = " << *newptr << '\n'; + +@@ -802,12 +797,10 @@ DSliceValue *DtoResizeDynArray(Loc &loc, Type *arrayType, DValue *array, + getRuntimeFunction(loc, gIR->module, zeroInit ? "_d_arraysetlengthT" + : "_d_arraysetlengthiT"); + +- LLValue *newArray = +- gIR->CreateCallOrInvoke( +- fn, DtoTypeInfoOf(arrayType), newdim, +- DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)), +- ".gc_mem") +- .getInstruction(); ++ LLValue *newArray = gIR->CreateCallOrInvoke( ++ fn, DtoTypeInfoOf(arrayType), newdim, ++ DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)), ++ ".gc_mem"); + + return getSlice(arrayType, newArray); + } +@@ -853,14 +846,11 @@ DSliceValue *DtoCatAssignArray(Loc &loc, DValue *arr, Expression *exp) { + + LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayappendT"); + // Call _d_arrayappendT(TypeInfo ti, byte[] *px, byte[] y) +- LLValue *newArray = +- gIR->CreateCallOrInvoke( +- fn, DtoTypeInfoOf(arrayType), +- DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(1)), +- DtoAggrPaint(DtoSlice(exp), +- fn->getFunctionType()->getParamType(2)), +- ".appendedArray") +- .getInstruction(); ++ LLValue *newArray = gIR->CreateCallOrInvoke( ++ fn, DtoTypeInfoOf(arrayType), ++ DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(1)), ++ DtoAggrPaint(DtoSlice(exp), fn->getFunctionType()->getParamType(2)), ++ ".appendedArray"); + + return getSlice(arrayType, newArray); + } +@@ -929,8 +919,7 @@ DSliceValue *DtoCatArrays(Loc &loc, Type *arrayType, Expression *exp1, + args.push_back(val); + } + +- auto newArray = +- gIR->CreateCallOrInvoke(fn, args, ".appendedArray").getInstruction(); ++ auto newArray = gIR->CreateCallOrInvoke(fn, args, ".appendedArray"); + return getSlice(arrayType, newArray); + } + +@@ -944,13 +933,10 @@ DSliceValue *DtoAppendDChar(Loc &loc, DValue *arr, Expression *exp, + LLFunction *fn = getRuntimeFunction(loc, gIR->module, func); + + // Call function (ref string x, dchar c) +- LLValue *newArray = +- gIR->CreateCallOrInvoke( +- fn, +- DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(0)), +- DtoBitCast(valueToAppend, fn->getFunctionType()->getParamType(1)), +- ".appendedArray") +- .getInstruction(); ++ LLValue *newArray = gIR->CreateCallOrInvoke( ++ fn, DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(0)), ++ DtoBitCast(valueToAppend, fn->getFunctionType()->getParamType(1)), ++ ".appendedArray"); + + return getSlice(arr->type, newArray); + } +@@ -1003,7 +989,7 @@ LLValue *DtoArrayEqCmp_impl(Loc &loc, const char *func, DValue *l, + args.push_back(DtoBitCast(tival, fn->getFunctionType()->getParamType(2))); + } + +- return gIR->CreateCallOrInvoke(fn, args).getInstruction(); ++ return gIR->CreateCallOrInvoke(fn, args); + } + + /// When `true` is returned, the type can be compared using `memcmp`. +diff --git a/gen/classes.cpp b/gen/classes.cpp +index 0777ce02..b78f9295 100644 +--- a/gen/classes.cpp ++++ b/gen/classes.cpp +@@ -103,10 +103,8 @@ DValue *DtoNewClass(Loc &loc, TypeClass *tc, NewExp *newexp) { + loc, gIR->module, useEHAlloc ? "_d_newThrowable" : "_d_allocclass"); + LLConstant *ci = DtoBitCast(getIrAggr(tc->sym)->getClassInfoSymbol(), + DtoType(getClassInfoType())); +- mem = gIR->CreateCallOrInvoke(fn, ci, +- useEHAlloc ? ".newthrowable_alloc" +- : ".newclass_gc_alloc") +- .getInstruction(); ++ mem = gIR->CreateCallOrInvoke( ++ fn, ci, useEHAlloc ? ".newthrowable_alloc" : ".newclass_gc_alloc"); + mem = DtoBitCast(mem, DtoType(tc), + useEHAlloc ? ".newthrowable" : ".newclass_gc"); + doInit = !useEHAlloc; +@@ -375,7 +373,7 @@ DValue *DtoDynamicCastObject(Loc &loc, DValue *val, Type *_to) { + assert(funcTy->getParamType(1) == cinfo->getType()); + + // call it +- LLValue *ret = gIR->CreateCallOrInvoke(func, obj, cinfo).getInstruction(); ++ LLValue *ret = gIR->CreateCallOrInvoke(func, obj, cinfo); + + // cast return value + ret = DtoBitCast(ret, DtoType(_to)); +@@ -409,7 +407,7 @@ DValue *DtoDynamicCastInterface(Loc &loc, DValue *val, Type *_to) { + cinfo = DtoBitCast(cinfo, funcTy->getParamType(1)); + + // call it +- LLValue *ret = gIR->CreateCallOrInvoke(func, ptr, cinfo).getInstruction(); ++ LLValue *ret = gIR->CreateCallOrInvoke(func, ptr, cinfo); + + // cast return value + ret = DtoBitCast(ret, DtoType(_to)); +diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp +index e2c16958..ae0504e7 100644 +--- a/gen/funcgenstate.cpp ++++ b/gen/funcgenstate.cpp +@@ -103,9 +103,10 @@ FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) + : irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(), + irs(irs) {} + +-llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee, +- llvm::ArrayRef args, +- const char *name, bool isNothrow) { ++llvm::CallBase *FuncGenState::callOrInvoke(llvm::Value *callee, ++ llvm::FunctionType *calleeType, ++ llvm::ArrayRef args, ++ const char *name, bool isNothrow) { + // If this is a direct call, we might be able to use the callee attributes + // to our advantage. + llvm::Function *calleeFn = llvm::dyn_cast(callee); +@@ -123,8 +124,14 @@ llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee, + // calls inside a funclet must be annotated with its value + llvm::SmallVector BundleList; + ++#if LDC_LLVM_VER >= 1100 ++ llvm::FunctionCallee calleeArg(calleeType, callee); ++#else ++ auto calleeArg = callee; ++#endif ++ + if (doesNotThrow || scopes.empty()) { +- llvm::CallInst *call = irs.ir->CreateCall(callee, args, BundleList, name); ++ auto call = irs.ir->CreateCall(calleeArg, args, BundleList, name); + if (calleeFn) { + call->setAttributes(calleeFn->getAttributes()); + } +@@ -134,8 +141,8 @@ llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee, + llvm::BasicBlock *landingPad = scopes.getLandingPad(); + + llvm::BasicBlock *postinvoke = irs.insertBB("postinvoke"); +- llvm::InvokeInst *invoke = irs.ir->CreateInvoke( +- callee, postinvoke, landingPad, args, BundleList, name); ++ auto invoke = irs.ir->CreateInvoke(calleeArg, postinvoke, landingPad, args, ++ BundleList, name); + if (calleeFn) { + invoke->setAttributes(calleeFn->getAttributes()); + } +diff --git a/gen/funcgenstate.h b/gen/funcgenstate.h +index 365b38bf..bd1edc0d 100644 +--- a/gen/funcgenstate.h ++++ b/gen/funcgenstate.h +@@ -18,7 +18,6 @@ + #include "gen/pgo_ASTbased.h" + #include "gen/trycatchfinally.h" + #include "llvm/ADT/DenseMap.h" +-#include "llvm/IR/CallSite.h" + #include + + class Identifier; +@@ -200,9 +199,10 @@ public: + + /// Emits a call or invoke to the given callee, depending on whether there + /// are catches/cleanups active or not. +- llvm::CallSite callOrInvoke(llvm::Value *callee, +- llvm::ArrayRef args, +- const char *name = "", bool isNothrow = false); ++ llvm::CallBase *callOrInvoke(llvm::Value *callee, ++ llvm::FunctionType *calleeType, ++ llvm::ArrayRef args, ++ const char *name = "", bool isNothrow = false); + + private: + IRState &irs; +diff --git a/gen/functions.cpp b/gen/functions.cpp +index 554102ca..46c10c46 100644 +--- a/gen/functions.cpp ++++ b/gen/functions.cpp +@@ -926,7 +926,7 @@ void emulateWeakAnyLinkageForMSVC(LLFunction *func, LINK linkage) { + finalMangle = mangleBuffer; + } + +- std::string finalWeakMangle = finalMangle; ++ std::string finalWeakMangle = finalMangle.str(); + if (linkage == LINKcpp) { + assert(finalMangle.startswith("?")); + // prepend `__weak_` to first identifier +diff --git a/gen/irstate.cpp b/gen/irstate.cpp +index 0ac6ddc3..eb73ab41 100644 +--- a/gen/irstate.cpp ++++ b/gen/irstate.cpp +@@ -99,35 +99,42 @@ llvm::BasicBlock *IRState::insertBB(const llvm::Twine &name) { + return insertBBAfter(scopebb(), name); + } + +-LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, const char *Name) { +- return funcGen().callOrInvoke(Callee, {}, Name); ++llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, ++ const char *Name) { ++ return CreateCallOrInvoke(Callee, {}, Name); + } + +-LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, +- llvm::ArrayRef Args, +- const char *Name, bool isNothrow) { +- return funcGen().callOrInvoke(Callee, Args, Name, isNothrow); ++llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, ++ llvm::ArrayRef Args, ++ const char *Name, ++ bool isNothrow) { ++ return funcGen().callOrInvoke(Callee, Callee->getFunctionType(), Args, Name, ++ isNothrow); + } + +-LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- const char *Name) { +- return funcGen().callOrInvoke(Callee, {Arg1}, Name); ++llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, ++ LLValue *Arg1, ++ const char *Name) { ++ return CreateCallOrInvoke(Callee, llvm::ArrayRef(Arg1), Name); + } + +-LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- LLValue *Arg2, const char *Name) { ++llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, ++ LLValue *Arg1, LLValue *Arg2, ++ const char *Name) { + return CreateCallOrInvoke(Callee, {Arg1, Arg2}, Name); + } + +-LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- LLValue *Arg2, LLValue *Arg3, +- const char *Name) { ++llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, ++ LLValue *Arg1, LLValue *Arg2, ++ LLValue *Arg3, ++ const char *Name) { + return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3}, Name); + } + +-LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- LLValue *Arg2, LLValue *Arg3, +- LLValue *Arg4, const char *Name) { ++llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, ++ LLValue *Arg1, LLValue *Arg2, ++ LLValue *Arg3, LLValue *Arg4, ++ const char *Name) { + return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3, Arg4}, Name); + } + +diff --git a/gen/irstate.h b/gen/irstate.h +index 340bfb57..83a33310 100644 +--- a/gen/irstate.h ++++ b/gen/irstate.h +@@ -22,7 +22,6 @@ + #include "ir/irvar.h" + #include "llvm/ADT/DenseMap.h" + #include "llvm/ADT/StringMap.h" +-#include "llvm/IR/CallSite.h" + #include "llvm/ProfileData/InstrProfReader.h" + #include + #include +@@ -188,21 +187,22 @@ public: + llvm::BasicBlock *insertBB(const llvm::Twine &name); + + // create a call or invoke, depending on the landing pad info +- llvm::CallSite CreateCallOrInvoke(LLValue *Callee, const char *Name = ""); +- llvm::CallSite CreateCallOrInvoke(LLValue *Callee, +- llvm::ArrayRef Args, +- const char *Name = "", +- bool isNothrow = false); +- llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- const char *Name = ""); +- llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- LLValue *Arg2, const char *Name = ""); +- llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- LLValue *Arg2, LLValue *Arg3, +- const char *Name = ""); +- llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, +- LLValue *Arg2, LLValue *Arg3, LLValue *Arg4, +- const char *Name = ""); ++ llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, ++ const char *Name = ""); ++ llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, ++ llvm::ArrayRef Args, ++ const char *Name = "", ++ bool isNothrow = false); ++ llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, ++ const char *Name = ""); ++ llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, ++ LLValue *Arg2, const char *Name = ""); ++ llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, ++ LLValue *Arg2, LLValue *Arg3, ++ const char *Name = ""); ++ llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, ++ LLValue *Arg2, LLValue *Arg3, ++ LLValue *Arg4, const char *Name = ""); + + // this holds the array being indexed or sliced so $ will work + // might be a better way but it works. problem is I only get a +diff --git a/gen/llvm.h b/gen/llvm.h +index ade01294..a4870c80 100644 +--- a/gen/llvm.h ++++ b/gen/llvm.h +@@ -30,7 +30,6 @@ + #include "llvm/IR/DataLayout.h" + #include "llvm/IR/IRBuilder.h" + #include "llvm/IR/DebugInfo.h" +-#include "llvm/IR/CallSite.h" + + #if LDC_LLVM_VER >= 1000 + // LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back +@@ -46,8 +45,14 @@ using llvm::APInt; + using llvm::IRBuilder; + + #if LDC_LLVM_VER >= 1000 ++#if LDC_LLVM_VER >= 1100 ++#define LLAlign llvm::Align ++#else ++#define LLAlign llvm::MaybeAlign ++#endif + #define LLMaybeAlign llvm::MaybeAlign + #else ++#define LLAlign + #define LLMaybeAlign + #endif + +@@ -75,6 +80,4 @@ using llvm::IRBuilder; + #define LLConstantInt llvm::ConstantInt + #define LLConstantFP llvm::ConstantFP + +-#define LLCallSite llvm::CallSite +- + #define LLSmallVector llvm::SmallVector +diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp +index 8096e93f..6024d827 100644 +--- a/gen/llvmhelpers.cpp ++++ b/gen/llvmhelpers.cpp +@@ -85,7 +85,7 @@ LLValue *DtoNew(Loc &loc, Type *newtype) { + LLConstant *ti = DtoTypeInfoOf(newtype); + assert(isaPointer(ti)); + // call runtime allocator +- LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem").getInstruction(); ++ LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem"); + // cast + return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_mem"); + } +@@ -95,7 +95,7 @@ LLValue *DtoNewStruct(Loc &loc, TypeStruct *newtype) { + loc, gIR->module, + newtype->isZeroInit(newtype->sym->loc) ? "_d_newitemT" : "_d_newitemiT"); + LLConstant *ti = DtoTypeInfoOf(newtype); +- LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct").getInstruction(); ++ LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct"); + return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_struct"); + } + +@@ -180,7 +180,9 @@ llvm::AllocaInst *DtoArrayAlloca(Type *type, unsigned arraysize, + auto ai = new llvm::AllocaInst( + lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), + DtoConstUint(arraysize), name, gIR->topallocapoint()); +- ai->setAlignment(LLMaybeAlign(DtoAlignment(type))); ++ if (auto alignment = DtoAlignment(type)) { ++ ai->setAlignment(LLAlign(alignment)); ++ } + return ai; + } + +@@ -190,7 +192,7 @@ llvm::AllocaInst *DtoRawAlloca(LLType *lltype, size_t alignment, + lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), name, + gIR->topallocapoint()); + if (alignment) { +- ai->setAlignment(LLMaybeAlign(alignment)); ++ ai->setAlignment(LLAlign(alignment)); + } + return ai; + } +@@ -201,7 +203,7 @@ LLValue *DtoGcMalloc(Loc &loc, LLType *lltype, const char *name) { + // parameters + LLValue *size = DtoConstSize_t(getTypeAllocSize(lltype)); + // call runtime allocator +- LLValue *mem = gIR->CreateCallOrInvoke(fn, size, name).getInstruction(); ++ LLValue *mem = gIR->CreateCallOrInvoke(fn, size, name); + // cast + return DtoBitCast(mem, getPtrToType(lltype), name); + } +@@ -1231,7 +1233,12 @@ LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp) { + assert(tv->basetype->ty == Tsarray); + dinteger_t elemCount = + static_cast(tv->basetype)->dim->toInteger(); +- return llvm::ConstantVector::getSplat(elemCount, val); ++#if LDC_LLVM_VER >= 1100 ++ const auto elementCount = llvm::ElementCount(elemCount, false); ++#else ++ const auto elementCount = elemCount; ++#endif ++ return llvm::ConstantVector::getSplat(elementCount, val); + } + + if (llType->isIntegerTy() && targetLLType->isIntegerTy()) { +@@ -1310,9 +1317,14 @@ static char *DtoOverloadedIntrinsicName(TemplateInstance *ti, + if (dtype->isPPC_FP128Ty()) { // special case + replacement = "ppcf128"; + } else if (dtype->isVectorTy()) { ++#if LDC_LLVM_VER >= 1100 ++ auto vectorType = llvm::cast(dtype); ++#else ++ auto vectorType = llvm::cast(dtype); ++#endif + llvm::raw_string_ostream stream(replacement); +- stream << 'v' << dtype->getVectorNumElements() << prefix +- << gDataLayout->getTypeSizeInBits(dtype->getVectorElementType()); ++ stream << 'v' << vectorType->getNumElements() << prefix ++ << gDataLayout->getTypeSizeInBits(vectorType->getElementType()); + stream.flush(); + } else { + replacement = prefix + std::to_string(gDataLayout->getTypeSizeInBits(dtype)); +diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp +index a726f844..9463b190 100644 +--- a/gen/passes/GarbageCollect2Stack.cpp ++++ b/gen/passes/GarbageCollect2Stack.cpp +@@ -28,7 +28,6 @@ + #include "llvm/ADT/SmallVector.h" + #include "llvm/ADT/Statistic.h" + #include "llvm/ADT/StringMap.h" +-#include "llvm/IR/CallSite.h" + #include "llvm/IR/Constants.h" + #include "llvm/IR/DataLayout.h" + #include "llvm/IR/Dominators.h" +@@ -115,14 +114,14 @@ public: + + // Analyze the current call, filling in some fields. Returns true if + // this is an allocation we can stack-allocate. +- virtual bool analyze(CallSite CS, const Analysis &A) = 0; ++ virtual bool analyze(CallBase *CB, const Analysis &A) = 0; + + // Returns the alloca to replace this call. + // It will always be inserted before the call. +- virtual Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) { ++ virtual Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) { + NumGcToStack++; + +- auto &BB = CS.getCaller()->getEntryBlock(); ++ auto &BB = CB->getCaller()->getEntryBlock(); + Instruction *Begin = &(*BB.begin()); + + // FIXME: set alignment on alloca? +@@ -167,8 +166,8 @@ public: + TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) + : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} + +- bool analyze(CallSite CS, const Analysis &A) override { +- Value *TypeInfo = CS.getArgument(TypeInfoArgNr); ++ bool analyze(CallBase *CB, const Analysis &A) override { ++ Value *TypeInfo = CB->getArgOperand(TypeInfoArgNr); + Ty = A.getTypeFor(TypeInfo); + if (!Ty) { + return false; +@@ -188,12 +187,12 @@ public: + : TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr), + Initialized(initialized) {} + +- bool analyze(CallSite CS, const Analysis &A) override { +- if (!TypeInfoFI::analyze(CS, A)) { ++ bool analyze(CallBase *CB, const Analysis &A) override { ++ if (!TypeInfoFI::analyze(CB, A)) { + return false; + } + +- arrSize = CS.getArgument(ArrSizeArgNr); ++ arrSize = CB->getArgOperand(ArrSizeArgNr); + + // Extract the element type from the array type. + const StructType *ArrTy = dyn_cast(Ty); +@@ -217,15 +216,16 @@ public: + return true; + } + +- Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) override { +- IRBuilder<> Builder = B; ++ Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) override { ++ IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint()); ++ + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. + if (isa(arrSize)) { +- BasicBlock &Entry = CS.getCaller()->getEntryBlock(); ++ BasicBlock &Entry = CB->getCaller()->getEntryBlock(); + if (Builder.GetInsertBlock() != &Entry) { + Builder.SetInsertPoint(&Entry, Entry.begin()); + } +@@ -250,7 +250,7 @@ public: + } + + if (ReturnType == ReturnType::Array) { +- Value *arrStruct = llvm::UndefValue::get(CS.getType()); ++ Value *arrStruct = llvm::UndefValue::get(CB->getType()); + arrStruct = Builder.CreateInsertValue(arrStruct, arrSize, 0); + Value *memPtr = + Builder.CreateBitCast(alloca, PointerType::getUnqual(B.getInt8Ty())); +@@ -265,11 +265,11 @@ public: + // FunctionInfo for _d_allocclass + class AllocClassFI : public FunctionInfo { + public: +- bool analyze(CallSite CS, const Analysis &A) override { +- if (CS.arg_size() != 1) { ++ bool analyze(CallBase *CB, const Analysis &A) override { ++ if (CB->arg_size() != 1) { + return false; + } +- Value *arg = CS.getArgument(0)->stripPointerCasts(); ++ Value *arg = CB->getArgOperand(0)->stripPointerCasts(); + GlobalVariable *ClassInfo = dyn_cast(arg); + if (!ClassInfo) { + return false; +@@ -322,12 +322,12 @@ class UntypedMemoryFI : public FunctionInfo { + Value *SizeArg; + + public: +- bool analyze(CallSite CS, const Analysis &A) override { +- if (CS.arg_size() < SizeArgNr + 1) { ++ bool analyze(CallBase *CB, const Analysis &A) override { ++ if (CB->arg_size() < SizeArgNr + 1) { + return false; + } + +- SizeArg = CS.getArgument(SizeArgNr); ++ SizeArg = CB->getArgOperand(SizeArgNr); + + // If the user explicitly disabled the limits, don't even check + // whether the allocated size fits in 32 bits. This could cause +@@ -341,19 +341,20 @@ public: + } + + // Should be i8. +- Ty = CS.getType()->getContainedType(0); ++ Ty = CB->getType()->getContainedType(0); + return true; + } + +- Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) override { +- IRBuilder<> Builder = B; ++ Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) override { ++ IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint()); ++ + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. + if (isa(SizeArg)) { +- BasicBlock &Entry = CS.getCaller()->getEntryBlock(); ++ BasicBlock &Entry = CB->getCaller()->getEntryBlock(); + if (Builder.GetInsertBlock() != &Entry) { + Builder.SetInsertPoint(&Entry, Entry.begin()); + } +@@ -367,7 +368,7 @@ public: + AllocaInst *alloca = + Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? + +- return Builder.CreateBitCast(alloca, CS.getType()); ++ return Builder.CreateBitCast(alloca, CB->getType()); + } + + explicit UntypedMemoryFI(unsigned sizeArgNr) +@@ -430,26 +431,24 @@ GarbageCollect2Stack::GarbageCollect2Stack() + KnownFunctions["_d_allocmemory"] = &AllocMemory; + } + +-static void RemoveCall(CallSite CS, const Analysis &A) { ++static void RemoveCall(CallBase *CB, const Analysis &A) { + // For an invoke instruction, we insert a branch to the normal target BB + // immediately before it. Ideally, we would find a way to not invalidate + // the dominator tree here. +- if (CS.isInvoke()) { +- InvokeInst *Invoke = cast(CS.getInstruction()); +- ++ if (auto Invoke = dyn_cast(CB)) { + BranchInst::Create(Invoke->getNormalDest(), Invoke); +- Invoke->getUnwindDest()->removePredecessor(CS->getParent()); ++ Invoke->getUnwindDest()->removePredecessor(CB->getParent()); + } + + // Remove the runtime call. + if (A.CGNode) { + #if LDC_LLVM_VER >= 900 +- A.CGNode->removeCallEdgeFor(*cast(CS.getInstruction())); ++ A.CGNode->removeCallEdgeFor(*CB); + #else +- A.CGNode->removeCallEdgeFor(CS); ++ A.CGNode->removeCallEdgeFor(CB); + #endif + } +- CS->eraseFromParent(); ++ CB->eraseFromParent(); + } + + static bool +@@ -482,14 +481,13 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + auto originalI = I; + + // Ignore non-calls. +- Instruction *Inst = &(*(I++)); +- CallSite CS(Inst); +- if (!CS.getInstruction()) { ++ auto CB = dyn_cast(&(*(I++))); ++ if (!CB) { + continue; + } + + // Ignore indirect calls and calls to non-external functions. +- Function *Callee = CS.getCalledFunction(); ++ Function *Callee = CB->getCalledFunction(); + if (Callee == nullptr || !Callee->isDeclaration() || + !Callee->hasExternalLinkage()) { + continue; +@@ -503,16 +501,16 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + + FunctionInfo *info = OMI->getValue(); + +- if (Inst->use_empty()) { ++ if (CB->use_empty()) { + Changed = true; + NumDeleted++; +- RemoveCall(CS, A); ++ RemoveCall(CB, A); + continue; + } + +- LLVM_DEBUG(errs() << "GarbageCollect2Stack inspecting: " << *Inst); ++ LLVM_DEBUG(errs() << "GarbageCollect2Stack inspecting: " << *CB); + +- if (!info->analyze(CS, A)) { ++ if (!info->analyze(CB, A)) { + continue; + } + +@@ -522,7 +520,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + continue; + } + } else { +- if (!isSafeToStackAllocate(originalI, Inst, DT, RemoveTailCallInsts)) { ++ if (!isSafeToStackAllocate(originalI, CB, DT, RemoveTailCallInsts)) { + continue; + } + } +@@ -537,18 +535,18 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + } + + IRBuilder<> Builder(&BB, originalI); +- Value *newVal = info->promote(CS, Builder, A); ++ Value *newVal = info->promote(CB, Builder, A); + + LLVM_DEBUG(errs() << "Promoted to: " << *newVal); + + // Make sure the type is the same as it was before, and replace all + // uses of the runtime call with the alloca. +- if (newVal->getType() != Inst->getType()) { +- newVal = Builder.CreateBitCast(newVal, Inst->getType()); ++ if (newVal->getType() != CB->getType()) { ++ newVal = Builder.CreateBitCast(newVal, CB->getType()); + } +- Inst->replaceAllUsesWith(newVal); ++ CB->replaceAllUsesWith(newVal); + +- RemoveCall(CS, A); ++ RemoveCall(CB, A); + } + } + +@@ -818,11 +816,11 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V, + switch (I->getOpcode()) { + case Instruction::Call: + case Instruction::Invoke: { +- CallSite CS(I); ++ auto CB = llvm::cast(I); + // Not captured if the callee is readonly, doesn't return a copy through + // its return value and doesn't unwind (a readonly function can leak bits + // by throwing an exception or not depending on the input value). +- if (CS.onlyReadsMemory() && CS.doesNotThrow() && ++ if (CB->onlyReadsMemory() && CB->doesNotThrow() && + I->getType() == llvm::Type::getVoidTy(I->getContext())) { + break; + } +@@ -834,18 +832,17 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V, + // that loading a value from a pointer does not cause the pointer to be + // captured, even though the loaded value might be the pointer itself + // (think of self-referential objects). +- CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end(); +- for (CallSite::arg_iterator A = B; A != E; ++A) { ++ auto B = CB->arg_begin(), E = CB->arg_end(); ++ for (auto A = B; A != E; ++A) { + if (A->get() == V) { +- if (!CS.paramHasAttr(A - B, llvm::Attribute::AttrKind::NoCapture)) { ++ if (!CB->paramHasAttr(A - B, llvm::Attribute::AttrKind::NoCapture)) { + // The parameter is not marked 'nocapture' - captured. + return false; + } + +- if (CS.isCall()) { +- CallInst *CI = cast(I); +- if (CI->isTailCall()) { +- RemoveTailCallInsts.push_back(CI); ++ if (auto call = dyn_cast(CB)) { ++ if (call->isTailCall()) { ++ RemoveTailCallInsts.push_back(call); + } + } + } +diff --git a/gen/tocall.cpp b/gen/tocall.cpp +index fb497446..b217ffd3 100644 +--- a/gen/tocall.cpp ++++ b/gen/tocall.cpp +@@ -405,7 +405,9 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, + + llvm::StoreInst *ret = p->ir->CreateStore(val, ptr); + ret->setAtomic(llvm::AtomicOrdering(atomicOrdering)); +- ret->setAlignment(LLMaybeAlign(getTypeAllocSize(val->getType()))); ++ if (auto alignment = getTypeAllocSize(val->getType())) { ++ ret->setAlignment(LLAlign(alignment)); ++ } + return true; + } + +@@ -435,7 +437,9 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, + } + + llvm::LoadInst *load = p->ir->CreateLoad(ptr); +- load->setAlignment(LLMaybeAlign(getTypeAllocSize(load->getType()))); ++ if (auto alignment = getTypeAllocSize(load->getType())) { ++ load->setAlignment(LLAlign(alignment)); ++ } + load->setAtomic(llvm::AtomicOrdering(atomicOrdering)); + llvm::Value *val = load; + if (val->getType() != pointeeType) { +@@ -868,21 +872,21 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, + } + + // call the function +- LLCallSite call = gIR->CreateCallOrInvoke(callable, args, "", tf->isnothrow); ++ llvm::CallBase *call = gIR->funcGen().callOrInvoke(callable, callableTy, args, ++ "", tf->isnothrow); + + // PGO: Insert instrumentation or attach profile metadata at indirect call + // sites. +- if (!call.getCalledFunction()) { ++ if (!call->getCalledFunction()) { + auto &PGO = gIR->funcGen().pgo; +- PGO.emitIndirectCallPGO(call.getInstruction(), callable); ++ PGO.emitIndirectCallPGO(call, callable); + } + + // get return value + const int sretArgIndex = + (irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(tf) ? 1 + : 0); +- LLValue *retllval = +- (irFty.arg_sret ? args[sretArgIndex] : call.getInstruction()); ++ LLValue *retllval = (irFty.arg_sret ? args[sretArgIndex] : call); + bool retValIsLVal = + (tf->isref && returnTy != Tvoid) || (irFty.arg_sret != nullptr); + +@@ -1002,16 +1006,16 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, + gIR->context(), + static_cast(llfunc->getIntrinsicID())); + } else { +- call.setCallingConv(callconv); ++ call->setCallingConv(callconv); + } + } else { +- call.setCallingConv(callconv); ++ call->setCallingConv(callconv); + } + // merge in function attributes set in callOrInvoke + attrlist = attrlist.addAttributes( + gIR->context(), LLAttributeList::FunctionIndex, +- llvm::AttrBuilder(call.getAttributes(), LLAttributeList::FunctionIndex)); +- call.setAttributes(attrlist); ++ llvm::AttrBuilder(call->getAttributes(), LLAttributeList::FunctionIndex)); ++ call->setAttributes(attrlist); + + // Special case for struct constructor calls: For temporaries, using the + // this pointer value returned from the constructor instead of the alloca +diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp +index 9a9ee994..282a6276 100644 +--- a/gen/toconstelem.cpp ++++ b/gen/toconstelem.cpp +@@ -664,8 +664,13 @@ public: + // cast. + // FIXME: Check DMD source to understand why two different ASTs are + // constructed. ++#if LDC_LLVM_VER >= 1100 ++ const auto elementCount = llvm::ElementCount(elemCount, false); ++#else ++ const auto elementCount = elemCount; ++#endif + result = llvm::ConstantVector::getSplat( +- elemCount, toConstElem(e->e1->optimize(WANTvalue))); ++ elementCount, toConstElem(e->e1->optimize(WANTvalue))); + } + } + +diff --git a/gen/toir.cpp b/gen/toir.cpp +index bbbd3783..63aa1154 100644 +--- a/gen/toir.cpp ++++ b/gen/toir.cpp +@@ -2428,8 +2428,7 @@ public: + LLValue *valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2)); + + LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, keysArray, +- valuesArray, "aa") +- .getInstruction(); ++ valuesArray, "aa"); + if (basetype->ty != Taarray) { + LLValue *tmp = DtoAlloca(e->type, "aaliteral"); + DtoStore(aa, DtoGEP(tmp, 0u, 0)); +@@ -2623,7 +2622,13 @@ public: + DValue *val = toElem(e->e1); + LLValue *llElement = getCastElement(val); + if (auto llConstant = isaConstant(llElement)) { +- auto vectorConstant = llvm::ConstantVector::getSplat(N, llConstant); ++#if LDC_LLVM_VER >= 1100 ++ const auto elementCount = llvm::ElementCount(N, false); ++#else ++ const auto elementCount = N; ++#endif ++ auto vectorConstant = ++ llvm::ConstantVector::getSplat(elementCount, llConstant); + DtoStore(vectorConstant, dstMem); + } else { + for (unsigned int i = 0; i < N; ++i) { +diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp +index 0d14027f..73f1bce8 100644 +--- a/gen/tollvm.cpp ++++ b/gen/tollvm.cpp +@@ -452,7 +452,9 @@ LLValue *DtoLoad(LLValue *src, const char *name) { + // the type. + LLValue *DtoAlignedLoad(LLValue *src, const char *name) { + llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name); +- ld->setAlignment(LLMaybeAlign(getABITypeAlign(ld->getType()))); ++ if (auto alignment = getABITypeAlign(ld->getType())) { ++ ld->setAlignment(LLAlign(alignment)); ++ } + return ld; + } + +@@ -489,7 +491,9 @@ void DtoAlignedStore(LLValue *src, LLValue *dst) { + assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) && + "Should store bools as i8 instead of i1."); + llvm::StoreInst *st = gIR->ir->CreateStore(src, dst); +- st->setAlignment(LLMaybeAlign(getABITypeAlign(src->getType()))); ++ if (auto alignment = getABITypeAlign(src->getType())) { ++ st->setAlignment(LLAlign(alignment)); ++ } + } + + //////////////////////////////////////////////////////////////////////////////// +diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp +index 9a42de2e..b18da5b7 100644 +--- a/gen/trycatchfinally.cpp ++++ b/gen/trycatchfinally.cpp +@@ -365,9 +365,9 @@ llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock, + // We need a branch selector if we are here... + if (!branchSelector) { + // ... and have not created one yet, so do so now. ++ llvm::Type *branchSelectorType = llvm::Type::getInt32Ty(irs.context()); + branchSelector = new llvm::AllocaInst( +- llvm::Type::getInt32Ty(irs.context()), +- irs.module.getDataLayout().getAllocaAddrSpace(), ++ branchSelectorType, irs.module.getDataLayout().getAllocaAddrSpace(), + llvm::Twine("branchsel.") + beginBlock()->getName(), + irs.topallocapoint()); + +@@ -380,7 +380,11 @@ llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock, + // 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::Value *sel = new llvm::LoadInst( ++#if LDC_LLVM_VER >= 1100 ++ branchSelectorType, ++#endif ++ branchSelector, "", endBlock()); + llvm::SwitchInst::Create( + sel, exitTargets[0].branchTarget, + 1, // Expected number of branches, only for pre-allocating. +diff --git a/gen/uda.cpp b/gen/uda.cpp +index 80fa194e..a8395463 100644 +--- a/gen/uda.cpp ++++ b/gen/uda.cpp +@@ -16,6 +16,7 @@ + #include "llvm/ADT/StringExtras.h" + #include "llvm/ADT/StringSwitch.h" + ++#if LDC_LLVM_VER < 1100 + namespace llvm { + // Auto-generate: + // Attribute::AttrKind getAttrKindFromName(StringRef AttrName) { ... } +@@ -26,6 +27,7 @@ namespace llvm { + #include "llvm/IR/Attributes.inc" + #endif + } ++#endif + + namespace { + +@@ -205,7 +207,11 @@ void applyAttrLLVMAttr(StructLiteralExp *sle, llvm::AttrBuilder &attrs) { + llvm::StringRef key = getStringElem(sle, 0); + llvm::StringRef value = getStringElem(sle, 1); + if (value.empty()) { ++#if LDC_LLVM_VER >= 1100 ++ const auto kind = llvm::Attribute::getAttrKindFromName(key); ++#else + const auto kind = llvm::getAttrKindFromName(key); ++#endif + if (kind != llvm::Attribute::None) { + attrs.addAttribute(kind); + } else { +diff --git a/ir/irtype.cpp b/ir/irtype.cpp +index f2aaf72f..46c94fb2 100644 +--- a/ir/irtype.cpp ++++ b/ir/irtype.cpp +@@ -220,5 +220,10 @@ llvm::Type *IrTypeVector::vector2llvm(Type *dt) { + TypeSArray *tsa = static_cast(tv->basetype); + uint64_t dim = static_cast(tsa->dim->toUInteger()); + LLType *elemType = DtoMemType(tsa->next); +- return llvm::VectorType::get(elemType, dim); ++ return llvm::VectorType::get(elemType, dim ++#if LDC_LLVM_VER >= 1100 ++ , ++ /*Scalable=*/false ++#endif ++ ); + } +diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in +index 9f051e1a..cb51035a 100644 +--- a/tests/lit.site.cfg.in ++++ b/tests/lit.site.cfg.in +@@ -84,7 +84,7 @@ config.available_features.add("llvm%d" % config.llvm_version) + plusoneable_llvmversion = config.llvm_version // 10 + config.llvm_version%10 + for version in range(60, plusoneable_llvmversion+1): + config.available_features.add("atleast_llvm%d0%d" % (version//10, version%10)) +-for version in range(plusoneable_llvmversion, 101): ++for version in range(plusoneable_llvmversion, 111): + config.available_features.add("atmost_llvm%d0%d" % (version//10, version%10)) + + # Define OS as available feature (Windows, Darwin, Linux) +diff --git a/tools/ldc-profdata/llvm-profdata-11.0.cpp b/tools/ldc-profdata/llvm-profdata-11.0.cpp +new file mode 100644 +index 00000000..843f072a +--- /dev/null ++++ b/tools/ldc-profdata/llvm-profdata-11.0.cpp +@@ -0,0 +1,1344 @@ ++//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++// ++// llvm-profdata merges .profdata files. ++// ++//===----------------------------------------------------------------------===// ++ ++#include "llvm/ADT/SmallSet.h" ++#include "llvm/ADT/SmallVector.h" ++#include "llvm/ADT/StringRef.h" ++#include "llvm/IR/LLVMContext.h" ++#include "llvm/ProfileData/InstrProfReader.h" ++#include "llvm/ProfileData/InstrProfWriter.h" ++#include "llvm/ProfileData/ProfileCommon.h" ++#include "llvm/ProfileData/SampleProfReader.h" ++#include "llvm/ProfileData/SampleProfWriter.h" ++#include "llvm/Support/CommandLine.h" ++#include "llvm/Support/Errc.h" ++#include "llvm/Support/FileSystem.h" ++#include "llvm/Support/Format.h" ++#include "llvm/Support/FormattedStream.h" ++#include "llvm/Support/InitLLVM.h" ++#include "llvm/Support/MemoryBuffer.h" ++#include "llvm/Support/Path.h" ++#include "llvm/Support/ThreadPool.h" ++#include "llvm/Support/Threading.h" ++#include "llvm/Support/WithColor.h" ++#include "llvm/Support/raw_ostream.h" ++#include ++ ++using namespace llvm; ++ ++enum ProfileFormat { ++ PF_None = 0, ++ PF_Text, ++ PF_Compact_Binary, ++ PF_Ext_Binary, ++ PF_GCC, ++ PF_Binary ++}; ++ ++static void warn(Twine Message, std::string Whence = "", ++ std::string Hint = "") { ++ WithColor::warning(); ++ if (!Whence.empty()) ++ errs() << Whence << ": "; ++ errs() << Message << "\n"; ++ if (!Hint.empty()) ++ WithColor::note() << Hint << "\n"; ++} ++ ++static void exitWithError(Twine Message, std::string Whence = "", ++ std::string Hint = "") { ++ WithColor::error(); ++ if (!Whence.empty()) ++ errs() << Whence << ": "; ++ errs() << Message << "\n"; ++ if (!Hint.empty()) ++ WithColor::note() << Hint << "\n"; ++ ::exit(1); ++} ++ ++static void exitWithError(Error E, StringRef Whence = "") { ++ if (E.isA()) { ++ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { ++ instrprof_error instrError = IPE.get(); ++ StringRef Hint = ""; ++ if (instrError == instrprof_error::unrecognized_format) { ++ // Hint for common error of forgetting --sample for sample profiles. ++ Hint = "Perhaps you forgot to use the --sample option?"; ++ } ++ exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); ++ }); ++ } ++ ++ exitWithError(toString(std::move(E)), std::string(Whence)); ++} ++ ++static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { ++ exitWithError(EC.message(), std::string(Whence)); ++} ++ ++namespace { ++enum ProfileKinds { instr, sample }; ++enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; ++} ++ ++static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, ++ StringRef Whence = "") { ++ if (FailMode == failIfAnyAreInvalid) ++ exitWithErrorCode(EC, Whence); ++ else ++ warn(EC.message(), std::string(Whence)); ++} ++ ++static void handleMergeWriterError(Error E, StringRef WhenceFile = "", ++ StringRef WhenceFunction = "", ++ bool ShowHint = true) { ++ if (!WhenceFile.empty()) ++ errs() << WhenceFile << ": "; ++ if (!WhenceFunction.empty()) ++ errs() << WhenceFunction << ": "; ++ ++ auto IPE = instrprof_error::success; ++ E = handleErrors(std::move(E), ++ [&IPE](std::unique_ptr E) -> Error { ++ IPE = E->get(); ++ return Error(std::move(E)); ++ }); ++ errs() << toString(std::move(E)) << "\n"; ++ ++ if (ShowHint) { ++ StringRef Hint = ""; ++ if (IPE != instrprof_error::success) { ++ switch (IPE) { ++ case instrprof_error::hash_mismatch: ++ case instrprof_error::count_mismatch: ++ case instrprof_error::value_site_count_mismatch: ++ Hint = "Make sure that all profile data to be merged is generated " ++ "from the same binary."; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ if (!Hint.empty()) ++ errs() << Hint << "\n"; ++ } ++} ++ ++namespace { ++/// A remapper from original symbol names to new symbol names based on a file ++/// containing a list of mappings from old name to new name. ++class SymbolRemapper { ++ std::unique_ptr File; ++ DenseMap RemappingTable; ++ ++public: ++ /// Build a SymbolRemapper from a file containing a list of old/new symbols. ++ static std::unique_ptr create(StringRef InputFile) { ++ auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); ++ if (!BufOrError) ++ exitWithErrorCode(BufOrError.getError(), InputFile); ++ ++ auto Remapper = std::make_unique(); ++ Remapper->File = std::move(BufOrError.get()); ++ ++ for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); ++ !LineIt.is_at_eof(); ++LineIt) { ++ std::pair Parts = LineIt->split(' '); ++ if (Parts.first.empty() || Parts.second.empty() || ++ Parts.second.count(' ')) { ++ exitWithError("unexpected line in remapping file", ++ (InputFile + ":" + Twine(LineIt.line_number())).str(), ++ "expected 'old_symbol new_symbol'"); ++ } ++ Remapper->RemappingTable.insert(Parts); ++ } ++ return Remapper; ++ } ++ ++ /// Attempt to map the given old symbol into a new symbol. ++ /// ++ /// \return The new symbol, or \p Name if no such symbol was found. ++ StringRef operator()(StringRef Name) { ++ StringRef New = RemappingTable.lookup(Name); ++ return New.empty() ? Name : New; ++ } ++}; ++} ++ ++struct WeightedFile { ++ std::string Filename; ++ uint64_t Weight; ++}; ++typedef SmallVector WeightedFileVector; ++ ++/// Keep track of merged data and reported errors. ++struct WriterContext { ++ std::mutex Lock; ++ InstrProfWriter Writer; ++ std::vector> Errors; ++ std::mutex &ErrLock; ++ SmallSet &WriterErrorCodes; ++ ++ WriterContext(bool IsSparse, std::mutex &ErrLock, ++ SmallSet &WriterErrorCodes) ++ : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock), ++ WriterErrorCodes(WriterErrorCodes) {} ++}; ++ ++/// Computer the overlap b/w profile BaseFilename and TestFileName, ++/// and store the program level result to Overlap. ++static void overlapInput(const std::string &BaseFilename, ++ const std::string &TestFilename, WriterContext *WC, ++ OverlapStats &Overlap, ++ const OverlapFuncFilters &FuncFilter, ++ raw_fd_ostream &OS, bool IsCS) { ++ auto ReaderOrErr = InstrProfReader::create(TestFilename); ++ if (Error E = ReaderOrErr.takeError()) { ++ // Skip the empty profiles by returning sliently. ++ instrprof_error IPE = InstrProfError::take(std::move(E)); ++ if (IPE != instrprof_error::empty_raw_profile) ++ WC->Errors.emplace_back(make_error(IPE), TestFilename); ++ return; ++ } ++ ++ auto Reader = std::move(ReaderOrErr.get()); ++ for (auto &I : *Reader) { ++ OverlapStats FuncOverlap(OverlapStats::FunctionLevel); ++ FuncOverlap.setFuncInfo(I.Name, I.Hash); ++ ++ WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); ++ FuncOverlap.dump(OS); ++ } ++} ++ ++/// Load an input into a writer context. ++static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, ++ WriterContext *WC) { ++ std::unique_lock CtxGuard{WC->Lock}; ++ ++ // Copy the filename, because llvm::ThreadPool copied the input "const ++ // WeightedFile &" by value, making a reference to the filename within it ++ // invalid outside of this packaged task. ++ std::string Filename = Input.Filename; ++ ++ auto ReaderOrErr = InstrProfReader::create(Input.Filename); ++ if (Error E = ReaderOrErr.takeError()) { ++ // Skip the empty profiles by returning sliently. ++ instrprof_error IPE = InstrProfError::take(std::move(E)); ++ if (IPE != instrprof_error::empty_raw_profile) ++ WC->Errors.emplace_back(make_error(IPE), Filename); ++ return; ++ } ++ ++ auto Reader = std::move(ReaderOrErr.get()); ++ bool IsIRProfile = Reader->isIRLevelProfile(); ++ bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); ++ if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { ++ WC->Errors.emplace_back( ++ make_error( ++ "Merge IR generated profile with Clang generated profile.", ++ std::error_code()), ++ Filename); ++ return; ++ } ++ ++ for (auto &I : *Reader) { ++ if (Remapper) ++ I.Name = (*Remapper)(I.Name); ++ const StringRef FuncName = I.Name; ++ bool Reported = false; ++ WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { ++ if (Reported) { ++ consumeError(std::move(E)); ++ return; ++ } ++ Reported = true; ++ // Only show hint the first time an error occurs. ++ instrprof_error IPE = InstrProfError::take(std::move(E)); ++ std::unique_lock ErrGuard{WC->ErrLock}; ++ bool firstTime = WC->WriterErrorCodes.insert(IPE).second; ++ handleMergeWriterError(make_error(IPE), Input.Filename, ++ FuncName, firstTime); ++ }); ++ } ++ if (Reader->hasError()) ++ if (Error E = Reader->getError()) ++ WC->Errors.emplace_back(std::move(E), Filename); ++} ++ ++/// Merge the \p Src writer context into \p Dst. ++static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { ++ for (auto &ErrorPair : Src->Errors) ++ Dst->Errors.push_back(std::move(ErrorPair)); ++ Src->Errors.clear(); ++ ++ Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { ++ instrprof_error IPE = InstrProfError::take(std::move(E)); ++ std::unique_lock ErrGuard{Dst->ErrLock}; ++ bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; ++ if (firstTime) ++ warn(toString(make_error(IPE))); ++ }); ++} ++ ++static void writeInstrProfile(StringRef OutputFilename, ++ ProfileFormat OutputFormat, ++ InstrProfWriter &Writer) { ++ std::error_code EC; ++ raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); ++ if (EC) ++ exitWithErrorCode(EC, OutputFilename); ++ ++ if (OutputFormat == PF_Text) { ++ if (Error E = Writer.writeText(Output)) ++ exitWithError(std::move(E)); ++ } else { ++ Writer.write(Output); ++ } ++} ++ ++static void mergeInstrProfile(const WeightedFileVector &Inputs, ++ SymbolRemapper *Remapper, ++ StringRef OutputFilename, ++ ProfileFormat OutputFormat, bool OutputSparse, ++ unsigned NumThreads, FailureMode FailMode) { ++ if (OutputFilename.compare("-") == 0) ++ exitWithError("Cannot write indexed profdata format to stdout."); ++ ++ if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && ++ OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) ++ exitWithError("Unknown format is specified."); ++ ++ std::mutex ErrorLock; ++ SmallSet WriterErrorCodes; ++ ++ // If NumThreads is not specified, auto-detect a good default. ++ if (NumThreads == 0) ++ NumThreads = std::min(hardware_concurrency().compute_thread_count(), ++ unsigned((Inputs.size() + 1) / 2)); ++ // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails ++ // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't ++ // merged, thus the emitted file ends up with a PF_Unknown kind. ++ ++ // Initialize the writer contexts. ++ SmallVector, 4> Contexts; ++ for (unsigned I = 0; I < NumThreads; ++I) ++ Contexts.emplace_back(std::make_unique( ++ OutputSparse, ErrorLock, WriterErrorCodes)); ++ ++ if (NumThreads == 1) { ++ for (const auto &Input : Inputs) ++ loadInput(Input, Remapper, Contexts[0].get()); ++ } else { ++ ThreadPool Pool(hardware_concurrency(NumThreads)); ++ ++ // Load the inputs in parallel (N/NumThreads serial steps). ++ unsigned Ctx = 0; ++ for (const auto &Input : Inputs) { ++ Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); ++ Ctx = (Ctx + 1) % NumThreads; ++ } ++ Pool.wait(); ++ ++ // Merge the writer contexts together (~ lg(NumThreads) serial steps). ++ unsigned Mid = Contexts.size() / 2; ++ unsigned End = Contexts.size(); ++ assert(Mid > 0 && "Expected more than one context"); ++ do { ++ for (unsigned I = 0; I < Mid; ++I) ++ Pool.async(mergeWriterContexts, Contexts[I].get(), ++ Contexts[I + Mid].get()); ++ Pool.wait(); ++ if (End & 1) { ++ Pool.async(mergeWriterContexts, Contexts[0].get(), ++ Contexts[End - 1].get()); ++ Pool.wait(); ++ } ++ End = Mid; ++ Mid /= 2; ++ } while (Mid > 0); ++ } ++ ++ // Handle deferred errors encountered during merging. If the number of errors ++ // is equal to the number of inputs the merge failed. ++ unsigned NumErrors = 0; ++ for (std::unique_ptr &WC : Contexts) { ++ for (auto &ErrorPair : WC->Errors) { ++ ++NumErrors; ++ warn(toString(std::move(ErrorPair.first)), ErrorPair.second); ++ } ++ } ++ if (NumErrors == Inputs.size() || ++ (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) ++ exitWithError("No profiles could be merged."); ++ ++ writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); ++} ++ ++/// Make a copy of the given function samples with all symbol names remapped ++/// by the provided symbol remapper. ++static sampleprof::FunctionSamples ++remapSamples(const sampleprof::FunctionSamples &Samples, ++ SymbolRemapper &Remapper, sampleprof_error &Error) { ++ sampleprof::FunctionSamples Result; ++ Result.setName(Remapper(Samples.getName())); ++ Result.addTotalSamples(Samples.getTotalSamples()); ++ Result.addHeadSamples(Samples.getHeadSamples()); ++ for (const auto &BodySample : Samples.getBodySamples()) { ++ Result.addBodySamples(BodySample.first.LineOffset, ++ BodySample.first.Discriminator, ++ BodySample.second.getSamples()); ++ for (const auto &Target : BodySample.second.getCallTargets()) { ++ Result.addCalledTargetSamples(BodySample.first.LineOffset, ++ BodySample.first.Discriminator, ++ Remapper(Target.first()), Target.second); ++ } ++ } ++ for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { ++ sampleprof::FunctionSamplesMap &Target = ++ Result.functionSamplesAt(CallsiteSamples.first); ++ for (const auto &Callsite : CallsiteSamples.second) { ++ sampleprof::FunctionSamples Remapped = ++ remapSamples(Callsite.second, Remapper, Error); ++ MergeResult(Error, ++ Target[std::string(Remapped.getName())].merge(Remapped)); ++ } ++ } ++ return Result; ++} ++ ++static sampleprof::SampleProfileFormat FormatMap[] = { ++ sampleprof::SPF_None, ++ sampleprof::SPF_Text, ++ sampleprof::SPF_Compact_Binary, ++ sampleprof::SPF_Ext_Binary, ++ sampleprof::SPF_GCC, ++ sampleprof::SPF_Binary}; ++ ++static std::unique_ptr ++getInputFileBuf(const StringRef &InputFile) { ++ if (InputFile == "") ++ return {}; ++ ++ auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); ++ if (!BufOrError) ++ exitWithErrorCode(BufOrError.getError(), InputFile); ++ ++ return std::move(*BufOrError); ++} ++ ++static void populateProfileSymbolList(MemoryBuffer *Buffer, ++ sampleprof::ProfileSymbolList &PSL) { ++ if (!Buffer) ++ return; ++ ++ SmallVector SymbolVec; ++ StringRef Data = Buffer->getBuffer(); ++ Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); ++ ++ for (StringRef symbol : SymbolVec) ++ PSL.add(symbol); ++} ++ ++static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, ++ ProfileFormat OutputFormat, ++ MemoryBuffer *Buffer, ++ sampleprof::ProfileSymbolList &WriterList, ++ bool CompressAllSections, bool UseMD5, ++ bool GenPartialProfile) { ++ populateProfileSymbolList(Buffer, WriterList); ++ if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) ++ warn("Profile Symbol list is not empty but the output format is not " ++ "ExtBinary format. The list will be lost in the output. "); ++ ++ Writer.setProfileSymbolList(&WriterList); ++ ++ if (CompressAllSections) { ++ if (OutputFormat != PF_Ext_Binary) ++ warn("-compress-all-section is ignored. Specify -extbinary to enable it"); ++ else ++ Writer.setToCompressAllSections(); ++ } ++ if (UseMD5) { ++ if (OutputFormat != PF_Ext_Binary) ++ warn("-use-md5 is ignored. Specify -extbinary to enable it"); ++ else ++ Writer.setUseMD5(); ++ } ++ if (GenPartialProfile) { ++ if (OutputFormat != PF_Ext_Binary) ++ warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); ++ else ++ Writer.setPartialProfile(); ++ } ++} ++ ++static void ++mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, ++ StringRef OutputFilename, ProfileFormat OutputFormat, ++ StringRef ProfileSymbolListFile, bool CompressAllSections, ++ bool UseMD5, bool GenPartialProfile, FailureMode FailMode) { ++ using namespace sampleprof; ++ StringMap ProfileMap; ++ SmallVector, 5> Readers; ++ LLVMContext Context; ++ sampleprof::ProfileSymbolList WriterList; ++ for (const auto &Input : Inputs) { ++ auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); ++ if (std::error_code EC = ReaderOrErr.getError()) { ++ warnOrExitGivenError(FailMode, EC, Input.Filename); ++ continue; ++ } ++ ++ // We need to keep the readers around until after all the files are ++ // read so that we do not lose the function names stored in each ++ // reader's memory. The function names are needed to write out the ++ // merged profile map. ++ Readers.push_back(std::move(ReaderOrErr.get())); ++ const auto Reader = Readers.back().get(); ++ if (std::error_code EC = Reader->read()) { ++ warnOrExitGivenError(FailMode, EC, Input.Filename); ++ Readers.pop_back(); ++ continue; ++ } ++ ++ StringMap &Profiles = Reader->getProfiles(); ++ for (StringMap::iterator I = Profiles.begin(), ++ E = Profiles.end(); ++ I != E; ++I) { ++ sampleprof_error Result = sampleprof_error::success; ++ FunctionSamples Remapped = ++ Remapper ? remapSamples(I->second, *Remapper, Result) ++ : FunctionSamples(); ++ FunctionSamples &Samples = Remapper ? Remapped : I->second; ++ StringRef FName = Samples.getName(); ++ MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); ++ if (Result != sampleprof_error::success) { ++ std::error_code EC = make_error_code(Result); ++ handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); ++ } ++ } ++ ++ std::unique_ptr ReaderList = ++ Reader->getProfileSymbolList(); ++ if (ReaderList) ++ WriterList.merge(*ReaderList); ++ } ++ auto WriterOrErr = ++ SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); ++ if (std::error_code EC = WriterOrErr.getError()) ++ exitWithErrorCode(EC, OutputFilename); ++ ++ auto Writer = std::move(WriterOrErr.get()); ++ // WriterList will have StringRef refering to string in Buffer. ++ // Make sure Buffer lives as long as WriterList. ++ auto Buffer = getInputFileBuf(ProfileSymbolListFile); ++ handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, ++ CompressAllSections, UseMD5, GenPartialProfile); ++ Writer->write(ProfileMap); ++} ++ ++static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { ++ StringRef WeightStr, FileName; ++ std::tie(WeightStr, FileName) = WeightedFilename.split(','); ++ ++ uint64_t Weight; ++ if (WeightStr.getAsInteger(10, Weight) || Weight < 1) ++ exitWithError("Input weight must be a positive integer."); ++ ++ return {std::string(FileName), Weight}; ++} ++ ++static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { ++ StringRef Filename = WF.Filename; ++ uint64_t Weight = WF.Weight; ++ ++ // If it's STDIN just pass it on. ++ if (Filename == "-") { ++ WNI.push_back({std::string(Filename), Weight}); ++ return; ++ } ++ ++ llvm::sys::fs::file_status Status; ++ llvm::sys::fs::status(Filename, Status); ++ if (!llvm::sys::fs::exists(Status)) ++ exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), ++ Filename); ++ // If it's a source file, collect it. ++ if (llvm::sys::fs::is_regular_file(Status)) { ++ WNI.push_back({std::string(Filename), Weight}); ++ return; ++ } ++ ++ if (llvm::sys::fs::is_directory(Status)) { ++ std::error_code EC; ++ for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; ++ F != E && !EC; F.increment(EC)) { ++ if (llvm::sys::fs::is_regular_file(F->path())) { ++ addWeightedInput(WNI, {F->path(), Weight}); ++ } ++ } ++ if (EC) ++ exitWithErrorCode(EC, Filename); ++ } ++} ++ ++static void parseInputFilenamesFile(MemoryBuffer *Buffer, ++ WeightedFileVector &WFV) { ++ if (!Buffer) ++ return; ++ ++ SmallVector Entries; ++ StringRef Data = Buffer->getBuffer(); ++ Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); ++ for (const StringRef &FileWeightEntry : Entries) { ++ StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); ++ // Skip comments. ++ if (SanitizedEntry.startswith("#")) ++ continue; ++ // If there's no comma, it's an unweighted profile. ++ else if (SanitizedEntry.find(',') == StringRef::npos) ++ addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); ++ else ++ addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); ++ } ++} ++ ++static int merge_main(int argc, const char *argv[]) { ++ cl::list InputFilenames(cl::Positional, ++ cl::desc("")); ++ cl::list WeightedInputFilenames("weighted-input", ++ cl::desc(",")); ++ cl::opt InputFilenamesFile( ++ "input-files", cl::init(""), ++ cl::desc("Path to file containing newline-separated " ++ "[,] entries")); ++ cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), ++ cl::aliasopt(InputFilenamesFile)); ++ cl::opt DumpInputFileList( ++ "dump-input-file-list", cl::init(false), cl::Hidden, ++ cl::desc("Dump the list of input files and their weights, then exit")); ++ cl::opt RemappingFile("remapping-file", cl::value_desc("file"), ++ cl::desc("Symbol remapping file")); ++ cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), ++ cl::aliasopt(RemappingFile)); ++ cl::opt OutputFilename("output", cl::value_desc("output"), ++ cl::init("-"), cl::Required, ++ cl::desc("Output file")); ++ cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), ++ cl::aliasopt(OutputFilename)); ++ cl::opt ProfileKind( ++ cl::desc("Profile kind:"), cl::init(instr), ++ cl::values(clEnumVal(instr, "Instrumentation profile (default)"), ++ clEnumVal(sample, "Sample profile"))); ++ cl::opt OutputFormat( ++ cl::desc("Format of output profile"), cl::init(PF_Binary), ++ cl::values( ++ clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), ++ clEnumValN(PF_Compact_Binary, "compbinary", ++ "Compact binary encoding"), ++ clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), ++ clEnumValN(PF_Text, "text", "Text encoding"), ++ clEnumValN(PF_GCC, "gcc", ++ "GCC encoding (only meaningful for -sample)"))); ++ cl::opt FailureMode( ++ "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), ++ cl::values(clEnumValN(failIfAnyAreInvalid, "any", ++ "Fail if any profile is invalid."), ++ clEnumValN(failIfAllAreInvalid, "all", ++ "Fail only if all profiles are invalid."))); ++ cl::opt OutputSparse("sparse", cl::init(false), ++ cl::desc("Generate a sparse profile (only meaningful for -instr)")); ++ cl::opt NumThreads( ++ "num-threads", cl::init(0), ++ cl::desc("Number of merge threads to use (default: autodetect)")); ++ cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), ++ cl::aliasopt(NumThreads)); ++ cl::opt ProfileSymbolListFile( ++ "prof-sym-list", cl::init(""), ++ cl::desc("Path to file containing the list of function symbols " ++ "used to populate profile symbol list")); ++ cl::opt CompressAllSections( ++ "compress-all-sections", cl::init(false), cl::Hidden, ++ cl::desc("Compress all sections when writing the profile (only " ++ "meaningful for -extbinary)")); ++ cl::opt UseMD5( ++ "use-md5", cl::init(false), cl::Hidden, ++ cl::desc("Choose to use MD5 to represent string in name table (only " ++ "meaningful for -extbinary)")); ++ cl::opt GenPartialProfile( ++ "gen-partial-profile", cl::init(false), cl::Hidden, ++ cl::desc("Generate a partial profile (only meaningful for -extbinary)")); ++ ++ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); ++ ++ WeightedFileVector WeightedInputs; ++ for (StringRef Filename : InputFilenames) ++ addWeightedInput(WeightedInputs, {std::string(Filename), 1}); ++ for (StringRef WeightedFilename : WeightedInputFilenames) ++ addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); ++ ++ // Make sure that the file buffer stays alive for the duration of the ++ // weighted input vector's lifetime. ++ auto Buffer = getInputFileBuf(InputFilenamesFile); ++ parseInputFilenamesFile(Buffer.get(), WeightedInputs); ++ ++ if (WeightedInputs.empty()) ++ exitWithError("No input files specified. See " + ++ sys::path::filename(argv[0]) + " -help"); ++ ++ if (DumpInputFileList) { ++ for (auto &WF : WeightedInputs) ++ outs() << WF.Weight << "," << WF.Filename << "\n"; ++ return 0; ++ } ++ ++ std::unique_ptr Remapper; ++ if (!RemappingFile.empty()) ++ Remapper = SymbolRemapper::create(RemappingFile); ++ ++ if (ProfileKind == instr) ++ mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, ++ OutputFormat, OutputSparse, NumThreads, FailureMode); ++ else ++ mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, ++ OutputFormat, ProfileSymbolListFile, CompressAllSections, ++ UseMD5, GenPartialProfile, FailureMode); ++ ++ return 0; ++} ++ ++/// Computer the overlap b/w profile BaseFilename and profile TestFilename. ++static void overlapInstrProfile(const std::string &BaseFilename, ++ const std::string &TestFilename, ++ const OverlapFuncFilters &FuncFilter, ++ raw_fd_ostream &OS, bool IsCS) { ++ std::mutex ErrorLock; ++ SmallSet WriterErrorCodes; ++ WriterContext Context(false, ErrorLock, WriterErrorCodes); ++ WeightedFile WeightedInput{BaseFilename, 1}; ++ OverlapStats Overlap; ++ Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); ++ if (E) ++ exitWithError(std::move(E), "Error in getting profile count sums"); ++ if (Overlap.Base.CountSum < 1.0f) { ++ OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; ++ exit(0); ++ } ++ if (Overlap.Test.CountSum < 1.0f) { ++ OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; ++ exit(0); ++ } ++ loadInput(WeightedInput, nullptr, &Context); ++ overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, ++ IsCS); ++ Overlap.dump(OS); ++} ++ ++static int overlap_main(int argc, const char *argv[]) { ++ cl::opt BaseFilename(cl::Positional, cl::Required, ++ cl::desc("")); ++ cl::opt TestFilename(cl::Positional, cl::Required, ++ cl::desc("")); ++ cl::opt Output("output", cl::value_desc("output"), cl::init("-"), ++ cl::desc("Output file")); ++ cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); ++ cl::opt IsCS("cs", cl::init(false), ++ cl::desc("For context sensitive counts")); ++ cl::opt ValueCutoff( ++ "value-cutoff", cl::init(-1), ++ cl::desc( ++ "Function level overlap information for every function in test " ++ "profile with max count value greater then the parameter value")); ++ cl::opt FuncNameFilter( ++ "function", ++ cl::desc("Function level overlap information for matching functions")); ++ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); ++ ++ std::error_code EC; ++ raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); ++ if (EC) ++ exitWithErrorCode(EC, Output); ++ ++ overlapInstrProfile(BaseFilename, TestFilename, ++ OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, ++ IsCS); ++ ++ return 0; ++} ++ ++typedef struct ValueSitesStats { ++ ValueSitesStats() ++ : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), ++ TotalNumValues(0) {} ++ uint64_t TotalNumValueSites; ++ uint64_t TotalNumValueSitesWithValueProfile; ++ uint64_t TotalNumValues; ++ std::vector ValueSitesHistogram; ++} ValueSitesStats; ++ ++static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, ++ ValueSitesStats &Stats, raw_fd_ostream &OS, ++ InstrProfSymtab *Symtab) { ++ uint32_t NS = Func.getNumValueSites(VK); ++ Stats.TotalNumValueSites += NS; ++ for (size_t I = 0; I < NS; ++I) { ++ uint32_t NV = Func.getNumValueDataForSite(VK, I); ++ std::unique_ptr VD = Func.getValueForSite(VK, I); ++ Stats.TotalNumValues += NV; ++ if (NV) { ++ Stats.TotalNumValueSitesWithValueProfile++; ++ if (NV > Stats.ValueSitesHistogram.size()) ++ Stats.ValueSitesHistogram.resize(NV, 0); ++ Stats.ValueSitesHistogram[NV - 1]++; ++ } ++ ++ uint64_t SiteSum = 0; ++ for (uint32_t V = 0; V < NV; V++) ++ SiteSum += VD[V].Count; ++ if (SiteSum == 0) ++ SiteSum = 1; ++ ++ for (uint32_t V = 0; V < NV; V++) { ++ OS << "\t[ " << format("%2u", I) << ", "; ++ if (Symtab == nullptr) ++ OS << format("%4" PRIu64, VD[V].Value); ++ else ++ OS << Symtab->getFuncName(VD[V].Value); ++ OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" ++ << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; ++ } ++ } ++} ++ ++static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, ++ ValueSitesStats &Stats) { ++ OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; ++ OS << " Total number of sites with values: " ++ << Stats.TotalNumValueSitesWithValueProfile << "\n"; ++ OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; ++ ++ OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; ++ for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { ++ if (Stats.ValueSitesHistogram[I] > 0) ++ OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; ++ } ++} ++ ++static int showInstrProfile(const std::string &Filename, bool ShowCounts, ++ uint32_t TopN, bool ShowIndirectCallTargets, ++ bool ShowMemOPSizes, bool ShowDetailedSummary, ++ std::vector DetailedSummaryCutoffs, ++ bool ShowAllFunctions, bool ShowCS, ++ uint64_t ValueCutoff, bool OnlyListBelow, ++ const std::string &ShowFunction, bool TextFormat, ++ raw_fd_ostream &OS) { ++ auto ReaderOrErr = InstrProfReader::create(Filename); ++ std::vector Cutoffs = std::move(DetailedSummaryCutoffs); ++ if (ShowDetailedSummary && Cutoffs.empty()) { ++ Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; ++ } ++ InstrProfSummaryBuilder Builder(std::move(Cutoffs)); ++ if (Error E = ReaderOrErr.takeError()) ++ exitWithError(std::move(E), Filename); ++ ++ auto Reader = std::move(ReaderOrErr.get()); ++ bool IsIRInstr = Reader->isIRLevelProfile(); ++ size_t ShownFunctions = 0; ++ size_t BelowCutoffFunctions = 0; ++ int NumVPKind = IPVK_Last - IPVK_First + 1; ++ std::vector VPStats(NumVPKind); ++ ++ auto MinCmp = [](const std::pair &v1, ++ const std::pair &v2) { ++ return v1.second > v2.second; ++ }; ++ ++ std::priority_queue, ++ std::vector>, ++ decltype(MinCmp)> ++ HottestFuncs(MinCmp); ++ ++ if (!TextFormat && OnlyListBelow) { ++ OS << "The list of functions with the maximum counter less than " ++ << ValueCutoff << ":\n"; ++ } ++ ++ // Add marker so that IR-level instrumentation round-trips properly. ++ if (TextFormat && IsIRInstr) ++ OS << ":ir\n"; ++ ++ for (const auto &Func : *Reader) { ++ if (Reader->isIRLevelProfile()) { ++ bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); ++ if (FuncIsCS != ShowCS) ++ continue; ++ } ++ bool Show = ++ ShowAllFunctions || (!ShowFunction.empty() && ++ Func.Name.find(ShowFunction) != Func.Name.npos); ++ ++ bool doTextFormatDump = (Show && TextFormat); ++ ++ if (doTextFormatDump) { ++ InstrProfSymtab &Symtab = Reader->getSymtab(); ++ InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, ++ OS); ++ continue; ++ } ++ ++ assert(Func.Counts.size() > 0 && "function missing entry counter"); ++ Builder.addRecord(Func); ++ ++ uint64_t FuncMax = 0; ++ uint64_t FuncSum = 0; ++ for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { ++ FuncMax = std::max(FuncMax, Func.Counts[I]); ++ FuncSum += Func.Counts[I]; ++ } ++ ++ if (FuncMax < ValueCutoff) { ++ ++BelowCutoffFunctions; ++ if (OnlyListBelow) { ++ OS << " " << Func.Name << ": (Max = " << FuncMax ++ << " Sum = " << FuncSum << ")\n"; ++ } ++ continue; ++ } else if (OnlyListBelow) ++ continue; ++ ++ if (TopN) { ++ if (HottestFuncs.size() == TopN) { ++ if (HottestFuncs.top().second < FuncMax) { ++ HottestFuncs.pop(); ++ HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); ++ } ++ } else ++ HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); ++ } ++ ++ if (Show) { ++ if (!ShownFunctions) ++ OS << "Counters:\n"; ++ ++ ++ShownFunctions; ++ ++ OS << " " << Func.Name << ":\n" ++ << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" ++ << " Counters: " << Func.Counts.size() << "\n"; ++ if (!IsIRInstr) ++ OS << " Function count: " << Func.Counts[0] << "\n"; ++ ++ if (ShowIndirectCallTargets) ++ OS << " Indirect Call Site Count: " ++ << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; ++ ++ uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); ++ if (ShowMemOPSizes && NumMemOPCalls > 0) ++ OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls ++ << "\n"; ++ ++ if (ShowCounts) { ++ OS << " Block counts: ["; ++ size_t Start = (IsIRInstr ? 0 : 1); ++ for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { ++ OS << (I == Start ? "" : ", ") << Func.Counts[I]; ++ } ++ OS << "]\n"; ++ } ++ ++ if (ShowIndirectCallTargets) { ++ OS << " Indirect Target Results:\n"; ++ traverseAllValueSites(Func, IPVK_IndirectCallTarget, ++ VPStats[IPVK_IndirectCallTarget], OS, ++ &(Reader->getSymtab())); ++ } ++ ++ if (ShowMemOPSizes && NumMemOPCalls > 0) { ++ OS << " Memory Intrinsic Size Results:\n"; ++ traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, ++ nullptr); ++ } ++ } ++ } ++ if (Reader->hasError()) ++ exitWithError(Reader->getError(), Filename); ++ ++ if (TextFormat) ++ return 0; ++ std::unique_ptr PS(Builder.getSummary()); ++ OS << "Instrumentation level: " ++ << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; ++ if (ShowAllFunctions || !ShowFunction.empty()) ++ OS << "Functions shown: " << ShownFunctions << "\n"; ++ OS << "Total functions: " << PS->getNumFunctions() << "\n"; ++ if (ValueCutoff > 0) { ++ OS << "Number of functions with maximum count (< " << ValueCutoff ++ << "): " << BelowCutoffFunctions << "\n"; ++ OS << "Number of functions with maximum count (>= " << ValueCutoff ++ << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; ++ } ++ OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; ++ OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; ++ ++ if (TopN) { ++ std::vector> SortedHottestFuncs; ++ while (!HottestFuncs.empty()) { ++ SortedHottestFuncs.emplace_back(HottestFuncs.top()); ++ HottestFuncs.pop(); ++ } ++ OS << "Top " << TopN ++ << " functions with the largest internal block counts: \n"; ++ for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) ++ OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; ++ } ++ ++ if (ShownFunctions && ShowIndirectCallTargets) { ++ OS << "Statistics for indirect call sites profile:\n"; ++ showValueSitesStats(OS, IPVK_IndirectCallTarget, ++ VPStats[IPVK_IndirectCallTarget]); ++ } ++ ++ if (ShownFunctions && ShowMemOPSizes) { ++ OS << "Statistics for memory intrinsic calls sizes profile:\n"; ++ showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); ++ } ++ ++ if (ShowDetailedSummary) { ++ OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; ++ OS << "Total count: " << PS->getTotalCount() << "\n"; ++ PS->printDetailedSummary(OS); ++ } ++ return 0; ++} ++ ++static void showSectionInfo(sampleprof::SampleProfileReader *Reader, ++ raw_fd_ostream &OS) { ++ if (!Reader->dumpSectionInfo(OS)) { ++ WithColor::warning() << "-show-sec-info-only is only supported for " ++ << "sample profile in extbinary format and is " ++ << "ignored for other formats.\n"; ++ return; ++ } ++} ++ ++namespace { ++struct HotFuncInfo { ++ StringRef FuncName; ++ uint64_t TotalCount; ++ double TotalCountPercent; ++ uint64_t MaxCount; ++ uint64_t EntryCount; ++ ++ HotFuncInfo() ++ : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), ++ EntryCount(0) {} ++ ++ HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) ++ : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS), ++ EntryCount(ES) {} ++}; ++} // namespace ++ ++// Print out detailed information about hot functions in PrintValues vector. ++// Users specify titles and offset of every columns through ColumnTitle and ++// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same ++// and at least 4. Besides, users can optionally give a HotFuncMetric string to ++// print out or let it be an empty string. ++static void dumpHotFunctionList(const std::vector &ColumnTitle, ++ const std::vector &ColumnOffset, ++ const std::vector &PrintValues, ++ uint64_t HotFuncCount, uint64_t TotalFuncCount, ++ uint64_t HotProfCount, uint64_t TotalProfCount, ++ const std::string &HotFuncMetric, ++ raw_fd_ostream &OS) { ++ assert(ColumnOffset.size() == ColumnTitle.size()); ++ assert(ColumnTitle.size() >= 4); ++ assert(TotalFuncCount > 0); ++ double TotalProfPercent = 0; ++ if (TotalProfCount > 0) ++ TotalProfPercent = ((double)HotProfCount) / TotalProfCount * 100; ++ ++ formatted_raw_ostream FOS(OS); ++ FOS << HotFuncCount << " out of " << TotalFuncCount ++ << " functions with profile (" ++ << format("%.2f%%", (((double)HotFuncCount) / TotalFuncCount * 100)) ++ << ") are considered hot functions"; ++ if (!HotFuncMetric.empty()) ++ FOS << " (" << HotFuncMetric << ")"; ++ FOS << ".\n"; ++ FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" ++ << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; ++ ++ for (size_t I = 0; I < ColumnTitle.size(); ++I) { ++ FOS.PadToColumn(ColumnOffset[I]); ++ FOS << ColumnTitle[I]; ++ } ++ FOS << "\n"; ++ ++ for (const HotFuncInfo &R : PrintValues) { ++ FOS.PadToColumn(ColumnOffset[0]); ++ FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; ++ FOS.PadToColumn(ColumnOffset[1]); ++ FOS << R.MaxCount; ++ FOS.PadToColumn(ColumnOffset[2]); ++ FOS << R.EntryCount; ++ FOS.PadToColumn(ColumnOffset[3]); ++ FOS << R.FuncName << "\n"; ++ } ++ return; ++} ++ ++static int ++showHotFunctionList(const StringMap &Profiles, ++ ProfileSummary &PS, raw_fd_ostream &OS) { ++ using namespace sampleprof; ++ ++ const uint32_t HotFuncCutoff = 990000; ++ auto &SummaryVector = PS.getDetailedSummary(); ++ uint64_t MinCountThreshold = 0; ++ for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { ++ if (SummaryEntry.Cutoff == HotFuncCutoff) { ++ MinCountThreshold = SummaryEntry.MinCount; ++ break; ++ } ++ } ++ assert(MinCountThreshold != 0); ++ ++ // Traverse all functions in the profile and keep only hot functions. ++ // The following loop also calculates the sum of total samples of all ++ // functions. ++ std::multimap, ++ std::greater> ++ HotFunc; ++ uint64_t ProfileTotalSample = 0; ++ uint64_t HotFuncSample = 0; ++ uint64_t HotFuncCount = 0; ++ uint64_t MaxCount = 0; ++ for (const auto &I : Profiles) { ++ const FunctionSamples &FuncProf = I.second; ++ ProfileTotalSample += FuncProf.getTotalSamples(); ++ MaxCount = FuncProf.getMaxCountInside(); ++ ++ // MinCountThreshold is a block/line threshold computed for a given cutoff. ++ // We intentionally compare the maximum sample count in a function with this ++ // threshold to get an approximate threshold for hot functions. ++ if (MaxCount >= MinCountThreshold) { ++ HotFunc.emplace(FuncProf.getTotalSamples(), ++ std::make_pair(&(I.second), MaxCount)); ++ HotFuncSample += FuncProf.getTotalSamples(); ++ ++HotFuncCount; ++ } ++ } ++ ++ std::vector ColumnTitle{"Total sample (%)", "Max sample", ++ "Entry sample", "Function name"}; ++ std::vector ColumnOffset{0, 24, 42, 58}; ++ std::string Metric = ++ std::string("max sample >= ") + std::to_string(MinCountThreshold); ++ std::vector PrintValues; ++ for (const auto &FuncPair : HotFunc) { ++ const FunctionSamples &Func = *FuncPair.second.first; ++ double TotalSamplePercent = ++ (ProfileTotalSample > 0) ++ ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample ++ : 0; ++ PrintValues.emplace_back(HotFuncInfo( ++ Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent, ++ FuncPair.second.second, Func.getEntrySamples())); ++ } ++ dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, ++ Profiles.size(), HotFuncSample, ProfileTotalSample, ++ Metric, OS); ++ ++ return 0; ++} ++ ++static int showSampleProfile(const std::string &Filename, bool ShowCounts, ++ bool ShowAllFunctions, bool ShowDetailedSummary, ++ const std::string &ShowFunction, ++ bool ShowProfileSymbolList, ++ bool ShowSectionInfoOnly, bool ShowHotFuncList, ++ raw_fd_ostream &OS) { ++ using namespace sampleprof; ++ LLVMContext Context; ++ auto ReaderOrErr = SampleProfileReader::create(Filename, Context); ++ if (std::error_code EC = ReaderOrErr.getError()) ++ exitWithErrorCode(EC, Filename); ++ ++ auto Reader = std::move(ReaderOrErr.get()); ++ ++ if (ShowSectionInfoOnly) { ++ showSectionInfo(Reader.get(), OS); ++ return 0; ++ } ++ ++ if (std::error_code EC = Reader->read()) ++ exitWithErrorCode(EC, Filename); ++ ++ if (ShowAllFunctions || ShowFunction.empty()) ++ Reader->dump(OS); ++ else ++ Reader->dumpFunctionProfile(ShowFunction, OS); ++ ++ if (ShowProfileSymbolList) { ++ std::unique_ptr ReaderList = ++ Reader->getProfileSymbolList(); ++ ReaderList->dump(OS); ++ } ++ ++ if (ShowDetailedSummary) { ++ auto &PS = Reader->getSummary(); ++ PS.printSummary(OS); ++ PS.printDetailedSummary(OS); ++ } ++ ++ if (ShowHotFuncList) ++ showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS); ++ ++ return 0; ++} ++ ++static int show_main(int argc, const char *argv[]) { ++ cl::opt Filename(cl::Positional, cl::Required, ++ cl::desc("")); ++ ++ cl::opt ShowCounts("counts", cl::init(false), ++ cl::desc("Show counter values for shown functions")); ++ cl::opt TextFormat( ++ "text", cl::init(false), ++ cl::desc("Show instr profile data in text dump format")); ++ cl::opt ShowIndirectCallTargets( ++ "ic-targets", cl::init(false), ++ cl::desc("Show indirect call site target values for shown functions")); ++ cl::opt ShowMemOPSizes( ++ "memop-sizes", cl::init(false), ++ cl::desc("Show the profiled sizes of the memory intrinsic calls " ++ "for shown functions")); ++ cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), ++ cl::desc("Show detailed profile summary")); ++ cl::list DetailedSummaryCutoffs( ++ cl::CommaSeparated, "detailed-summary-cutoffs", ++ cl::desc( ++ "Cutoff percentages (times 10000) for generating detailed summary"), ++ cl::value_desc("800000,901000,999999")); ++ cl::opt ShowHotFuncList( ++ "hot-func-list", cl::init(false), ++ cl::desc("Show profile summary of a list of hot functions")); ++ cl::opt ShowAllFunctions("all-functions", cl::init(false), ++ cl::desc("Details for every function")); ++ cl::opt ShowCS("showcs", cl::init(false), ++ cl::desc("Show context sensitive counts")); ++ cl::opt ShowFunction("function", ++ cl::desc("Details for matching functions")); ++ ++ cl::opt OutputFilename("output", cl::value_desc("output"), ++ cl::init("-"), cl::desc("Output file")); ++ cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), ++ cl::aliasopt(OutputFilename)); ++ cl::opt ProfileKind( ++ cl::desc("Profile kind:"), cl::init(instr), ++ cl::values(clEnumVal(instr, "Instrumentation profile (default)"), ++ clEnumVal(sample, "Sample profile"))); ++ cl::opt TopNFunctions( ++ "topn", cl::init(0), ++ cl::desc("Show the list of functions with the largest internal counts")); ++ cl::opt ValueCutoff( ++ "value-cutoff", cl::init(0), ++ cl::desc("Set the count value cutoff. Functions with the maximum count " ++ "less than this value will not be printed out. (Default is 0)")); ++ cl::opt OnlyListBelow( ++ "list-below-cutoff", cl::init(false), ++ cl::desc("Only output names of functions whose max count values are " ++ "below the cutoff value")); ++ cl::opt ShowProfileSymbolList( ++ "show-prof-sym-list", cl::init(false), ++ cl::desc("Show profile symbol list if it exists in the profile. ")); ++ cl::opt ShowSectionInfoOnly( ++ "show-sec-info-only", cl::init(false), ++ cl::desc("Show the information of each section in the sample profile. " ++ "The flag is only usable when the sample profile is in " ++ "extbinary format")); ++ ++ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); ++ ++ if (OutputFilename.empty()) ++ OutputFilename = "-"; ++ ++ if (!Filename.compare(OutputFilename)) { ++ errs() << sys::path::filename(argv[0]) ++ << ": Input file name cannot be the same as the output file name!\n"; ++ return 1; ++ } ++ ++ std::error_code EC; ++ raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); ++ if (EC) ++ exitWithErrorCode(EC, OutputFilename); ++ ++ if (ShowAllFunctions && !ShowFunction.empty()) ++ WithColor::warning() << "-function argument ignored: showing all functions\n"; ++ ++ if (ProfileKind == instr) ++ return showInstrProfile(Filename, ShowCounts, TopNFunctions, ++ ShowIndirectCallTargets, ShowMemOPSizes, ++ ShowDetailedSummary, DetailedSummaryCutoffs, ++ ShowAllFunctions, ShowCS, ValueCutoff, ++ OnlyListBelow, ShowFunction, TextFormat, OS); ++ else ++ return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ++ ShowDetailedSummary, ShowFunction, ++ ShowProfileSymbolList, ShowSectionInfoOnly, ++ ShowHotFuncList, OS); ++} ++ ++int main(int argc, const char *argv[]) { ++ InitLLVM X(argc, argv); ++ ++ StringRef ProgName(sys::path::filename(argv[0])); ++ if (argc > 1) { ++ int (*func)(int, const char *[]) = nullptr; ++ ++ if (strcmp(argv[1], "merge") == 0) ++ func = merge_main; ++ else if (strcmp(argv[1], "show") == 0) ++ func = show_main; ++ else if (strcmp(argv[1], "overlap") == 0) ++ func = overlap_main; ++ ++ if (func) { ++ std::string Invocation(ProgName.str() + " " + argv[1]); ++ argv[1] = Invocation.c_str(); ++ return func(argc - 1, argv + 1); ++ } ++ ++ if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || ++ strcmp(argv[1], "--help") == 0) { ++ ++ errs() << "OVERVIEW: LLVM profile data tools\n\n" ++ << "USAGE: " << ProgName << " [args...]\n" ++ << "USAGE: " << ProgName << " -help\n\n" ++ << "See each individual command --help for more details.\n" ++ << "Available commands: merge, show, overlap\n"; ++ return 0; ++ } ++ } ++ ++ if (argc < 2) ++ errs() << ProgName << ": No command specified!\n"; ++ else ++ errs() << ProgName << ": Unknown command!\n"; ++ ++ errs() << "USAGE: " << ProgName << " [args...]\n"; ++ return 1; ++} +diff --git a/utils/FileCheck-11.0.cpp b/utils/FileCheck-11.0.cpp +new file mode 100644 +index 00000000..fa79c5e8 +--- /dev/null ++++ b/utils/FileCheck-11.0.cpp +@@ -0,0 +1,859 @@ ++//===- FileCheck.cpp - Check that File's Contents match what is expected --===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++// ++// FileCheck does a line-by line check of a file that validates whether it ++// contains the expected content. This is useful for regression tests etc. ++// ++// This program exits with an exit status of 2 on error, exit status of 0 if ++// the file matched the expected contents, and exit status of 1 if it did not ++// contain the expected contents. ++// ++//===----------------------------------------------------------------------===// ++ ++#include "llvm/Support/CommandLine.h" ++#include "llvm/Support/InitLLVM.h" ++#include "llvm/Support/Process.h" ++#include "llvm/Support/WithColor.h" ++#include "llvm/Support/raw_ostream.h" ++#include "llvm/Support/FileCheck.h" ++#include ++using namespace llvm; ++ ++static cl::extrahelp FileCheckOptsEnv( ++ "\nOptions are parsed from the environment variable FILECHECK_OPTS and\n" ++ "from the command line.\n"); ++ ++static cl::opt ++ CheckFilename(cl::Positional, cl::desc(""), cl::Optional); ++ ++static cl::opt ++ InputFilename("input-file", cl::desc("File to check (defaults to stdin)"), ++ cl::init("-"), cl::value_desc("filename")); ++ ++static cl::list CheckPrefixes( ++ "check-prefix", ++ cl::desc("Prefix to use from check file (defaults to 'CHECK')")); ++static cl::alias CheckPrefixesAlias( ++ "check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated, ++ cl::NotHidden, ++ cl::desc( ++ "Alias for -check-prefix permitting multiple comma separated values")); ++ ++static cl::list CommentPrefixes( ++ "comment-prefixes", cl::CommaSeparated, cl::Hidden, ++ cl::desc("Comma-separated list of comment prefixes to use from check file\n" ++ "(defaults to 'COM,RUN'). Please avoid using this feature in\n" ++ "LLVM's LIT-based test suites, which should be easier to\n" ++ "maintain if they all follow a consistent comment style. This\n" ++ "feature is meant for non-LIT test suites using FileCheck.")); ++ ++static cl::opt NoCanonicalizeWhiteSpace( ++ "strict-whitespace", ++ cl::desc("Do not treat all horizontal whitespace as equivalent")); ++ ++static cl::opt IgnoreCase( ++ "ignore-case", ++ cl::desc("Use case-insensitive matching")); ++ ++static cl::list ImplicitCheckNot( ++ "implicit-check-not", ++ cl::desc("Add an implicit negative check with this pattern to every\n" ++ "positive check. This can be used to ensure that no instances of\n" ++ "this pattern occur which are not matched by a positive pattern"), ++ cl::value_desc("pattern")); ++ ++static cl::list ++ GlobalDefines("D", cl::AlwaysPrefix, ++ cl::desc("Define a variable to be used in capture patterns."), ++ cl::value_desc("VAR=VALUE")); ++ ++static cl::opt AllowEmptyInput( ++ "allow-empty", cl::init(false), ++ cl::desc("Allow the input file to be empty. This is useful when making\n" ++ "checks that some error message does not occur, for example.")); ++ ++static cl::opt MatchFullLines( ++ "match-full-lines", cl::init(false), ++ cl::desc("Require all positive matches to cover an entire input line.\n" ++ "Allows leading and trailing whitespace if --strict-whitespace\n" ++ "is not also passed.")); ++ ++static cl::opt EnableVarScope( ++ "enable-var-scope", cl::init(false), ++ cl::desc("Enables scope for regex variables. Variables with names that\n" ++ "do not start with '$' will be reset at the beginning of\n" ++ "each CHECK-LABEL block.")); ++ ++static cl::opt AllowDeprecatedDagOverlap( ++ "allow-deprecated-dag-overlap", cl::init(false), ++ cl::desc("Enable overlapping among matches in a group of consecutive\n" ++ "CHECK-DAG directives. This option is deprecated and is only\n" ++ "provided for convenience as old tests are migrated to the new\n" ++ "non-overlapping CHECK-DAG implementation.\n")); ++ ++static cl::opt Verbose( ++ "v", cl::init(false), cl::ZeroOrMore, ++ cl::desc("Print directive pattern matches, or add them to the input dump\n" ++ "if enabled.\n")); ++ ++static cl::opt VerboseVerbose( ++ "vv", cl::init(false), cl::ZeroOrMore, ++ cl::desc("Print information helpful in diagnosing internal FileCheck\n" ++ "issues, or add it to the input dump if enabled. Implies\n" ++ "-v.\n")); ++ ++// The order of DumpInputValue members affects their precedence, as documented ++// for -dump-input below. ++enum DumpInputValue { ++ DumpInputNever, ++ DumpInputFail, ++ DumpInputAlways, ++ DumpInputHelp ++}; ++ ++static cl::list DumpInputs( ++ "dump-input", ++ cl::desc("Dump input to stderr, adding annotations representing\n" ++ "currently enabled diagnostics. When there are multiple\n" ++ "occurrences of this option, the that appears earliest\n" ++ "in the list below has precedence. The default is 'fail'.\n"), ++ cl::value_desc("mode"), ++ cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"), ++ clEnumValN(DumpInputAlways, "always", "Always dump input"), ++ clEnumValN(DumpInputFail, "fail", "Dump input on failure"), ++ clEnumValN(DumpInputNever, "never", "Never dump input"))); ++ ++// The order of DumpInputFilterValue members affects their precedence, as ++// documented for -dump-input-filter below. ++enum DumpInputFilterValue { ++ DumpInputFilterError, ++ DumpInputFilterAnnotation, ++ DumpInputFilterAnnotationFull, ++ DumpInputFilterAll ++}; ++ ++static cl::list DumpInputFilters( ++ "dump-input-filter", ++ cl::desc("In the dump requested by -dump-input, print only input lines of\n" ++ "kind plus any context specified by -dump-input-context.\n" ++ "When there are multiple occurrences of this option, the \n" ++ "that appears earliest in the list below has precedence. The\n" ++ "default is 'error' when -dump-input=fail, and it's 'all' when\n" ++ "-dump-input=always.\n"), ++ cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"), ++ clEnumValN(DumpInputFilterAnnotationFull, "annotation-full", ++ "Input lines with annotations"), ++ clEnumValN(DumpInputFilterAnnotation, "annotation", ++ "Input lines with starting points of annotations"), ++ clEnumValN(DumpInputFilterError, "error", ++ "Input lines with starting points of error " ++ "annotations"))); ++ ++static cl::list DumpInputContexts( ++ "dump-input-context", cl::value_desc("N"), ++ cl::desc("In the dump requested by -dump-input, print input lines\n" ++ "before and input lines after any lines specified by\n" ++ "-dump-input-filter. When there are multiple occurrences of\n" ++ "this option, the largest specified has precedence. The\n" ++ "default is 5.\n")); ++ ++typedef cl::list::const_iterator prefix_iterator; ++ ++ ++ ++ ++ ++ ++ ++static void DumpCommandLine(int argc, char **argv) { ++ errs() << "FileCheck command line: "; ++ for (int I = 0; I < argc; I++) ++ errs() << " " << argv[I]; ++ errs() << "\n"; ++} ++ ++struct MarkerStyle { ++ /// The starting char (before tildes) for marking the line. ++ char Lead; ++ /// What color to use for this annotation. ++ raw_ostream::Colors Color; ++ /// A note to follow the marker, or empty string if none. ++ std::string Note; ++ /// Does this marker indicate inclusion by -dump-input-filter=error? ++ bool FiltersAsError; ++ MarkerStyle() {} ++ MarkerStyle(char Lead, raw_ostream::Colors Color, ++ const std::string &Note = "", bool FiltersAsError = false) ++ : Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) { ++ assert((!FiltersAsError || !Note.empty()) && ++ "expected error diagnostic to have note"); ++ } ++}; ++ ++static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) { ++ switch (MatchTy) { ++ case FileCheckDiag::MatchFoundAndExpected: ++ return MarkerStyle('^', raw_ostream::GREEN); ++ case FileCheckDiag::MatchFoundButExcluded: ++ return MarkerStyle('!', raw_ostream::RED, "error: no match expected", ++ /*FiltersAsError=*/true); ++ case FileCheckDiag::MatchFoundButWrongLine: ++ return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line", ++ /*FiltersAsError=*/true); ++ case FileCheckDiag::MatchFoundButDiscarded: ++ return MarkerStyle('!', raw_ostream::CYAN, ++ "discard: overlaps earlier match"); ++ case FileCheckDiag::MatchNoneAndExcluded: ++ return MarkerStyle('X', raw_ostream::GREEN); ++ case FileCheckDiag::MatchNoneButExpected: ++ return MarkerStyle('X', raw_ostream::RED, "error: no match found", ++ /*FiltersAsError=*/true); ++ case FileCheckDiag::MatchFuzzy: ++ return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match", ++ /*FiltersAsError=*/true); ++ } ++ llvm_unreachable_internal("unexpected match type"); ++} ++ ++static void DumpInputAnnotationHelp(raw_ostream &OS) { ++ OS << "The following description was requested by -dump-input=help to\n" ++ << "explain the input dump printed by FileCheck.\n" ++ << "\n" ++ << "Related command-line options:\n" ++ << "\n" ++ << " - -dump-input= enables or disables the input dump\n" ++ << " - -dump-input-filter= filters the input lines\n" ++ << " - -dump-input-context= adjusts the context of filtered lines\n" ++ << " - -v and -vv add more annotations\n" ++ << " - -color forces colors to be enabled both in the dump and below\n" ++ << " - -help documents the above options in more detail\n" ++ << "\n" ++ << "These options can also be set via FILECHECK_OPTS. For example, for\n" ++ << "maximum debugging output on failures:\n" ++ << "\n" ++ << " $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n" ++ << "\n" ++ << "Input dump annotation format:\n" ++ << "\n"; ++ ++ // Labels for input lines. ++ OS << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:"; ++ OS << " labels line number L of the input file\n"; ++ ++ // Labels for annotation lines. ++ OS << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L"; ++ OS << " labels the only match result for either (1) a pattern of type T" ++ << " from\n" ++ << " line L of the check file if L is an integer or (2) the" ++ << " I-th implicit\n" ++ << " pattern if L is \"imp\" followed by an integer " ++ << "I (index origin one)\n"; ++ OS << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; ++ OS << " labels the Nth match result for such a pattern\n"; ++ ++ // Markers on annotation lines. ++ OS << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~"; ++ OS << " marks good match (reported if -v)\n" ++ << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~"; ++ OS << " marks bad match, such as:\n" ++ << " - CHECK-NEXT on same line as previous match (error)\n" ++ << " - CHECK-NOT found (error)\n" ++ << " - CHECK-DAG overlapping match (discarded, reported if " ++ << "-vv)\n" ++ << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~"; ++ OS << " marks search range when no match is found, such as:\n" ++ << " - CHECK-NEXT not found (error)\n" ++ << " - CHECK-NOT not found (success, reported if -vv)\n" ++ << " - CHECK-DAG not found after discarded matches (error)\n" ++ << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?"; ++ OS << " marks fuzzy match when no match is found\n"; ++ ++ // Elided lines. ++ OS << " - "; ++ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "..."; ++ OS << " indicates elided input lines and annotations, as specified by\n" ++ << " -dump-input-filter and -dump-input-context\n"; ++ ++ // Colors. ++ OS << " - colors "; ++ WithColor(OS, raw_ostream::GREEN, true) << "success"; ++ OS << ", "; ++ WithColor(OS, raw_ostream::RED, true) << "error"; ++ OS << ", "; ++ WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match"; ++ OS << ", "; ++ WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match"; ++ OS << ", "; ++ WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input"; ++ OS << "\n"; ++} ++ ++/// An annotation for a single input line. ++struct InputAnnotation { ++ /// The index of the match result across all checks ++ unsigned DiagIndex; ++ /// The label for this annotation. ++ std::string Label; ++ /// Is this the initial fragment of a diagnostic that has been broken across ++ /// multiple lines? ++ bool IsFirstLine; ++ /// What input line (one-origin indexing) this annotation marks. This might ++ /// be different from the starting line of the original diagnostic if ++ /// !IsFirstLine. ++ unsigned InputLine; ++ /// The column range (one-origin indexing, open end) in which to mark the ++ /// input line. If InputEndCol is UINT_MAX, treat it as the last column ++ /// before the newline. ++ unsigned InputStartCol, InputEndCol; ++ /// The marker to use. ++ MarkerStyle Marker; ++ /// Whether this annotation represents a good match for an expected pattern. ++ bool FoundAndExpectedMatch; ++}; ++ ++/// Get an abbreviation for the check type. ++std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) { ++ switch (Ty) { ++ case Check::CheckPlain: ++ if (Ty.getCount() > 1) ++ return "count"; ++ return "check"; ++ case Check::CheckNext: ++ return "next"; ++ case Check::CheckSame: ++ return "same"; ++ case Check::CheckNot: ++ return "not"; ++ case Check::CheckDAG: ++ return "dag"; ++ case Check::CheckLabel: ++ return "label"; ++ case Check::CheckEmpty: ++ return "empty"; ++ case Check::CheckComment: ++ return "com"; ++ case Check::CheckEOF: ++ return "eof"; ++ case Check::CheckBadNot: ++ return "bad-not"; ++ case Check::CheckBadCount: ++ return "bad-count"; ++ case Check::CheckNone: ++ llvm_unreachable("invalid FileCheckType"); ++ } ++ llvm_unreachable("unknown FileCheckType"); ++} ++ ++static void ++BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID, ++ const std::pair &ImpPatBufferIDRange, ++ const std::vector &Diags, ++ std::vector &Annotations, ++ unsigned &LabelWidth) { ++ // How many diagnostics have we seen so far? ++ unsigned DiagCount = 0; ++ // How many diagnostics has the current check seen so far? ++ unsigned CheckDiagCount = 0; ++ // What's the widest label? ++ LabelWidth = 0; ++ for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd; ++ ++DiagItr) { ++ InputAnnotation A; ++ A.DiagIndex = DiagCount++; ++ ++ // Build label, which uniquely identifies this check result. ++ unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc); ++ auto CheckLineAndCol = ++ SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID); ++ llvm::raw_string_ostream Label(A.Label); ++ Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"; ++ if (CheckBufferID == CheckFileBufferID) ++ Label << CheckLineAndCol.first; ++ else if (ImpPatBufferIDRange.first <= CheckBufferID && ++ CheckBufferID < ImpPatBufferIDRange.second) ++ Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1); ++ else ++ llvm_unreachable("expected diagnostic's check location to be either in " ++ "the check file or for an implicit pattern"); ++ unsigned CheckDiagIndex = UINT_MAX; ++ auto DiagNext = std::next(DiagItr); ++ if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && ++ DiagItr->CheckLoc == DiagNext->CheckLoc) ++ CheckDiagIndex = CheckDiagCount++; ++ else if (CheckDiagCount) { ++ CheckDiagIndex = CheckDiagCount; ++ CheckDiagCount = 0; ++ } ++ if (CheckDiagIndex != UINT_MAX) ++ Label << "'" << CheckDiagIndex; ++ Label.flush(); ++ LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); ++ ++ A.Marker = GetMarker(DiagItr->MatchTy); ++ A.FoundAndExpectedMatch = ++ DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; ++ ++ // Compute the mark location, and break annotation into multiple ++ // annotations if it spans multiple lines. ++ A.IsFirstLine = true; ++ A.InputLine = DiagItr->InputStartLine; ++ A.InputStartCol = DiagItr->InputStartCol; ++ if (DiagItr->InputStartLine == DiagItr->InputEndLine) { ++ // Sometimes ranges are empty in order to indicate a specific point, but ++ // that would mean nothing would be marked, so adjust the range to ++ // include the following character. ++ A.InputEndCol = ++ std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol); ++ Annotations.push_back(A); ++ } else { ++ assert(DiagItr->InputStartLine < DiagItr->InputEndLine && ++ "expected input range not to be inverted"); ++ A.InputEndCol = UINT_MAX; ++ Annotations.push_back(A); ++ for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine; ++ L <= E; ++L) { ++ // If a range ends before the first column on a line, then it has no ++ // characters on that line, so there's nothing to render. ++ if (DiagItr->InputEndCol == 1 && L == E) ++ break; ++ InputAnnotation B; ++ B.DiagIndex = A.DiagIndex; ++ B.Label = A.Label; ++ B.IsFirstLine = false; ++ B.InputLine = L; ++ B.Marker = A.Marker; ++ B.Marker.Lead = '~'; ++ B.Marker.Note = ""; ++ B.InputStartCol = 1; ++ if (L != E) ++ B.InputEndCol = UINT_MAX; ++ else ++ B.InputEndCol = DiagItr->InputEndCol; ++ B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; ++ Annotations.push_back(B); ++ } ++ } ++ } ++} ++ ++static unsigned FindInputLineInFilter( ++ DumpInputFilterValue DumpInputFilter, unsigned CurInputLine, ++ const std::vector::iterator &AnnotationBeg, ++ const std::vector::iterator &AnnotationEnd) { ++ if (DumpInputFilter == DumpInputFilterAll) ++ return CurInputLine; ++ for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd; ++ ++AnnotationItr) { ++ switch (DumpInputFilter) { ++ case DumpInputFilterAll: ++ llvm_unreachable("unexpected DumpInputFilterAll"); ++ break; ++ case DumpInputFilterAnnotationFull: ++ return AnnotationItr->InputLine; ++ case DumpInputFilterAnnotation: ++ if (AnnotationItr->IsFirstLine) ++ return AnnotationItr->InputLine; ++ break; ++ case DumpInputFilterError: ++ if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError) ++ return AnnotationItr->InputLine; ++ break; ++ } ++ } ++ return UINT_MAX; ++} ++ ++/// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would ++/// occupy less lines than ElidedLines, but print ElidedLines otherwise. Either ++/// way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing. ++static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines, ++ unsigned LabelWidth) { ++ if (ElidedLines.empty()) ++ return; ++ unsigned EllipsisLines = 3; ++ if (EllipsisLines < StringRef(ElidedLines).count('\n')) { ++ for (unsigned i = 0; i < EllipsisLines; ++i) { ++ WithColor(OS, raw_ostream::BLACK, /*Bold=*/true) ++ << right_justify(".", LabelWidth); ++ OS << '\n'; ++ } ++ } else ++ OS << ElidedLines; ++ ElidedLines.clear(); ++} ++ ++static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, ++ DumpInputFilterValue DumpInputFilter, ++ unsigned DumpInputContext, ++ StringRef InputFileText, ++ std::vector &Annotations, ++ unsigned LabelWidth) { ++ OS << "Input was:\n<<<<<<\n"; ++ ++ // Sort annotations. ++ std::sort(Annotations.begin(), Annotations.end(), ++ [](const InputAnnotation &A, const InputAnnotation &B) { ++ // 1. Sort annotations in the order of the input lines. ++ // ++ // This makes it easier to find relevant annotations while ++ // iterating input lines in the implementation below. FileCheck ++ // does not always produce diagnostics in the order of input ++ // lines due to, for example, CHECK-DAG and CHECK-NOT. ++ if (A.InputLine != B.InputLine) ++ return A.InputLine < B.InputLine; ++ // 2. Sort annotations in the temporal order FileCheck produced ++ // their associated diagnostics. ++ // ++ // This sort offers several benefits: ++ // ++ // A. On a single input line, the order of annotations reflects ++ // the FileCheck logic for processing directives/patterns. ++ // This can be helpful in understanding cases in which the ++ // order of the associated directives/patterns in the check ++ // file or on the command line either (i) does not match the ++ // temporal order in which FileCheck looks for matches for the ++ // directives/patterns (due to, for example, CHECK-LABEL, ++ // CHECK-NOT, or `--implicit-check-not`) or (ii) does match ++ // that order but does not match the order of those ++ // diagnostics along an input line (due to, for example, ++ // CHECK-DAG). ++ // ++ // On the other hand, because our presentation format presents ++ // input lines in order, there's no clear way to offer the ++ // same benefit across input lines. For consistency, it might ++ // then seem worthwhile to have annotations on a single line ++ // also sorted in input order (that is, by input column). ++ // However, in practice, this appears to be more confusing ++ // than helpful. Perhaps it's intuitive to expect annotations ++ // to be listed in the temporal order in which they were ++ // produced except in cases the presentation format obviously ++ // and inherently cannot support it (that is, across input ++ // lines). ++ // ++ // B. When diagnostics' annotations are split among multiple ++ // input lines, the user must track them from one input line ++ // to the next. One property of the sort chosen here is that ++ // it facilitates the user in this regard by ensuring the ++ // following: when comparing any two input lines, a ++ // diagnostic's annotations are sorted in the same position ++ // relative to all other diagnostics' annotations. ++ return A.DiagIndex < B.DiagIndex; ++ }); ++ ++ // Compute the width of the label column. ++ const unsigned char *InputFilePtr = InputFileText.bytes_begin(), ++ *InputFileEnd = InputFileText.bytes_end(); ++ unsigned LineCount = InputFileText.count('\n'); ++ if (InputFileEnd[-1] != '\n') ++ ++LineCount; ++ unsigned LineNoWidth = std::log10(LineCount) + 1; ++ // +3 below adds spaces (1) to the left of the (right-aligned) line numbers ++ // on input lines and (2) to the right of the (left-aligned) labels on ++ // annotation lines so that input lines and annotation lines are more ++ // visually distinct. For example, the spaces on the annotation lines ensure ++ // that input line numbers and check directive line numbers never align ++ // horizontally. Those line numbers might not even be for the same file. ++ // One space would be enough to achieve that, but more makes it even easier ++ // to see. ++ LabelWidth = std::max(LabelWidth, LineNoWidth) + 3; ++ ++ // Print annotated input lines. ++ unsigned PrevLineInFilter = 0; // 0 means none so far ++ unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none ++ std::string ElidedLines; ++ raw_string_ostream ElidedLinesOS(ElidedLines); ++ ColorMode TheColorMode = ++ WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable; ++ if (TheColorMode == ColorMode::Enable) ++ ElidedLinesOS.enable_colors(true); ++ auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); ++ for (unsigned Line = 1; ++ InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; ++ ++Line) { ++ const unsigned char *InputFileLine = InputFilePtr; ++ ++ // Compute the previous and next line included by the filter. ++ if (NextLineInFilter < Line) ++ NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line, ++ AnnotationItr, AnnotationEnd); ++ assert(NextLineInFilter && "expected NextLineInFilter to be computed"); ++ if (NextLineInFilter == Line) ++ PrevLineInFilter = Line; ++ ++ // Elide this input line and its annotations if it's not within the ++ // context specified by -dump-input-context of an input line included by ++ // -dump-input-filter. However, in case the resulting ellipsis would occupy ++ // more lines than the input lines and annotations it elides, buffer the ++ // elided lines and annotations so we can print them instead. ++ raw_ostream *LineOS = &OS; ++ if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) && ++ (NextLineInFilter == UINT_MAX || ++ Line + DumpInputContext < NextLineInFilter)) ++ LineOS = &ElidedLinesOS; ++ else { ++ LineOS = &OS; ++ DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); ++ } ++ ++ // Print right-aligned line number. ++ WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false, ++ TheColorMode) ++ << format_decimal(Line, LabelWidth) << ": "; ++ ++ // For the case where -v and colors are enabled, find the annotations for ++ // good matches for expected patterns in order to highlight everything ++ // else in the line. There are no such annotations if -v is disabled. ++ std::vector FoundAndExpectedMatches; ++ if (Req.Verbose && TheColorMode == ColorMode::Enable) { ++ for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line; ++ ++I) { ++ if (I->FoundAndExpectedMatch) ++ FoundAndExpectedMatches.push_back(*I); ++ } ++ } ++ ++ // Print numbered line with highlighting where there are no matches for ++ // expected patterns. ++ bool Newline = false; ++ { ++ WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false, ++ /*BG=*/false, TheColorMode); ++ bool InMatch = false; ++ if (Req.Verbose) ++ COS.changeColor(raw_ostream::CYAN, true, true); ++ for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) { ++ bool WasInMatch = InMatch; ++ InMatch = false; ++ for (auto M : FoundAndExpectedMatches) { ++ if (M.InputStartCol <= Col && Col < M.InputEndCol) { ++ InMatch = true; ++ break; ++ } ++ } ++ if (!WasInMatch && InMatch) ++ COS.resetColor(); ++ else if (WasInMatch && !InMatch) ++ COS.changeColor(raw_ostream::CYAN, true, true); ++ if (*InputFilePtr == '\n') ++ Newline = true; ++ else ++ COS << *InputFilePtr; ++ ++InputFilePtr; ++ } ++ } ++ *LineOS << '\n'; ++ unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; ++ ++ // Print any annotations. ++ while (AnnotationItr != AnnotationEnd && ++ AnnotationItr->InputLine == Line) { ++ WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true, ++ /*BG=*/false, TheColorMode); ++ // The two spaces below are where the ": " appears on input lines. ++ COS << left_justify(AnnotationItr->Label, LabelWidth) << " "; ++ unsigned Col; ++ for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col) ++ COS << ' '; ++ COS << AnnotationItr->Marker.Lead; ++ // If InputEndCol=UINT_MAX, stop at InputLineWidth. ++ for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth; ++ ++Col) ++ COS << '~'; ++ const std::string &Note = AnnotationItr->Marker.Note; ++ if (!Note.empty()) { ++ // Put the note at the end of the input line. If we were to instead ++ // put the note right after the marker, subsequent annotations for the ++ // same input line might appear to mark this note instead of the input ++ // line. ++ for (; Col <= InputLineWidth; ++Col) ++ COS << ' '; ++ COS << ' ' << Note; ++ } ++ COS << '\n'; ++ ++AnnotationItr; ++ } ++ } ++ DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth); ++ ++ OS << ">>>>>>\n"; ++} ++ ++int main(int argc, char **argv) { ++ // Enable use of ANSI color codes because FileCheck is using them to ++ // highlight text. ++ llvm::sys::Process::UseANSIEscapeCodes(true); ++ ++ InitLLVM X(argc, argv); ++ cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr, ++ "FILECHECK_OPTS"); ++ ++ // Select -dump-input* values. The -help documentation specifies the default ++ // value and which value to choose if an option is specified multiple times. ++ // In the latter case, the general rule of thumb is to choose the value that ++ // provides the most information. ++ DumpInputValue DumpInput = ++ DumpInputs.empty() ++ ? DumpInputFail ++ : *std::max_element(DumpInputs.begin(), DumpInputs.end()); ++ DumpInputFilterValue DumpInputFilter; ++ if (DumpInputFilters.empty()) ++ DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll ++ : DumpInputFilterError; ++ else ++ DumpInputFilter = ++ *std::max_element(DumpInputFilters.begin(), DumpInputFilters.end()); ++ unsigned DumpInputContext = DumpInputContexts.empty() ++ ? 5 ++ : *std::max_element(DumpInputContexts.begin(), ++ DumpInputContexts.end()); ++ ++ if (DumpInput == DumpInputHelp) { ++ DumpInputAnnotationHelp(outs()); ++ return 0; ++ } ++ if (CheckFilename.empty()) { ++ errs() << " not specified\n"; ++ return 2; ++ } ++ ++ FileCheckRequest Req; ++ for (StringRef Prefix : CheckPrefixes) ++ Req.CheckPrefixes.push_back(Prefix); ++ ++ for (StringRef Prefix : CommentPrefixes) ++ Req.CommentPrefixes.push_back(Prefix); ++ ++ for (StringRef CheckNot : ImplicitCheckNot) ++ Req.ImplicitCheckNot.push_back(CheckNot); ++ ++ bool GlobalDefineError = false; ++ for (StringRef G : GlobalDefines) { ++ size_t EqIdx = G.find('='); ++ if (EqIdx == std::string::npos) { ++ errs() << "Missing equal sign in command-line definition '-D" << G ++ << "'\n"; ++ GlobalDefineError = true; ++ continue; ++ } ++ if (EqIdx == 0) { ++ errs() << "Missing variable name in command-line definition '-D" << G ++ << "'\n"; ++ GlobalDefineError = true; ++ continue; ++ } ++ Req.GlobalDefines.push_back(G); ++ } ++ if (GlobalDefineError) ++ return 2; ++ ++ Req.AllowEmptyInput = AllowEmptyInput; ++ Req.EnableVarScope = EnableVarScope; ++ Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap; ++ Req.Verbose = Verbose; ++ Req.VerboseVerbose = VerboseVerbose; ++ Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace; ++ Req.MatchFullLines = MatchFullLines; ++ Req.IgnoreCase = IgnoreCase; ++ ++ if (VerboseVerbose) ++ Req.Verbose = true; ++ ++ FileCheck FC(Req); ++ if (!FC.ValidateCheckPrefixes()) ++ return 2; ++ ++ Regex PrefixRE = FC.buildCheckPrefixRegex(); ++ std::string REError; ++ if (!PrefixRE.isValid(REError)) { ++ errs() << "Unable to combine check-prefix strings into a prefix regular " ++ "expression! This is likely a bug in FileCheck's verification of " ++ "the check-prefix strings. Regular expression parsing failed " ++ "with the following error: " ++ << REError << "\n"; ++ return 2; ++ } ++ ++ SourceMgr SM; ++ ++ // Read the expected strings from the check file. ++ ErrorOr> CheckFileOrErr = ++ MemoryBuffer::getFileOrSTDIN(CheckFilename); ++ if (std::error_code EC = CheckFileOrErr.getError()) { ++ errs() << "Could not open check file '" << CheckFilename ++ << "': " << EC.message() << '\n'; ++ return 2; ++ } ++ MemoryBuffer &CheckFile = *CheckFileOrErr.get(); ++ ++ SmallString<4096> CheckFileBuffer; ++ StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer); ++ ++ unsigned CheckFileBufferID = ++ SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( ++ CheckFileText, CheckFile.getBufferIdentifier()), ++ SMLoc()); ++ ++ std::pair ImpPatBufferIDRange; ++ if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange)) ++ return 2; ++ ++ // Open the file to check and add it to SourceMgr. ++ ErrorOr> InputFileOrErr = ++ MemoryBuffer::getFileOrSTDIN(InputFilename); ++ if (InputFilename == "-") ++ InputFilename = ""; // Overwrite for improved diagnostic messages ++ if (std::error_code EC = InputFileOrErr.getError()) { ++ errs() << "Could not open input file '" << InputFilename ++ << "': " << EC.message() << '\n'; ++ return 2; ++ } ++ MemoryBuffer &InputFile = *InputFileOrErr.get(); ++ ++ if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) { ++ errs() << "FileCheck error: '" << InputFilename << "' is empty.\n"; ++ DumpCommandLine(argc, argv); ++ return 2; ++ } ++ ++ SmallString<4096> InputFileBuffer; ++ StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer); ++ ++ SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( ++ InputFileText, InputFile.getBufferIdentifier()), ++ SMLoc()); ++ ++ std::vector Diags; ++ int ExitCode = FC.checkInput(SM, InputFileText, ++ DumpInput == DumpInputNever ? nullptr : &Diags) ++ ? EXIT_SUCCESS ++ : 1; ++ if (DumpInput == DumpInputAlways || ++ (ExitCode == 1 && DumpInput == DumpInputFail)) { ++ errs() << "\n" ++ << "Input file: " << InputFilename << "\n" ++ << "Check file: " << CheckFilename << "\n" ++ << "\n" ++ << "-dump-input=help explains the following input dump.\n" ++ << "\n"; ++ std::vector Annotations; ++ unsigned LabelWidth; ++ BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags, ++ Annotations, LabelWidth); ++ DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext, ++ InputFileText, Annotations, LabelWidth); ++ } ++ ++ return ExitCode; ++} +diff --git a/utils/gen_gccbuiltins.cpp b/utils/gen_gccbuiltins.cpp +index 3afdfd12..cc7bd1a5 100644 +--- a/utils/gen_gccbuiltins.cpp ++++ b/utils/gen_gccbuiltins.cpp +@@ -68,16 +68,17 @@ string dtype(Record* rec, bool readOnlyMem) + return ""; + } + +-string attributes(ListInit* propertyList) ++StringRef attributes(ListInit* propertyList) + { +- string prop = +- propertyList->size() +- ? propertyList->getElementAsRecord(0)->getName() : ""; +- +- return +- prop == "IntrNoMem" ? " pure @safe" : +- prop == "IntrReadArgMem" ? " pure" : +- prop == "IntrReadWriteArgMem" ? " pure" : ""; ++ const auto prop = propertyList->size() ++ ? propertyList->getElementAsRecord(0)->getName() ++ : ""; ++ ++ if (prop == "IntrNoMem") ++ return " pure @safe"; ++ if (prop == "IntrReadArgMem" || prop == "IntrReadWriteArgMem") ++ return " pure"; ++ return ""; + } + + void processRecord(raw_ostream& os, Record& rec, string arch) +@@ -85,8 +86,8 @@ void processRecord(raw_ostream& os, Record& rec, string arch) + if(!rec.getValue("GCCBuiltinName")) + return; + +- string builtinName = rec.getValueAsString("GCCBuiltinName"); +- string name = rec.getName(); ++ const StringRef builtinName = rec.getValueAsString("GCCBuiltinName"); ++ string name = rec.getName().str(); + + if(name.substr(0, 4) != "int_" || name.find(arch) == string::npos) + return; +@@ -96,9 +97,8 @@ void processRecord(raw_ostream& os, Record& rec, string arch) + name = string("llvm.") + name; + + ListInit* propsList = rec.getValueAsListInit("IntrProperties"); +- string prop = +- propsList->size() +- ? propsList->getElementAsRecord(0)->getName() : ""; ++ const StringRef prop = ++ propsList->size() ? propsList->getElementAsRecord(0)->getName() : ""; + + bool readOnlyMem = prop == "IntrReadArgMem" || prop == "IntrReadMem"; + +@@ -127,8 +127,8 @@ void processRecord(raw_ostream& os, Record& rec, string arch) + else + return; + +- os << "pragma(LDC_intrinsic, \"" + name + "\")\n "; +- os << ret + " " + builtinName + "("; ++ os << "pragma(LDC_intrinsic, \"" << name << "\")\n "; ++ os << ret << " " << builtinName << "("; + + if(params.size()) + os << params[0]; +@@ -136,7 +136,7 @@ void processRecord(raw_ostream& os, Record& rec, string arch) + for(size_t i = 1; i < params.size(); i++) + os << ", " << params[i]; + +- os << ")" + attributes(propsList) + ";\n\n"; ++ os << ")" << attributes(propsList) << ";\n\n"; + } + + std::string arch; +-- +2.29.0 + + +From 93dc2a59cfa2c662a2cf39e4a8d0427fb3d9099c Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Sun, 23 Aug 2020 17:56:52 +0200 +Subject: [PATCH 04/16] Azure CI: Upgrade to LDC-LLVM v11.0.0-rc2 + +The AArch64 build for Shippable hasn't succeeded (failed to compile +MLIR). +--- + .azure-pipelines/android-llvm-config.in | 28 ++++++++++++------------- + .azure-pipelines/posix.yml | 9 ++++---- + .azure-pipelines/windows.yml | 3 ++- + azure-pipelines.yml | 2 +- + 4 files changed, 22 insertions(+), 20 deletions(-) + +diff --git a/.azure-pipelines/android-llvm-config.in b/.azure-pipelines/android-llvm-config.in +index 9d49ca08..d00ac475 100644 +--- a/.azure-pipelines/android-llvm-config.in ++++ b/.azure-pipelines/android-llvm-config.in +@@ -54,26 +54,26 @@ components="aarch64 aarch64asmparser aarch64codegen aarch64desc aarch64disassemb + aggressiveinstcombine all all-targets analysis arm armasmparser armcodegen armdesc armdisassembler arminfo \ + armutils asmparser asmprinter binaryformat bitreader bitstreamreader bitwriter cfguard codegen core coroutines \ + coverage debuginfocodeview debuginfodwarf debuginfogsym debuginfomsf debuginfopdb demangle dlltooldriver \ +-dwarflinker engine executionengine frontendopenmp fuzzmutate globalisel instcombine instrumentation interpreter \ ++dwarflinker engine executionengine extensions frontendopenmp fuzzmutate globalisel instcombine instrumentation interpreter \ + ipo irreader jitlink libdriver lineeditor linker lto mc mca mcdisassembler mcjit mcparser mirparser native \ + nativecodegen objcarcopts object objectyaml option orcerror orcjit passes profiledata remarks runtimedyld \ + scalaropts selectiondag support symbolize tablegen target textapi transformutils vectorize webassembly \ + webassemblyasmparser webassemblycodegen webassemblydesc webassemblydisassembler webassemblyinfo windowsmanifest \ +-x86 x86asmparser x86codegen x86desc x86disassembler x86info x86utils xray" ++x86 x86asmparser x86codegen x86desc x86disassembler x86info xray" + static_libs="-lLLVMXRay -lLLVMWindowsManifest -lLLVMTableGen -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMOrcJIT \ +--lLLVMOrcError -lLLVMJITLink -lLLVMObjectYAML -lLLVMMIRParser -lLLVMMCA -lLLVMLTO -lLLVMPasses -lLLVMObjCARCOpts \ +--lLLVMLineEditor -lLLVMLibDriver -lLLVMInterpreter -lLLVMFuzzMutate -lLLVMFrontendOpenMP -lLLVMMCJIT \ ++-lLLVMOrcError -lLLVMJITLink -lLLVMObjectYAML -lLLVMMIRParser -lLLVMMCA -lLLVMLTO -lLLVMPasses -lLLVMCoroutines \ ++-lLLVMObjCARCOpts -lLLVMipo -lLLVMInstrumentation -lLLVMVectorize -lLLVMLinker -lLLVMIRReader -lLLVMAsmParser \ ++-lLLVMFrontendOpenMP -lLLVMExtensions -lLLVMLineEditor -lLLVMLibDriver -lLLVMInterpreter -lLLVMFuzzMutate -lLLVMMCJIT \ + -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMDWARFLinker -lLLVMDlltoolDriver -lLLVMOption -lLLVMDebugInfoGSYM \ +--lLLVMCoverage -lLLVMCoroutines -lLLVMipo -lLLVMInstrumentation -lLLVMVectorize -lLLVMLinker -lLLVMIRReader \ +--lLLVMAsmParser -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Utils \ +--lLLVMX86Info -lLLVMWebAssemblyDisassembler -lLLVMWebAssemblyCodeGen -lLLVMWebAssemblyDesc \ +--lLLVMWebAssemblyAsmParser -lLLVMWebAssemblyInfo -lLLVMARMDisassembler -lLLVMARMCodeGen -lLLVMARMAsmParser \ +--lLLVMARMDesc -lLLVMARMUtils -lLLVMARMInfo -lLLVMAArch64Disassembler -lLLVMMCDisassembler -lLLVMAArch64CodeGen \ +--lLLVMCFGuard -lLLVMGlobalISel -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMDebugInfoDWARF -lLLVMCodeGen \ +--lLLVMTarget -lLLVMScalarOpts -lLLVMInstCombine -lLLVMAggressiveInstCombine -lLLVMTransformUtils -lLLVMBitWriter \ +--lLLVMAnalysis -lLLVMProfileData -lLLVMObject -lLLVMTextAPI -lLLVMBitReader -lLLVMCore -lLLVMRemarks \ +--lLLVMBitstreamReader -lLLVMAArch64AsmParser -lLLVMMCParser -lLLVMAArch64Desc -lLLVMMC -lLLVMDebugInfoCodeView \ +--lLLVMDebugInfoMSF -lLLVMBinaryFormat -lLLVMAArch64Utils -lLLVMAArch64Info -lLLVMSupport -lLLVMDemangle" ++-lLLVMCoverage -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info \ ++-lLLVMWebAssemblyDisassembler -lLLVMWebAssemblyCodeGen -lLLVMWebAssemblyDesc -lLLVMWebAssemblyAsmParser \ ++-lLLVMWebAssemblyInfo -lLLVMARMDisassembler -lLLVMARMCodeGen -lLLVMARMAsmParser -lLLVMARMDesc -lLLVMARMUtils \ ++-lLLVMARMInfo -lLLVMAArch64Disassembler -lLLVMMCDisassembler -lLLVMAArch64CodeGen -lLLVMCFGuard -lLLVMGlobalISel \ ++-lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMDebugInfoDWARF -lLLVMCodeGen -lLLVMTarget -lLLVMScalarOpts \ ++-lLLVMInstCombine -lLLVMAggressiveInstCombine -lLLVMTransformUtils -lLLVMBitWriter -lLLVMAnalysis -lLLVMProfileData \ ++-lLLVMObject -lLLVMTextAPI -lLLVMBitReader -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMAArch64AsmParser \ ++-lLLVMMCParser -lLLVMAArch64Desc -lLLVMMC -lLLVMDebugInfoCodeView -lLLVMDebugInfoMSF -lLLVMBinaryFormat \ ++-lLLVMAArch64Utils -lLLVMAArch64Info -lLLVMSupport -lLLVMDemangle" + shared_libs="-lLLVM-$version" + libs=$shared_libs + handle_args () { +diff --git a/.azure-pipelines/posix.yml b/.azure-pipelines/posix.yml +index e12191a6..15c2a559 100644 +--- a/.azure-pipelines/posix.yml ++++ b/.azure-pipelines/posix.yml +@@ -47,7 +47,7 @@ steps: + if [ "${BUILD_SOURCEBRANCH:0:10}" != "refs/tags/" ]; then + assertsSuffix="-withAsserts" + fi +- curl -L -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$LLVM_VERSION/llvm-$LLVM_VERSION-$HOST_OS-x86_64$assertsSuffix.tar.xz ++ curl -L -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/CI/llvm-$LLVM_VERSION-$HOST_OS-x86_64$assertsSuffix.tar.xz + mkdir llvm + tar -xf llvm.tar.xz --strip 1 -C llvm + # Set PARALLEL_JOBS env variable and persist it for future steps +@@ -110,7 +110,7 @@ steps: + set -ex + cd .. + # Download & extract LDC-flavoured LLVM for Android target +- curl -L -o llvm-$ARCH.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$LLVM_VERSION/llvm-$LLVM_VERSION-$CI_OS-$ARCH.tar.xz ++ curl -L -o llvm-$ARCH.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/CI/llvm-$LLVM_VERSION-$CI_OS-$ARCH.tar.xz + mkdir llvm-$ARCH + tar -xf llvm-$ARCH.tar.xz --strip 1 -C llvm-$ARCH + # Download & extract Android NDK +@@ -130,7 +130,7 @@ steps: + if [ "$ARCH" = "armv7a" ]; then androidEnv="androideabi"; fi + LLVM_TRIPLE=$ARCH--linux-$androidEnv + sed $BUILD_SOURCESDIRECTORY/.azure-pipelines/android-llvm-config.in \ +- -e "s|@LLVM_VERSION@|$LLVM_VERSION|g" \ ++ -e "s|@LLVM_VERSION@|11.0.0|g" \ + -e "s|@LLVM_INSTALL_DIR@|$PWD/llvm-$ARCH|g" \ + -e "s|@LLVM_DEFAULT_TARGET_TRIPLE@|$LLVM_TRIPLE|g" \ + -e "s|@LLVM_TARGETS@|AArch64 ARM X86 WebAssembly|g" > llvm-$ARCH/bin/llvm-config +@@ -378,7 +378,8 @@ steps: + cd .. + installed/bin/ldc2 -enable-dynamic-compile -run $BUILD_SOURCESDIRECTORY/tests/dynamiccompile/array.d + displayName: Run dynamic-compile integration test +- condition: and(succeeded(), ne(variables['CI_OS'], 'android')) ++ #condition: and(succeeded(), ne(variables['CI_OS'], 'android')) ++ condition: not(always()) + + # Add dub & dlang tools + - script: | +diff --git a/.azure-pipelines/windows.yml b/.azure-pipelines/windows.yml +index 47ecf985..c2fc43da 100644 +--- a/.azure-pipelines/windows.yml ++++ b/.azure-pipelines/windows.yml +@@ -56,7 +56,7 @@ steps: + :: Download & extract LDC-flavoured LLVM + set ASSERTS_SUFFIX= + if not "%BUILD_SOURCEBRANCH:~0,10%" == "refs/tags/" ( set ASSERTS_SUFFIX=-withAsserts) +- curl -L -o llvm.7z https://github.com/ldc-developers/llvm-project/releases/download/ldc-v%LLVM_VERSION%/llvm-%LLVM_VERSION%-windows-%ARCH%%ASSERTS_SUFFIX%.7z 2>&1 ++ curl -L -o llvm.7z https://github.com/ldc-developers/llvm-project/releases/download/CI/llvm-%LLVM_VERSION%-windows-%ARCH%%ASSERTS_SUFFIX%.7z 2>&1 + mkdir llvm + cd llvm + 7z x ../llvm.7z > nul +@@ -172,6 +172,7 @@ steps: + call "%VSINSTALLDIR%Common7\Tools\VsDevCmd.bat" -arch=%ARCH% + installed\bin\ldc2 -enable-dynamic-compile -run %BUILD_SOURCESDIRECTORY%/tests/dynamiccompile/array.d + displayName: Run dynamic-compile integration test ++ condition: not(always()) + - script: | + cd .. + call "%VSINSTALLDIR%Common7\Tools\VsDevCmd.bat" -arch=%ARCH% +diff --git a/azure-pipelines.yml b/azure-pipelines.yml +index d4807498..ac415714 100644 +--- a/azure-pipelines.yml ++++ b/azure-pipelines.yml +@@ -1,5 +1,5 @@ + variables: +- LLVM_VERSION: 10.0.1 ++ LLVM_VERSION: b3db88a4 + CLANG_VERSION: 10.0.1 + HOST_LDC_VERSION: 1.22.0 + +-- +2.29.0 + + +From 7544a974754efcbe2b4ed1a5ee8aaa4d6d88a201 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Mon, 24 Aug 2020 20:47:54 +0200 +Subject: [PATCH 05/16] Fix LLVM < 8 compatibility (no llvm::CallBase) + +--- + gen/funcgenstate.cpp | 8 +++--- + gen/funcgenstate.h | 8 +++--- + gen/llvm.h | 21 +++++++++++++++ + gen/passes/GarbageCollect2Stack.cpp | 42 ++++++++++++++++++----------- + gen/tocall.cpp | 7 ++--- + 5 files changed, 60 insertions(+), 26 deletions(-) + +diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp +index ae0504e7..fbdb9a34 100644 +--- a/gen/funcgenstate.cpp ++++ b/gen/funcgenstate.cpp +@@ -103,10 +103,10 @@ FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) + : irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(), + irs(irs) {} + +-llvm::CallBase *FuncGenState::callOrInvoke(llvm::Value *callee, +- llvm::FunctionType *calleeType, +- llvm::ArrayRef args, +- const char *name, bool isNothrow) { ++LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, ++ llvm::FunctionType *calleeType, ++ llvm::ArrayRef args, ++ const char *name, bool isNothrow) { + // If this is a direct call, we might be able to use the callee attributes + // to our advantage. + llvm::Function *calleeFn = llvm::dyn_cast(callee); +diff --git a/gen/funcgenstate.h b/gen/funcgenstate.h +index bd1edc0d..af719c74 100644 +--- a/gen/funcgenstate.h ++++ b/gen/funcgenstate.h +@@ -199,10 +199,10 @@ public: + + /// Emits a call or invoke to the given callee, depending on whether there + /// are catches/cleanups active or not. +- llvm::CallBase *callOrInvoke(llvm::Value *callee, +- llvm::FunctionType *calleeType, +- llvm::ArrayRef args, +- const char *name = "", bool isNothrow = false); ++ LLCallBasePtr callOrInvoke(llvm::Value *callee, ++ llvm::FunctionType *calleeType, ++ llvm::ArrayRef args, ++ const char *name = "", bool isNothrow = false); + + private: + IRState &irs; +diff --git a/gen/llvm.h b/gen/llvm.h +index a4870c80..bed932f8 100644 +--- a/gen/llvm.h ++++ b/gen/llvm.h +@@ -30,6 +30,9 @@ + #include "llvm/IR/DataLayout.h" + #include "llvm/IR/IRBuilder.h" + #include "llvm/IR/DebugInfo.h" ++#if LDC_LLVM_VER < 800 ++#include "llvm/IR/CallSite.h" ++#endif + + #if LDC_LLVM_VER >= 1000 + // LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back +@@ -81,3 +84,21 @@ using llvm::IRBuilder; + #define LLConstantFP llvm::ConstantFP + + #define LLSmallVector llvm::SmallVector ++ ++#if LDC_LLVM_VER >= 800 ++using LLCallBasePtr = llvm::CallBase *; ++#else ++class LLCallBasePtr { ++ llvm::CallSite CS; ++ ++public: ++ LLCallBasePtr(llvm::CallInst *CI) : CS(CI) {} ++ LLCallBasePtr(llvm::InvokeInst *II) : CS(II) {} ++ explicit LLCallBasePtr(llvm::Instruction *I) : CS(I) {} ++ ++ llvm::CallSite *operator->() { return &CS; } ++ ++ operator llvm::CallSite &() { return CS; } ++ operator llvm::Instruction *() { return CS.getInstruction(); } ++}; ++#endif +diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp +index 9463b190..fc914762 100644 +--- a/gen/passes/GarbageCollect2Stack.cpp ++++ b/gen/passes/GarbageCollect2Stack.cpp +@@ -114,11 +114,11 @@ public: + + // Analyze the current call, filling in some fields. Returns true if + // this is an allocation we can stack-allocate. +- virtual bool analyze(CallBase *CB, const Analysis &A) = 0; ++ virtual bool analyze(LLCallBasePtr CB, const Analysis &A) = 0; + + // Returns the alloca to replace this call. + // It will always be inserted before the call. +- virtual Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) { ++ virtual Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const Analysis &A) { + NumGcToStack++; + + auto &BB = CB->getCaller()->getEntryBlock(); +@@ -166,7 +166,7 @@ public: + TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr) + : FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {} + +- bool analyze(CallBase *CB, const Analysis &A) override { ++ bool analyze(LLCallBasePtr CB, const Analysis &A) override { + Value *TypeInfo = CB->getArgOperand(TypeInfoArgNr); + Ty = A.getTypeFor(TypeInfo); + if (!Ty) { +@@ -187,7 +187,7 @@ public: + : TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr), + Initialized(initialized) {} + +- bool analyze(CallBase *CB, const Analysis &A) override { ++ bool analyze(LLCallBasePtr CB, const Analysis &A) override { + if (!TypeInfoFI::analyze(CB, A)) { + return false; + } +@@ -216,7 +216,7 @@ public: + return true; + } + +- Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) override { ++ Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const Analysis &A) override { + IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint()); + + // If the allocation is of constant size it's best to put it in the +@@ -265,7 +265,7 @@ public: + // FunctionInfo for _d_allocclass + class AllocClassFI : public FunctionInfo { + public: +- bool analyze(CallBase *CB, const Analysis &A) override { ++ bool analyze(LLCallBasePtr CB, const Analysis &A) override { + if (CB->arg_size() != 1) { + return false; + } +@@ -322,7 +322,7 @@ class UntypedMemoryFI : public FunctionInfo { + Value *SizeArg; + + public: +- bool analyze(CallBase *CB, const Analysis &A) override { ++ bool analyze(LLCallBasePtr CB, const Analysis &A) override { + if (CB->arg_size() < SizeArgNr + 1) { + return false; + } +@@ -345,7 +345,7 @@ public: + return true; + } + +- Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) override { ++ Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const Analysis &A) override { + IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint()); + + // If the allocation is of constant size it's best to put it in the +@@ -431,11 +431,11 @@ GarbageCollect2Stack::GarbageCollect2Stack() + KnownFunctions["_d_allocmemory"] = &AllocMemory; + } + +-static void RemoveCall(CallBase *CB, const Analysis &A) { ++static void RemoveCall(LLCallBasePtr CB, const Analysis &A) { + // For an invoke instruction, we insert a branch to the normal target BB + // immediately before it. Ideally, we would find a way to not invalidate + // the dominator tree here. +- if (auto Invoke = dyn_cast(CB)) { ++ if (auto Invoke = dyn_cast(static_cast(CB))) { + BranchInst::Create(Invoke->getNormalDest(), Invoke); + Invoke->getUnwindDest()->removePredecessor(CB->getParent()); + } +@@ -448,7 +448,7 @@ static void RemoveCall(CallBase *CB, const Analysis &A) { + A.CGNode->removeCallEdgeFor(CB); + #endif + } +- CB->eraseFromParent(); ++ static_cast(CB)->eraseFromParent(); + } + + static bool +@@ -481,10 +481,18 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + auto originalI = I; + + // Ignore non-calls. +- auto CB = dyn_cast(&(*(I++))); ++ Instruction *Inst = &(*(I++)); ++#if LDC_LLVM_VER >= 800 ++ auto CB = dyn_cast(Inst); + if (!CB) { + continue; + } ++#else ++ LLCallBasePtr CB(Inst); ++ if (!CB->getInstruction()) { ++ continue; ++ } ++#endif + + // Ignore indirect calls and calls to non-external functions. + Function *Callee = CB->getCalledFunction(); +@@ -501,7 +509,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + + FunctionInfo *info = OMI->getValue(); + +- if (CB->use_empty()) { ++ if (static_cast(CB)->use_empty()) { + Changed = true; + NumDeleted++; + RemoveCall(CB, A); +@@ -544,7 +552,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { + if (newVal->getType() != CB->getType()) { + newVal = Builder.CreateBitCast(newVal, CB->getType()); + } +- CB->replaceAllUsesWith(newVal); ++ static_cast(CB)->replaceAllUsesWith(newVal); + + RemoveCall(CB, A); + } +@@ -816,7 +824,11 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V, + switch (I->getOpcode()) { + case Instruction::Call: + case Instruction::Invoke: { ++#if LDC_LLVM_VER >= 800 + auto CB = llvm::cast(I); ++#else ++ LLCallBasePtr CB(I); ++#endif + // Not captured if the callee is readonly, doesn't return a copy through + // its return value and doesn't unwind (a readonly function can leak bits + // by throwing an exception or not depending on the input value). +@@ -840,7 +852,7 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V, + return false; + } + +- if (auto call = dyn_cast(CB)) { ++ if (auto call = dyn_cast(static_cast(CB))) { + if (call->isTailCall()) { + RemoveTailCallInsts.push_back(call); + } +diff --git a/gen/tocall.cpp b/gen/tocall.cpp +index b217ffd3..ff4e7359 100644 +--- a/gen/tocall.cpp ++++ b/gen/tocall.cpp +@@ -872,8 +872,8 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, + } + + // call the function +- llvm::CallBase *call = gIR->funcGen().callOrInvoke(callable, callableTy, args, +- "", tf->isnothrow); ++ LLCallBasePtr call = gIR->funcGen().callOrInvoke(callable, callableTy, args, ++ "", tf->isnothrow); + + // PGO: Insert instrumentation or attach profile metadata at indirect call + // sites. +@@ -886,7 +886,8 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, + const int sretArgIndex = + (irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(tf) ? 1 + : 0); +- LLValue *retllval = (irFty.arg_sret ? args[sretArgIndex] : call); ++ LLValue *retllval = irFty.arg_sret ? args[sretArgIndex] ++ : static_cast(call); + bool retValIsLVal = + (tf->isref && returnTy != Tvoid) || (irFty.arg_sret != nullptr); + +-- +2.29.0 + + +From cacdc461543ab2cbebc4dad0dee5292704364f38 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Tue, 25 Aug 2020 01:41:24 +0200 +Subject: [PATCH 06/16] Use LLVM 10+ setFunctionAttributes() + +Which supports more cmdline options in the meantime than what we have +been supporting manually, and should be future-proof, similar to +InitTargetOptionsFromCodeGenFlags(). +Attributes are only set if explicitly specified in the cmdline (and not +already present in the function's existing attributes). + +This started out as a workaround for not being able to determine whether +the user has explicitly set -frame-pointer in the cmdline with LLVM 11, +and ended with having to touch more than I wanted. An *enabled* +-ffast-math flag (from us, not LLVM) overriding LLVM's +-enable-unsafe-fp-math, but e.g. -ffast-math=false NOT overriding was/is +one of the quirks. +--- + driver/cl_options-llvm.cpp | 13 +++++++++++++ + driver/cl_options-llvm.h | 8 ++++++++ + gen/functions.cpp | 31 +++++++++++++++++++----------- + gen/optimizer.cpp | 4 ++-- + gen/optimizer.h | 4 ++-- + tests/codegen/attr_targetoptions.d | 4 ---- + tests/codegen/inlineIR_math.d | 10 ++++++---- + 7 files changed, 51 insertions(+), 23 deletions(-) + +diff --git a/driver/cl_options-llvm.cpp b/driver/cl_options-llvm.cpp +index 5071fd11..0910ed1b 100644 +--- a/driver/cl_options-llvm.cpp ++++ b/driver/cl_options-llvm.cpp +@@ -63,6 +63,8 @@ Optional getCodeModel() { + #if LDC_LLVM_VER >= 800 + llvm::Optional framePointerUsage() { + #if LDC_LLVM_VER >= 1100 ++ // Defaults to `FP::None`; no way to check if set explicitly by user except ++ // indirectly via setFunctionAttributes()... + return codegen::getFramePointerUsage(); + #else + if (::FramePointerUsage.getNumOccurrences() > 0) +@@ -116,6 +118,17 @@ std::string getFeaturesStr() { + return ::getFeaturesStr(); + #endif + } ++ ++#if LDC_LLVM_VER >= 1000 ++void setFunctionAttributes(StringRef cpu, StringRef features, ++ Function &function) { ++#if LDC_LLVM_VER >= 1100 ++ return codegen::setFunctionAttributes(cpu, features, function); ++#else ++ return ::setFunctionAttributes(cpu, features, function); ++#endif ++} ++#endif + } // namespace opts + + #if LDC_WITH_LLD +diff --git a/driver/cl_options-llvm.h b/driver/cl_options-llvm.h +index dda90542..d43bc584 100644 +--- a/driver/cl_options-llvm.h ++++ b/driver/cl_options-llvm.h +@@ -14,6 +14,10 @@ + #include "llvm/Support/CodeGen.h" + #include "llvm/Target/TargetOptions.h" + ++namespace llvm { ++class Function; ++} ++ + namespace opts { + + std::string getArchStr(); +@@ -31,4 +35,8 @@ bool printTargetFeaturesHelp(); + llvm::TargetOptions InitTargetOptionsFromCodeGenFlags(); + std::string getCPUStr(); + std::string getFeaturesStr(); ++#if LDC_LLVM_VER >= 1000 ++void setFunctionAttributes(llvm::StringRef cpu, llvm::StringRef features, ++ llvm::Function &function); ++#endif + } +diff --git a/gen/functions.cpp b/gen/functions.cpp +index 46c10c46..84f05e6b 100644 +--- a/gen/functions.cpp ++++ b/gen/functions.cpp +@@ -454,20 +454,28 @@ void applyParamAttrsToLLFunc(TypeFunction *f, IrFuncTy &irFty, + /// does the same). See https://llvm.org/bugs/show_bug.cgi?id=23172 + void applyTargetMachineAttributes(llvm::Function &func, + const llvm::TargetMachine &target) { +- const llvm::TargetOptions &TO = target.Options; ++ const auto dcompute = gIR->dcomputetarget; + +- // TODO: implement commandline switches to change the default values. + // TODO: (correctly) apply these for NVPTX (but not for SPIRV). +- if (gIR->dcomputetarget && gIR->dcomputetarget->target == DComputeTarget::OpenCL) ++ if (dcompute && dcompute->target == DComputeTarget::OpenCL) + return; +- if (!gIR->dcomputetarget) { +- // Target CPU capabilities +- func.addFnAttr("target-cpu", target.getTargetCPU()); +- auto featStr = target.getTargetFeatureString(); +- if (!featStr.empty()) +- func.addFnAttr("target-features", featStr); +- } ++ const auto cpu = dcompute ? "" : target.getTargetCPU(); ++ const auto features = dcompute ? "" : target.getTargetFeatureString(); ++ ++#if LDC_LLVM_VER >= 1000 ++ opts::setFunctionAttributes(cpu, features, func); ++ if (opts::fFastMath) // -ffast-math[=true] overrides -enable-unsafe-fp-math ++ func.addFnAttr("unsafe-fp-math", "true"); ++ if (!func.hasFnAttribute("frame-pointer")) // not explicitly set by user ++ func.addFnAttr("frame-pointer", isOptimizationEnabled() ? "none" : "all"); ++#else ++ if (!cpu.empty()) ++ func.addFnAttr("target-cpu", cpu); ++ if (!features.empty()) ++ func.addFnAttr("target-features", features); ++ + // Floating point settings ++ const auto &TO = target.Options; + func.addFnAttr("unsafe-fp-math", TO.UnsafeFPMath ? "true" : "false"); + // This option was removed from llvm::TargetOptions in LLVM 5.0. + // Clang sets this to true when `-cl-mad-enable` is passed (OpenCL only). +@@ -490,10 +498,11 @@ void applyTargetMachineAttributes(llvm::Function &func, + func.addFnAttr("frame-pointer", "all"); + break; + } +-#else ++#else // LDC_LLVM_VER < 800 + func.addFnAttr("no-frame-pointer-elim", + willEliminateFramePointer() ? "false" : "true"); + #endif ++#endif // LDC_LLVM_VER < 1000 + } + + void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { +diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp +index 5832251b..c4faeb16 100644 +--- a/gen/optimizer.cpp ++++ b/gen/optimizer.cpp +@@ -131,14 +131,14 @@ bool willCrossModuleInline() { + return enableCrossModuleInlining == llvm::cl::BOU_TRUE; + } + +-#if LDC_LLVM_VER >= 800 ++#if LDC_LLVM_VER >= 800 && LDC_LLVM_VER < 1000 + llvm::FramePointer::FP whichFramePointersToEmit() { + if (auto option = opts::framePointerUsage()) + return *option; + return isOptimizationEnabled() ? llvm::FramePointer::None + : llvm::FramePointer::All; + } +-#else ++#elif LDC_LLVM_VER < 800 + bool willEliminateFramePointer() { + const llvm::cl::boolOrDefault disableFPElimEnum = opts::disableFPElim(); + return disableFPElimEnum == llvm::cl::BOU_FALSE || +diff --git a/gen/optimizer.h b/gen/optimizer.h +index 0a5583bb..eb08f268 100644 +--- a/gen/optimizer.h ++++ b/gen/optimizer.h +@@ -34,9 +34,9 @@ bool willInline(); + + bool willCrossModuleInline(); + +-#if LDC_LLVM_VER >= 800 ++#if LDC_LLVM_VER >= 800 && LDC_LLVM_VER < 1000 + llvm::FramePointer::FP whichFramePointersToEmit(); +-#else ++#elif LDC_LLVM_VER < 800 + bool willEliminateFramePointer(); + #endif + +diff --git a/tests/codegen/attr_targetoptions.d b/tests/codegen/attr_targetoptions.d +index eb8ec92d..f70fd24b 100644 +--- a/tests/codegen/attr_targetoptions.d ++++ b/tests/codegen/attr_targetoptions.d +@@ -17,10 +17,6 @@ void foo() + + // COMMON: attributes #[[KEYVALUE]] + // COMMON-DAG: "target-cpu"= +-// COMMON-DAG: "unsafe-fp-math"="false" +-// COMMON-DAG: "less-precise-fpmad"="false" +-// COMMON-DAG: "no-infs-fp-math"="false" +-// COMMON-DAG: "no-nans-fp-math"="false" + + // WITH_FP-DAG: "frame-pointer"="all" + // NO_FP-DAG: "frame-pointer"="none" +diff --git a/tests/codegen/inlineIR_math.d b/tests/codegen/inlineIR_math.d +index 000ce927..03c534e1 100644 +--- a/tests/codegen/inlineIR_math.d ++++ b/tests/codegen/inlineIR_math.d +@@ -91,7 +91,7 @@ extern (C) double aliasInlineUnsafe(double[] a, double[] b) + } + + // LLVM-LABEL: define{{.*}} @aliasInlineSafe +-// LLVM-SAME: #[[UNSAFEFPMATH2:[0-9]+]] ++// LLVM-SAME: #[[NO_UNSAFEFPMATH:[0-9]+]] + // ASM-LABEL: aliasInlineSafe: + extern (C) double aliasInlineSafe(double[] a, double[] b) + { +@@ -113,6 +113,8 @@ double neverInlinedEnclosingFunction() + return muladd(1.0, 2.0, 3.0); + } + +-// LLVM-DAG: attributes #[[UNSAFEFPMATH]] ={{.*}} "unsafe-fp-math"="true" +-// LLVM-DAG: attributes #[[UNSAFEFPMATH2]] ={{.*}} "unsafe-fp-math"="false" +-// LLVM-DAG: attributes #[[FEAT]] ={{.*}} "target-features"="{{.*}}+fma{{.*}}" ++// LLVM: attributes #[[UNSAFEFPMATH]] ={{.*}} "unsafe-fp-math"="true" ++// LLVM: attributes #[[FEAT]] ={{.*}} "target-features"="{{.*}}+fma{{.*}}" ++ ++// LLVM: attributes #[[NO_UNSAFEFPMATH]] = ++// LLVM-NOT: "unsafe-fp-math"="true" +-- +2.29.0 + + +From 2fb31f84a98f3fabbcfb5dae651b08d2f5538b5f Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Tue, 25 Aug 2020 19:24:42 +0200 +Subject: [PATCH 07/16] Adapt lit-test plugins/addFuncEntryCall for LLVM 11 + +--- + tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +index f24a3c3e..69befefd 100644 +--- a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp ++++ b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +@@ -50,7 +50,11 @@ bool FuncEntryCallPass::runOnFunction(Function &F) { + // (this includes e.g. `ldc.register_dso`!) + llvm::BasicBlock &block = F.getEntryBlock(); + IRBuilder<> builder(&block, block.begin()); ++#if LLVM_VERSION >= 1100 ++ builder.CreateCall(FunctionCallee(cast(funcToCallUponEntry))); ++#else + builder.CreateCall(funcToCallUponEntry); ++#endif + return true; + } + +-- +2.29.0 + + +From 1d969cfccaf7181b5760a0ae7cd6088a36e3de44 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Tue, 25 Aug 2020 19:25:19 +0200 +Subject: [PATCH 08/16] Revise IRScope refactoring + +LLVM already provides suited RAII helper types to restore the IRBuilder +state. [They sadly aren't movable, so I've had to wrap them in a +unique_ptr.] + +While at it, also minimally revise debuginfo generation for functions. +--- + gen/dibuilder.cpp | 29 +++++----------- + gen/dibuilder.h | 13 ++----- + gen/functions.cpp | 14 +++----- + gen/irstate.cpp | 54 +++++------------------------ + gen/irstate.h | 41 +++++++++------------- + gen/passes/GarbageCollect2Stack.cpp | 30 ++++++++-------- + gen/statements.cpp | 4 +-- + gen/toir.cpp | 4 +-- + gen/trycatchfinally.cpp | 14 ++------ + ir/irclass.cpp | 6 +--- + 10 files changed, 59 insertions(+), 150 deletions(-) + +diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp +index a5fdfceb..137c79a5 100644 +--- a/gen/dibuilder.cpp ++++ b/gen/dibuilder.cpp +@@ -207,7 +207,7 @@ void DIBuilder::SetValue(const Loc &loc, llvm::Value *value, + IR->scopebb()); + } + +-DIFile DIBuilder::CreateFile(Loc &loc) { ++DIFile DIBuilder::CreateFile(const Loc &loc) { + const char *filename = loc.filename; + if (!filename) + filename = IR->dmodule->srcfile.toChars(); +@@ -1149,26 +1149,15 @@ void DIBuilder::EmitFuncStart(FuncDeclaration *fd) { + Logger::println("D to dwarf funcstart"); + LOG_SCOPE; + +- assert(static_cast(getIrFunc(fd)->diSubprogram) != 0); +- EmitStopPoint(fd->loc); +-} +- +-void DIBuilder::EmitFuncEnd(FuncDeclaration *fd) { +- if (!mustEmitLocationsDebugInfo()) +- return; +- +- Logger::println("D to dwarf funcend"); +- LOG_SCOPE; +- + auto irFunc = getIrFunc(fd); +- +- assert(static_cast(irFunc->diSubprogram) != 0); +- EmitStopPoint(fd->endloc); +- ++ assert(irFunc->diSubprogram); + irFunc->getLLVMFunc()->setSubprogram(irFunc->diSubprogram); ++ ++ IR->ir->SetCurrentDebugLocation({}); // clear first ++ EmitStopPoint(fd->loc); + } + +-void DIBuilder::EmitBlockStart(Loc &loc) { ++void DIBuilder::EmitBlockStart(const Loc &loc) { + if (!mustEmitLocationsDebugInfo()) + return; + +@@ -1193,7 +1182,7 @@ void DIBuilder::EmitBlockEnd() { + fn->diLexicalBlocks.pop(); + } + +-void DIBuilder::EmitStopPoint(Loc &loc) { ++void DIBuilder::EmitStopPoint(const Loc &loc) { + if (!mustEmitLocationsDebugInfo()) + return; + +@@ -1202,6 +1191,7 @@ void DIBuilder::EmitStopPoint(Loc &loc) { + // cannot do this in all cases). + if (!loc.linnum && IR->ir->getCurrentDebugLocation()) + return; ++ + unsigned linnum = loc.linnum; + // without proper loc use the line of the enclosing symbol that has line + // number debug info +@@ -1215,11 +1205,8 @@ void DIBuilder::EmitStopPoint(Loc &loc) { + LOG_SCOPE; + IR->ir->SetCurrentDebugLocation( + llvm::DebugLoc::get(linnum, col, GetCurrentScope())); +- currentLoc = loc; + } + +-Loc DIBuilder::GetCurrentLoc() const { return currentLoc; } +- + void DIBuilder::EmitValue(llvm::Value *val, VarDeclaration *vd) { + auto sub = IR->func()->variableMap.find(vd); + if (sub == IR->func()->variableMap.end()) +diff --git a/gen/dibuilder.h b/gen/dibuilder.h +index fb535235..2ea095b5 100644 +--- a/gen/dibuilder.h ++++ b/gen/dibuilder.h +@@ -72,8 +72,6 @@ class DIBuilder { + return CUNode; + } + +- Loc currentLoc; +- + public: + explicit DIBuilder(IRState *const IR); + +@@ -114,18 +112,13 @@ public: + /// \brief Emits debug info for function start + void EmitFuncStart(FuncDeclaration *fd); + +- /// \brief Emits debug info for function end +- void EmitFuncEnd(FuncDeclaration *fd); +- + /// \brief Emits debug info for block start +- void EmitBlockStart(Loc &loc); ++ void EmitBlockStart(const Loc &loc); + + /// \brief Emits debug info for block end + void EmitBlockEnd(); + +- Loc GetCurrentLoc() const; +- +- void EmitStopPoint(Loc &loc); ++ void EmitStopPoint(const Loc &loc); + + void EmitValue(llvm::Value *val, VarDeclaration *vd); + +@@ -171,7 +164,7 @@ private: + llvm::SmallVector &elems); + void AddStaticMembers(AggregateDeclaration *sd, ldc::DIFile file, + llvm::SmallVector &elems); +- DIFile CreateFile(Loc &loc); ++ DIFile CreateFile(const Loc &loc); + DIFile CreateFile(); + DIFile CreateFile(Dsymbol* decl); + DIType CreateBasicType(Type *type); +diff --git a/gen/functions.cpp b/gen/functions.cpp +index 84f05e6b..1af45d08 100644 +--- a/gen/functions.cpp ++++ b/gen/functions.cpp +@@ -892,12 +892,11 @@ void emitDMDStyleFunctionTrace(IRState &irs, FuncDeclaration *fd, + // Push cleanup block that calls _c_trace_epi at function exit. + { + auto traceEpilogBB = irs.insertBB("trace_epi"); +- const auto savedInsertPoint = irs.getInsertPoint(); ++ const auto savedInsertPoint = irs.saveInsertPoint(); + irs.ir->SetInsertPoint(traceEpilogBB); + irs.ir->CreateCall( + getRuntimeFunction(fd->endloc, irs.module, "_c_trace_epi")); + funcGen.scopes.pushCleanup(traceEpilogBB, irs.scopebb()); +- irs.setInsertPoint(savedInsertPoint); + } + } + +@@ -1175,9 +1174,9 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + llvm::BasicBlock::Create(gIR->context(), "", func); + + // set up the IRBuilder scope for the function +- FunctionIRBuilderScope irBuilderScope(*gIR); +- gIR->setInsertPoint(beginbb); ++ const auto savedIRBuilderScope = gIR->setInsertPoint(beginbb); + gIR->ir->setFastMathFlags(irFunc->FMF); ++ gIR->DBuilder.EmitFuncStart(fd); + + // @naked: emit body and return, no prologue/epilogue + if (func->hasFnAttribute(llvm::Attribute::Naked)) { +@@ -1200,9 +1199,6 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + "alloca_point", beginbb); + funcGen.allocapoint = allocaPoint; + +- // debug info - after all allocas, but before any llvm.dbg.declare etc +- gIR->DBuilder.EmitFuncStart(fd); +- + emitInstrumentationFnEnter(fd); + + if (global.params.trace && fd->emitInstrumentation && !fd->isCMain() && +@@ -1283,11 +1279,10 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + { + auto *vaendBB = + llvm::BasicBlock::Create(gIR->context(), "vaend", gIR->topfunc()); +- const auto savedInsertPoint = gIR->getInsertPoint(); ++ const auto savedInsertPoint = gIR->saveInsertPoint(); + gIR->ir->SetInsertPoint(vaendBB); + gIR->ir->CreateCall(GET_INTRINSIC_DECL(vaend), llAp); + funcGen.scopes.pushCleanup(vaendBB, gIR->scopebb()); +- gIR->setInsertPoint(savedInsertPoint); + } + } + +@@ -1328,7 +1323,6 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { + gIR->ir->CreateRet(llvm::UndefValue::get(func->getReturnType())); + } + } +- gIR->DBuilder.EmitFuncEnd(fd); + + // erase alloca point + if (allocaPoint->getParent()) { +diff --git a/gen/irstate.cpp b/gen/irstate.cpp +index eb73ab41..54990b15 100644 +--- a/gen/irstate.cpp ++++ b/gen/irstate.cpp +@@ -46,36 +46,15 @@ llvm::Function *IRState::topfunc() { return func()->getLLVMFunc(); } + + llvm::Instruction *IRState::topallocapoint() { return funcGen().allocapoint; } + +-InsertionPoint IRState::getInsertPoint() { +- auto bb = builder.GetInsertBlock(); +- if (!bb) +- return {nullptr, llvm::None}; +- +- llvm::Optional point = builder.GetInsertPoint(); +- if (point == bb->end()) +- point = llvm::None; +- +- return {bb, point}; +-} +- +-void IRState::setInsertPoint(llvm::BasicBlock *insertAtEnd) { +- builder.SetInsertPoint(insertAtEnd); +-} +- +-void IRState::setInsertPoint(InsertionPoint point) { +- if (!point.bb) { +- builder.ClearInsertionPoint(); +- } else if (!point.point.hasValue()) { +- builder.SetInsertPoint(point.bb); +- } else { +- builder.SetInsertPoint(point.bb, point.point.getValue()); +- } ++std::unique_ptr IRState::setInsertPoint(llvm::BasicBlock *bb) { ++ auto savedScope = llvm::make_unique(builder); ++ builder.SetInsertPoint(bb); ++ return savedScope; + } + +-llvm::BasicBlock *IRState::scopebb() { +- auto bb = ir->GetInsertBlock(); +- assert(bb); +- return bb; ++std::unique_ptr ++IRState::saveInsertPoint() { ++ return llvm::make_unique(builder); + } + + bool IRState::scopereturned() { +@@ -300,26 +279,9 @@ const Loc &IRState::getInlineAsmSrcLoc(unsigned srcLocCookie) const { + + //////////////////////////////////////////////////////////////////////////////// + +-FunctionIRBuilderScope::FunctionIRBuilderScope(IRState &state) +- : state(state), previousInsertionPoint(state.getInsertPoint()), +- previousFMF(state.builder.getFastMathFlags()), +- previousDebugLoc(state.builder.getCurrentDebugLocation()) { +- state.builder.ClearInsertionPoint(); +- state.builder.clearFastMathFlags(); +- state.builder.SetCurrentDebugLocation({}); +-} +- +-FunctionIRBuilderScope::~FunctionIRBuilderScope() { +- state.setInsertPoint(previousInsertionPoint); +- state.builder.setFastMathFlags(previousFMF); +- state.builder.SetCurrentDebugLocation(previousDebugLoc); +-} +- +-//////////////////////////////////////////////////////////////////////////////// +- + IRBuilder<> *IRBuilderHelper::operator->() { + IRBuilder<> &b = state->builder; +- assert(b.GetInsertBlock() != NULL); ++ assert(b.GetInsertBlock()); + return &b; + } + +diff --git a/gen/irstate.h b/gen/irstate.h +index 83a33310..37b9a119 100644 +--- a/gen/irstate.h ++++ b/gen/irstate.h +@@ -58,28 +58,15 @@ class StructLiteralExp; + struct IrFunction; + struct IrModule; + +-// insertion point for IRBuilder +-struct InsertionPoint { +- llvm::BasicBlock *bb; // can be null +- llvm::Optional point; // bb end if not set +- +- InsertionPoint(llvm::BasicBlock *bb, +- llvm::Optional point) +- : bb(bb), point(std::move(point)) {} +-}; +- +-// Resets the IRBuilder for a new function and restores its previous state on +-// destruction. +-struct FunctionIRBuilderScope { ++// Saves the IRBuilder state and restores it on destruction. ++struct IRBuilderScope { + private: +- IRState &state; +- InsertionPoint previousInsertionPoint; +- llvm::FastMathFlags previousFMF; +- llvm::DebugLoc previousDebugLoc; ++ llvm::IRBuilderBase::InsertPointGuard ipGuard; ++ llvm::IRBuilderBase::FastMathFlagGuard fmfGuard; + + public: +- FunctionIRBuilderScope(IRState &state); +- ~FunctionIRBuilderScope(); ++ explicit IRBuilderScope(llvm::IRBuilderBase &builder) ++ : ipGuard(builder), fmfGuard(builder) {} + }; + + struct IRBuilderHelper { +@@ -123,7 +110,6 @@ struct IRAsmBlock { + struct IRState { + private: + IRBuilder<> builder; +- friend struct FunctionIRBuilderScope; + friend struct IRBuilderHelper; + + std::vector> +@@ -169,11 +155,16 @@ public: + llvm::Function *topfunc(); + llvm::Instruction *topallocapoint(); + +- // basic block scopes +- InsertionPoint getInsertPoint(); +- void setInsertPoint(llvm::BasicBlock *insertAtEnd); +- void setInsertPoint(InsertionPoint point); +- llvm::BasicBlock *scopebb(); ++ // Use this to set the IRBuilder's insertion point for a new function. ++ // The previous IRBuilder state is restored when the returned value is ++ // destructed. Use `ir->SetInsertPoint()` instead to change the insertion ++ // point inside the same function. ++ std::unique_ptr setInsertPoint(llvm::BasicBlock *bb); ++ // Use this to have the IRBuilder's current insertion point (incl. debug ++ // location) restored when the returned value is destructed. ++ std::unique_ptr saveInsertPoint(); ++ // Returns the basic block the IRBuilder currently inserts into. ++ llvm::BasicBlock *scopebb() { return ir->GetInsertBlock(); } + bool scopereturned(); + + // Creates a new basic block and inserts it before the specified one. +diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp +index fc914762..cbac1620 100644 +--- a/gen/passes/GarbageCollect2Stack.cpp ++++ b/gen/passes/GarbageCollect2Stack.cpp +@@ -217,17 +217,16 @@ public: + } + + Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const Analysis &A) override { +- IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint()); +- + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. ++ const IRBuilderBase::InsertPointGuard savedInsertPoint(B); + if (isa(arrSize)) { + BasicBlock &Entry = CB->getCaller()->getEntryBlock(); +- if (Builder.GetInsertBlock() != &Entry) { +- Builder.SetInsertPoint(&Entry, Entry.begin()); ++ if (B.GetInsertBlock() != &Entry) { ++ B.SetInsertPoint(&Entry, Entry.begin()); + } + NumGcToStack++; + } else { +@@ -235,9 +234,9 @@ public: + } + + // Convert array size to 32 bits if necessary +- Value *count = Builder.CreateIntCast(arrSize, Builder.getInt32Ty(), false); ++ Value *count = B.CreateIntCast(arrSize, B.getInt32Ty(), false); + AllocaInst *alloca = +- Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? ++ B.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? + + if (Initialized) { + // For now, only zero-init is supported. +@@ -251,10 +250,10 @@ public: + + if (ReturnType == ReturnType::Array) { + Value *arrStruct = llvm::UndefValue::get(CB->getType()); +- arrStruct = Builder.CreateInsertValue(arrStruct, arrSize, 0); ++ arrStruct = B.CreateInsertValue(arrStruct, arrSize, 0); + Value *memPtr = +- Builder.CreateBitCast(alloca, PointerType::getUnqual(B.getInt8Ty())); +- arrStruct = Builder.CreateInsertValue(arrStruct, memPtr, 1); ++ B.CreateBitCast(alloca, PointerType::getUnqual(B.getInt8Ty())); ++ arrStruct = B.CreateInsertValue(arrStruct, memPtr, 1); + return arrStruct; + } + +@@ -346,17 +345,16 @@ public: + } + + Value *promote(LLCallBasePtr CB, IRBuilder<> &B, const Analysis &A) override { +- IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint()); +- + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. ++ const IRBuilderBase::InsertPointGuard savedInsertPoint(B); + if (isa(SizeArg)) { + BasicBlock &Entry = CB->getCaller()->getEntryBlock(); +- if (Builder.GetInsertBlock() != &Entry) { +- Builder.SetInsertPoint(&Entry, Entry.begin()); ++ if (B.GetInsertBlock() != &Entry) { ++ B.SetInsertPoint(&Entry, Entry.begin()); + } + NumGcToStack++; + } else { +@@ -364,11 +362,11 @@ public: + } + + // Convert array size to 32 bits if necessary +- Value *count = Builder.CreateIntCast(SizeArg, Builder.getInt32Ty(), false); ++ Value *count = B.CreateIntCast(SizeArg, B.getInt32Ty(), false); + AllocaInst *alloca = +- Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? ++ B.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align? + +- return Builder.CreateBitCast(alloca, CB->getType()); ++ return B.CreateBitCast(alloca, CB->getType()); + } + + explicit UntypedMemoryFI(unsigned sizeArgNr) +diff --git a/gen/statements.cpp b/gen/statements.cpp +index 34266b4c..e5fc5932 100644 +--- a/gen/statements.cpp ++++ b/gen/statements.cpp +@@ -1087,12 +1087,10 @@ public: + if (PGO.emitsInstrumentation()) { + llvm::BasicBlock *casecntr = + irs->insertBBBefore(casejumptargetbb, "casecntr"); +- const auto savedInsertPoint = irs->getInsertPoint(); ++ const auto savedInsertPoint = irs->saveInsertPoint(); + irs->ir->SetInsertPoint(casecntr); + PGO.emitCounterIncrement(cs); + llvm::BranchInst::Create(casejumptargetbb, casecntr); +- irs->setInsertPoint(savedInsertPoint); +- + casejumptargetbb = casecntr; + } + +diff --git a/gen/toir.cpp b/gen/toir.cpp +index 63aa1154..fcccfaec 100644 +--- a/gen/toir.cpp ++++ b/gen/toir.cpp +@@ -187,12 +187,10 @@ namespace { + void pushVarDtorCleanup(IRState *p, VarDeclaration *vd) { + llvm::BasicBlock *beginBB = p->insertBB(llvm::Twine("dtor.") + vd->toChars()); + +- // TODO: Clean this up with push/pop insertion point methods. +- const auto savedInsertPoint = p->getInsertPoint(); ++ const auto savedInsertPoint = p->saveInsertPoint(); + p->ir->SetInsertPoint(beginBB); + toElemDtor(vd->edtor); + p->funcGen().scopes.pushCleanup(beginBB, p->scopebb()); +- p->setInsertPoint(savedInsertPoint); + } + } + +diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp +index b18da5b7..835ed77b 100644 +--- a/gen/trycatchfinally.cpp ++++ b/gen/trycatchfinally.cpp +@@ -218,7 +218,7 @@ void emitBeginCatchMSVC(IRState &irs, Catch *ctch, + + // redirect scope to avoid the generation of debug info before the + // catchpad +- const auto savedInsertPoint = irs.getInsertPoint(); ++ const auto savedInsertPoint = irs.saveInsertPoint(); + irs.ir->SetInsertPoint(gIR->topallocapoint()); + DtoDeclarationExp(var); + +@@ -231,9 +231,6 @@ void emitBeginCatchMSVC(IRState &irs, Catch *ctch, + cpyObj = exnObj; + exnObj = DtoAlloca(var->type, "exnObj"); + } +- irs.setInsertPoint(savedInsertPoint); +- irs.DBuilder.EmitStopPoint(ctch->loc); // re-set debug loc after the +- // SetInsertPoint(allocaInst) call + } else if (ctch->type) { + // catch without var + exnObj = DtoAlloca(ctch->type, "exnObj"); +@@ -692,7 +689,7 @@ llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() { + } + + // save and rewrite scope +- const auto savedInsertPoint = irs.getInsertPoint(); ++ const auto savedInsertPoint = irs.saveInsertPoint(); + + // insert landing pads at the end of the function, in emission order, + // to improve human-readability of the IR +@@ -768,7 +765,6 @@ llvm::BasicBlock *TryCatchFinallyScopes::emitLandingPad() { + irs.ir->CreateBr(resumeUnwindBlock); + } + +- irs.setInsertPoint(savedInsertPoint); + return beginBB; + } + +@@ -847,8 +843,7 @@ TryCatchFinallyScopes::runCleanupPad(CleanupCursor scope, + // can place an exception frame (but not done here) + auto frame = getNullPtr(getVoidPtrType()); + +- auto savedInsertPoint = irs.getInsertPoint(); +- auto savedDbgLoc = irs.DBuilder.GetCurrentLoc(); ++ const auto savedInsertPoint = irs.saveInsertPoint(); + + auto endFn = getRuntimeFunction(Loc(), irs.module, "_d_leave_cleanup"); + irs.ir->SetInsertPoint(cleanupret); +@@ -867,8 +862,5 @@ TryCatchFinallyScopes::runCleanupPad(CleanupCursor scope, + beginFn, frame, {llvm::OperandBundleDef("funclet", cleanuppad)}, ""); + llvm::BranchInst::Create(copybb, cleanupret, exec, cleanupbb); + +- irs.setInsertPoint(savedInsertPoint); +- irs.DBuilder.EmitStopPoint(savedDbgLoc); +- + return cleanupbb; + } +diff --git a/ir/irclass.cpp b/ir/irclass.cpp +index 8eecc2cd..0d74614e 100644 +--- a/ir/irclass.cpp ++++ b/ir/irclass.cpp +@@ -562,9 +562,7 @@ void IrClass::defineInterfaceVtbl(BaseClass *b, bool new_instance, + llvm::BasicBlock::Create(gIR->context(), "", thunk); + + // set up the IRBuilder scope for the thunk +- FunctionIRBuilderScope irBuilderScope(*gIR); +- gIR->setInsertPoint(beginbb); +- ++ const auto savedIRBuilderScope = gIR->setInsertPoint(beginbb); + gIR->DBuilder.EmitFuncStart(thunkFd); + + // Copy the function parameters, so later we can pass them to the +@@ -607,8 +605,6 @@ void IrClass::defineInterfaceVtbl(BaseClass *b, bool new_instance, + llvm::ReturnInst::Create(gIR->context(), call, beginbb); + } + +- gIR->DBuilder.EmitFuncEnd(thunkFd); +- + gIR->funcGenStates.pop_back(); + } + +-- +2.29.0 + + +From 4b7f14f58d8a6bae056b8b5254c2c8f96a0636a0 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Wed, 26 Aug 2020 00:31:31 +0200 +Subject: [PATCH 09/16] Hide new LLVM 11 CLI options + +--- + driver/cl_options.cpp | 44 +++++++++++++++++++++++++++---------------- + 1 file changed, 28 insertions(+), 16 deletions(-) + +diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp +index 40afb6dd..25c13bf0 100644 +--- a/driver/cl_options.cpp ++++ b/driver/cl_options.cpp +@@ -620,17 +620,24 @@ void hideLLVMOptions() { + static const char *const hiddenOptions[] = { + "aarch64-neon-syntax", "addrsig", "arm-add-build-attributes", + "arm-implicit-it", "asm-instrumentation", "asm-show-inst", +- "atomic-counter-update-promoted", "bounds-checking-single-trap", ++ "atomic-counter-update-promoted", "atomic-first-counter", ++ "basicblock-sections", "bounds-checking-single-trap", ++ "cfg-hide-deoptimize-paths", "cfg-hide-unreachable-paths", + "code-model", "cost-kind", "cppfname", "cppfor", "cppgen", + "cvp-dont-add-nowrap-flags", + "cvp-dont-process-adds", "debug-counter", "debug-entry-values", +- "debugger-tune", "denormal-fp-math", "disable-debug-info-verifier", ++ "debugger-tune", "debugify-level", "debugify-quiet", ++ "denormal-fp-math", "denormal-fp-math-f32", "disable-debug-info-verifier", + "disable-objc-arc-checkforcfghazards", "disable-spill-fusing", +- "do-counter-promotion", "emscripten-cxx-exceptions-whitelist", ++ "do-counter-promotion", "dwarf64", "emit-call-site-info", ++ "emscripten-cxx-exceptions-allowed", ++ "emscripten-cxx-exceptions-whitelist", + "emulated-tls", "enable-correct-eh-support", + "enable-cse-in-irtranslator", "enable-cse-in-legalizer", + "enable-emscripten-cxx-exceptions", "enable-emscripten-sjlj", +- "enable-fp-mad", "enable-gvn-memdep", "enable-implicit-null-checks", ++ "enable-fp-mad", "enable-gvn-hoist", "enable-gvn-memdep", ++ "enable-gvn-sink", "enable-implicit-null-checks", ++ "enable-load-in-loop-pre", + "enable-load-pre", "enable-loop-simplifycfg-term-folding", + "enable-misched", "enable-name-compression", "enable-no-infs-fp-math", + "enable-no-nans-fp-math", "enable-no-signed-zeros-fp-math", +@@ -640,23 +647,27 @@ void hideLLVMOptions() { + "exhaustive-register-search", "expensive-combines", + "fatal-assembler-warnings", "filter-print-funcs", + "force-dwarf-frame-section", "gpsize", "hash-based-counter-split", ++ "hot-cold-split", + "imp-null-check-page-size", "imp-null-max-insts-to-consider", + "import-all-index", "incremental-linker-compatible", + "instcombine-code-sinking", "instcombine-guard-widening-window", + "instcombine-max-iterations", "instcombine-max-num-phis", +- "instcombine-maxarray-size", "instrprof-atomic-counter-update-all", +- "internalize-public-api-file", ++ "instcombine-maxarray-size", "instcombine-negator-enabled", ++ "instcombine-negator-max-depth", ++ "instrprof-atomic-counter-update-all", "internalize-public-api-file", + "internalize-public-api-list", "iterative-counter-promotion", + "join-liveintervals", "jump-table-type", "limit-float-precision", +- "lto-embed-bitcode", "matrix-propagate-shape", ++ "lto-embed-bitcode", "matrix-default-layout", "matrix-propagate-shape", + "max-counter-promotions", "max-counter-promotions-per-loop", + "mc-relax-all", "mc-x86-disable-arith-relaxation", "meabi", + "memop-size-large", "memop-size-range", "merror-missing-parenthesis", + "merror-noncontigious-register", "mfuture-regs", "mips-compact-branches", +- "mips16-constant-islands", "mips16-hard-float", "mlsm", "mno-compound", ++ "mips16-constant-islands", "mips16-hard-float", "mir-strip-debugify-only", ++ "mlsm", "mno-compound", + "mno-fixup", "mno-ldc1-sdc1", "mno-pairing", "mwarn-missing-parenthesis", + "mwarn-noncontigious-register", "mwarn-sign-mismatch", +- "no-discriminators", "nozero-initialized-in-bss", "nvptx-sched4reg", ++ "no-discriminators", "no-xray-index", ++ "nozero-initialized-in-bss", "nvptx-sched4reg", + "objc-arc-annotation-target-identifier", "pie-copy-relocations", + "poison-checking-function-local", + "polly-dump-after", "polly-dump-after-file", "polly-dump-before", +@@ -666,7 +677,7 @@ void hideLLVMOptions() { + "profile-file", "profile-info-file", "profile-verifier-noassert", + "r600-ir-structurize", "rdf-dump", "rdf-limit", "recip", "regalloc", + "relax-elf-relocations", "remarks-section", "rewrite-map-file", "rng-seed", +- "safepoint-ir-verifier-print-only", ++ "runtime-counter-relocation", "safepoint-ir-verifier-print-only", + "sample-profile-check-record-coverage", + "sample-profile-check-sample-coverage", + "sample-profile-inline-hot-threshold", +@@ -674,14 +685,15 @@ void hideLLVMOptions() { + "speculative-counter-promotion-max-exiting", + "speculative-counter-promotion-to-loop", "spiller", "spirv-debug", + "spirv-erase-cl-md", "spirv-lower-const-expr", "spirv-mem2reg", +- "spirv-no-deref-attr", "spirv-text", "spvbool-validate", ++ "spirv-no-deref-attr", "spirv-text", ++ "spv-lower-saddwithoverflow-validate", "spvbool-validate", + "spvmemmove-validate", + "stack-alignment", "stack-size-section", "stack-symbol-ordering", + "stackmap-version", "static-func-full-module-prefix", + "static-func-strip-dirname-prefix", "stats", "stats-json", "strip-debug", +- "struct-path-tbaa", "summary-file", "tailcallopt", "thread-model", +- "time-passes", "time-trace-granularity", "tls-size", +- "unfold-element-atomic-memcpy-max-elements", ++ "struct-path-tbaa", "summary-file", "tail-predication", "tailcallopt", ++ "thread-model", "time-passes", "time-trace-granularity", "tls-size", ++ "unfold-element-atomic-memcpy-max-elements", "unique-bb-section-names", + "unique-section-names", "unit-at-a-time", "use-ctors", + "verify-debug-info", "verify-dom-info", "verify-loop-info", + "verify-loop-lcssa", "verify-machine-dom-info", "verify-regalloc", +@@ -689,6 +701,7 @@ void hideLLVMOptions() { + "vp-counters-per-site", "vp-static-alloc", + "x86-align-branch", "x86-align-branch-boundary", + "x86-branches-within-32B-boundaries", "x86-early-ifcvt", ++ "x86-pad-max-prefix-size", + "x86-recip-refinement-steps", "x86-use-vzeroupper", + + // We enable -fdata-sections/-ffunction-sections by default where it makes +@@ -697,8 +710,7 @@ void hideLLVMOptions() { + // We need our own switch as these two are defined by LLVM and linked to + // static TargetMachine members, but the default we want to use depends + // on the target triple (and thus we do not know it until after the +- // command +- // line has been parsed). ++ // command line has been parsed). + "fdata-sections", "ffunction-sections", "data-sections", + "function-sections"}; + +-- +2.29.0 + + +From 7aea2e73e9b1af5985ddf44eaf18af5483ffab97 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Wed, 26 Aug 2020 01:08:01 +0200 +Subject: [PATCH 10/16] Adapt dynamic-compile/JIT stuff for LLVM 11 + +--- + .azure-pipelines/posix.yml | 3 +- + .azure-pipelines/windows.yml | 1 - + CMakeLists.txt | 7 +---- + gen/dynamiccompile.cpp | 9 ++++-- + runtime/jit-rt/cpp-so/bind.cpp | 12 ++++++-- + runtime/jit-rt/cpp-so/disassembler.cpp | 38 ++++++++++++++++++++++---- + runtime/jit-rt/cpp-so/jit_context.cpp | 5 ++++ + 7 files changed, 57 insertions(+), 18 deletions(-) + +diff --git a/.azure-pipelines/posix.yml b/.azure-pipelines/posix.yml +index 15c2a559..2c2598b7 100644 +--- a/.azure-pipelines/posix.yml ++++ b/.azure-pipelines/posix.yml +@@ -378,8 +378,7 @@ steps: + cd .. + installed/bin/ldc2 -enable-dynamic-compile -run $BUILD_SOURCESDIRECTORY/tests/dynamiccompile/array.d + displayName: Run dynamic-compile integration test +- #condition: and(succeeded(), ne(variables['CI_OS'], 'android')) +- condition: not(always()) ++ condition: and(succeeded(), ne(variables['CI_OS'], 'android')) + + # Add dub & dlang tools + - script: | +diff --git a/.azure-pipelines/windows.yml b/.azure-pipelines/windows.yml +index c2fc43da..09f9e370 100644 +--- a/.azure-pipelines/windows.yml ++++ b/.azure-pipelines/windows.yml +@@ -172,7 +172,6 @@ steps: + call "%VSINSTALLDIR%Common7\Tools\VsDevCmd.bat" -arch=%ARCH% + installed\bin\ldc2 -enable-dynamic-compile -run %BUILD_SOURCESDIRECTORY%/tests/dynamiccompile/array.d + displayName: Run dynamic-compile integration test +- condition: not(always()) + - script: | + cd .. + call "%VSINSTALLDIR%Common7\Tools\VsDevCmd.bat" -arch=%ARCH% +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 46c2d3ae..d6f7c596 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -450,12 +450,7 @@ include(HandleLTOPGOBuildOptions) + set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (ON|OFF). Enabled by default.") + option(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES "Use custom LDC passes in jit" ON) + if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO") +- if(LDC_LLVM_VER LESS 1100) +- set(LDC_DYNAMIC_COMPILE ON) +- else() +- # FIXME: support LLVM 11+ too +- set(LDC_DYNAMIC_COMPILE OFF) +- endif() ++ set(LDC_DYNAMIC_COMPILE ON) + endif() + message(STATUS "Building LDC with dynamic compilation support: ${LDC_DYNAMIC_COMPILE} (LDC_DYNAMIC_COMPILE=${LDC_DYNAMIC_COMPILE})") + if(LDC_DYNAMIC_COMPILE) +diff --git a/gen/dynamiccompile.cpp b/gen/dynamiccompile.cpp +index d59cd00f..05d613d6 100644 +--- a/gen/dynamiccompile.cpp ++++ b/gen/dynamiccompile.cpp +@@ -193,8 +193,8 @@ void stripModule(llvm::Module &module) { + + void fixRtModule(llvm::Module &newModule, + const decltype(IRState::dynamicCompiledFunctions) &funcs) { +- std::unordered_map thunkVar2func; +- std::unordered_map thunkFun2func; ++ std::unordered_map thunkVar2func; ++ std::unordered_map thunkFun2func; + std::unordered_set externalFuncs; + for (auto &&it : funcs) { + assert(nullptr != it.first); +@@ -769,7 +769,12 @@ void createThunkFunc(llvm::Module &module, const llvm::Function *src, + for (auto &arg : dst->args()) { + args.push_back(&arg); + } ++#if LDC_LLVM_VER >= 1100 ++ auto ret = builder.CreateCall( ++ llvm::FunctionCallee(dst->getFunctionType(), thunkPtr), args); ++#else + auto ret = builder.CreateCall(thunkPtr, args); ++#endif + ret->setCallingConv(src->getCallingConv()); + ret->setAttributes(src->getAttributes()); + if (dst->getReturnType()->isVoidTy()) { +diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp +index 37cb7e79..e1a0bea3 100644 +--- a/runtime/jit-rt/cpp-so/bind.cpp ++++ b/runtime/jit-rt/cpp-so/bind.cpp +@@ -19,8 +19,14 @@ + #include "valueparser.h" + + #if LDC_LLVM_VER >= 1000 ++#if LDC_LLVM_VER >= 1100 ++#define LLAlign llvm::Align ++#else ++#define LLAlign llvm::MaybeAlign ++#endif + #define LLMaybeAlign llvm::MaybeAlign + #else ++#define LLAlign + #define LLMaybeAlign + #endif + +@@ -77,14 +83,16 @@ allocParam(llvm::IRBuilder<> &builder, llvm::Type &srcType, + if (param.type == ParamType::Aggregate && srcType.isPointerTy()) { + auto elemType = llvm::cast(&srcType)->getElementType(); + auto stackArg = builder.CreateAlloca(elemType); +- stackArg->setAlignment(LLMaybeAlign(layout.getABITypeAlignment(elemType))); ++ if (auto alignment = layout.getABITypeAlignment(elemType)) ++ stackArg->setAlignment(LLAlign(alignment)); + auto init = + parseInitializer(layout, *elemType, param.data, errHandler, override); + builder.CreateStore(init, stackArg); + return stackArg; + } + auto stackArg = builder.CreateAlloca(&srcType); +- stackArg->setAlignment(LLMaybeAlign(layout.getABITypeAlignment(&srcType))); ++ if (auto alignment = layout.getABITypeAlignment(&srcType)) ++ stackArg->setAlignment(LLAlign(alignment)); + auto init = + parseInitializer(layout, srcType, param.data, errHandler, override); + builder.CreateStore(init, stackArg); +diff --git a/runtime/jit-rt/cpp-so/disassembler.cpp b/runtime/jit-rt/cpp-so/disassembler.cpp +index 79e47761..eda0be22 100644 +--- a/runtime/jit-rt/cpp-so/disassembler.cpp ++++ b/runtime/jit-rt/cpp-so/disassembler.cpp +@@ -135,11 +135,21 @@ void printFunction(const llvm::MCDisassembler &disasm, + + switch (status) { + case llvm::MCDisassembler::Fail: +- streamer.EmitRawText("failed to disassemble"); ++#if LDC_LLVM_VER >= 1100 ++ streamer.emitRawText( ++#else ++ streamer.EmitRawText( ++#endif ++ "failed to disassemble"); + return; + + case llvm::MCDisassembler::SoftFail: +- streamer.EmitRawText("potentially undefined instruction encoding:"); ++#if LDC_LLVM_VER >= 1100 ++ streamer.emitRawText( ++#else ++ streamer.EmitRawText( ++#endif ++ "potentially undefined instruction encoding:"); + LLVM_FALLTHROUGH; + + case llvm::MCDisassembler::Success: +@@ -152,14 +162,22 @@ void printFunction(const llvm::MCDisassembler &disasm, + } + } else if (Stage::Emit == stage) { + if (auto label = symTable.getTargetLabel(pos)) { ++#if LDC_LLVM_VER >= 1100 ++ streamer.emitLabel(label); ++#else + streamer.EmitLabel(label); ++#endif + } + commentStream.flush(); + if (!comment.empty()) { + streamer.AddComment(comment); + comment.clear(); + } ++#if LDC_LLVM_VER >= 1100 ++ streamer.emitInstruction(inst, sti); ++#else + streamer.EmitInstruction(inst, sti); ++#endif + } + break; + } +@@ -304,7 +322,11 @@ void disassemble(const llvm::TargetMachine &tm, + for (const auto &symbol : object.symbols()) { + const auto secIt = llvm::cantFail(symbol.getSection()); + if (object.section_end() != secIt) { ++#if LDC_LLVM_VER >= 1100 ++ auto offset = llvm::cantFail(symbol.getValue()); ++#else + auto offset = symbol.getValue(); ++#endif + sectionsToProcess[secIt->getIndex()].push_back(offset); + } + } +@@ -322,9 +344,7 @@ void disassemble(const llvm::TargetMachine &tm, + const auto sec = *secIt; + llvm::StringRef data; + #if LDC_LLVM_VER >= 900 +- auto dataOrError = sec.getContents(); +- assert(dataOrError); +- data = *dataOrError; ++ data = llvm::cantFail(sec.getContents()); + #else + sec.getContents(data); + #endif +@@ -333,7 +353,11 @@ void disassemble(const llvm::TargetMachine &tm, + llvm::cantFail(symbol.getType())) { + symTable.reset(); + symTable.addLabel(0, 0, name); // Function start ++#if LDC_LLVM_VER >= 1100 ++ auto offset = llvm::cantFail(symbol.getValue()); ++#else + auto offset = symbol.getValue(); ++#endif + processRelocations(symTable, offset, object, sec); + + // TODO: something more optimal +@@ -360,7 +384,11 @@ void disassemble(const llvm::TargetMachine &tm, + reinterpret_cast(data.data() + offset), size); + + printFunction(*disasm, *mcia, buff, symTable, *sti, *asmStreamer); ++#if LDC_LLVM_VER >= 1100 ++ asmStreamer->emitRawText(""); ++#else + asmStreamer->EmitRawText(""); ++#endif + } + } + } +diff --git a/runtime/jit-rt/cpp-so/jit_context.cpp b/runtime/jit-rt/cpp-so/jit_context.cpp +index 2a783e18..ad4378eb 100644 +--- a/runtime/jit-rt/cpp-so/jit_context.cpp ++++ b/runtime/jit-rt/cpp-so/jit_context.cpp +@@ -202,7 +202,12 @@ std::shared_ptr + DynamicCompilerContext::createResolver() { + return llvm::orc::createLegacyLookupResolver( + execSession, ++#if LDC_LLVM_VER >= 1100 ++ [this](llvm::StringRef name_) -> llvm::JITSymbol { ++ const std::string name = name_.str(); ++#else + [this](const std::string &name) -> llvm::JITSymbol { ++#endif + if (auto Sym = compileLayer.findSymbol(name, false)) { + return Sym; + } else if (auto Err = Sym.takeError()) { +-- +2.29.0 + + +From c8889a9219a7d6ae96f81d020ae6d8f760e84297 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Wed, 26 Aug 2020 12:30:21 +0200 +Subject: [PATCH 11/16] Fix alignment issue when casting vector rvalue to + static array + +Previously, the static-array alloca wasn't suitably aligned for the +store instruction, which uses the greater vector alignment. +This has surfaced now with LLVM 11 on Win32 - this fixes dmd-testsuite's +runnable/ldc_llvm_inline_ir.d. +--- + gen/llvmhelpers.cpp | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp +index 6024d827..8b5ff3c4 100644 +--- a/gen/llvmhelpers.cpp ++++ b/gen/llvmhelpers.cpp +@@ -637,8 +637,7 @@ DValue *DtoCastVector(Loc &loc, DValue *val, Type *to) { + LLValue *vector = DtoRVal(val); + IF_LOG Logger::cout() << "src: " << *vector << "to type: " << *tolltype + << " (creating temporary)\n"; +- LLValue *array = DtoAlloca(to); +- DtoStore(vector, DtoBitCast(array, getPtrToType(vector->getType()))); ++ LLValue *array = DtoAllocaDump(vector, tolltype, DtoAlignment(val->type)); + return new DLValue(to, array); + } + if (totype->ty == Tvector && to->size() == val->type->size()) { +-- +2.29.0 + + +From 76de43f6c2f3ba211095ce0b36d8cff44ae50a39 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Wed, 26 Aug 2020 17:26:16 +0200 +Subject: [PATCH 12/16] Disable lit-test instrument/xray_link.d for Mac and + LLVM 11+ + +--- + tests/instrument/xray_link.d | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tests/instrument/xray_link.d b/tests/instrument/xray_link.d +index 3f2079c5..6ef69dd6 100644 +--- a/tests/instrument/xray_link.d ++++ b/tests/instrument/xray_link.d +@@ -1,5 +1,9 @@ + // REQUIRES: XRay_RT + ++// fails on macOS with LLVM 11 due to a linker error, see ++// https://github.com/llvm/llvm-test-suite/commit/2c3c4a6286d453f763c0245c6536ddd368f0db99 ++// XFAIL: Darwin && atleast_llvm1100 ++ + // RUN: %ldc -fxray-instrument -fxray-instruction-threshold=1 -of=%t%exe %s -vv | FileCheck %s + + void foo() +-- +2.29.0 + + +From ca05705af6bc8a9d58821e3a3646cc6bfa5020de Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Tue, 1 Sep 2020 00:17:44 +0200 +Subject: [PATCH 13/16] Azure CI: Bump LDC-LLVM to v11.0.0-rc2+ + +--- + azure-pipelines.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/azure-pipelines.yml b/azure-pipelines.yml +index ac415714..8b0f9fee 100644 +--- a/azure-pipelines.yml ++++ b/azure-pipelines.yml +@@ -1,5 +1,5 @@ + variables: +- LLVM_VERSION: b3db88a4 ++ LLVM_VERSION: 887792a5 + CLANG_VERSION: 10.0.1 + HOST_LDC_VERSION: 1.22.0 + +-- +2.29.0 + + +From aa7d38ff7ee6f771539e40a3c5ad532c4e755304 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Tue, 22 Sep 2020 21:04:07 +0200 +Subject: [PATCH 14/16] Azure CI: Bump LDC-LLVM to ~v11.0.0-rc3 + +--- + azure-pipelines.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/azure-pipelines.yml b/azure-pipelines.yml +index 8b0f9fee..459948e1 100644 +--- a/azure-pipelines.yml ++++ b/azure-pipelines.yml +@@ -1,5 +1,5 @@ + variables: +- LLVM_VERSION: 887792a5 ++ LLVM_VERSION: cc7582ff + CLANG_VERSION: 10.0.1 + HOST_LDC_VERSION: 1.22.0 + +-- +2.29.0 + + +From 5b0a046fd63a4aef559815afac9461a76d8cb5d2 Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Tue, 29 Sep 2020 03:38:09 +0200 +Subject: [PATCH 15/16] CI: Bump LDC-LLVM to v11.0.0-rc4 + +--- + azure-pipelines.yml | 2 +- + shippable.yml | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/azure-pipelines.yml b/azure-pipelines.yml +index 459948e1..cf37e6ea 100644 +--- a/azure-pipelines.yml ++++ b/azure-pipelines.yml +@@ -1,5 +1,5 @@ + variables: +- LLVM_VERSION: cc7582ff ++ LLVM_VERSION: f13ab423 + CLANG_VERSION: 10.0.1 + HOST_LDC_VERSION: 1.22.0 + +diff --git a/shippable.yml b/shippable.yml +index e98d00a8..3e47fb57 100644 +--- a/shippable.yml ++++ b/shippable.yml +@@ -13,7 +13,7 @@ env: + global: + - secure: RQ6gpJFPBDGVlnz+ZzSgeMpkcnvcA/7Lzyj/r06fMFR5iOz2cYaImCekNRw2PlhYQ+0FCQ119TLMKNOa7OUu6XxUp5LZtq7pSB6QLe3RB3YysFsosNPlY/wyyRsrW9ICEbDP/X8kPcfrDtOPGS/dGIwgeo0+R4Yl0OLDK9GrExEY45bWgvuLqoWDO89pi31kBk5LG5MAYhHZ0UTdboi5A2GRT0T8M0kr53jBRka8FGkbncXfHp9+/6IjTVJoUduRkdsk0A9RN1KRoao6rtrBNNvwIStc6zxJSOHszoaTp/K/ucGC4InZl/9GHPS/Y78SGKn7YJv3tGmcGzqAxrVaXQ== + matrix: +- - LLVM_VERSION=10.0.1 ++ - LLVM_VERSION=f13ab423 + HOST_LDC_VERSION=1.22.0 + EXTRA_CMAKE_FLAGS="-DBUILD_LTO_LIBS=ON -DCMAKE_EXE_LINKER_FLAGS=-static-libstdc++ -DJITRT_EXTRA_LDFLAGS=-static-libstdc++ -DLDC_INSTALL_LTOPLUGIN=ON -DLDC_INSTALL_LLVM_RUNTIME_LIBS=ON -DLDC_INSTALL_LLVM_RUNTIME_LIBS_ARCH=aarch64" + +@@ -39,7 +39,7 @@ build: + echo 'Using LLVM with enabled assertions' + assertsSuffix="-withAsserts" + fi +- curl -L -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$LLVM_VERSION/llvm-$LLVM_VERSION-linux-aarch64$assertsSuffix.tar.xz ++ curl -L -o llvm.tar.xz https://github.com/ldc-developers/llvm-project/releases/download/CI/llvm-$LLVM_VERSION-linux-aarch64$assertsSuffix.tar.xz + - mkdir llvm + - tar -xf llvm.tar.xz --strip 1 -C llvm + - rm llvm.tar.xz +-- +2.29.0 + + +From 92ec0435e61f49ea20b70c3e10e9749eb8bd231b Mon Sep 17 00:00:00 2001 +From: Martin Kinkelin +Date: Wed, 30 Sep 2020 17:29:40 +0200 +Subject: [PATCH 16/16] CI: Bump LDC-LLVM to v11.0.0-rc4+ with AArch64 + regression fix + +--- + azure-pipelines.yml | 2 +- + shippable.yml | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/azure-pipelines.yml b/azure-pipelines.yml +index cf37e6ea..f404b3a9 100644 +--- a/azure-pipelines.yml ++++ b/azure-pipelines.yml +@@ -1,5 +1,5 @@ + variables: +- LLVM_VERSION: f13ab423 ++ LLVM_VERSION: 84863923 + CLANG_VERSION: 10.0.1 + HOST_LDC_VERSION: 1.22.0 + +diff --git a/shippable.yml b/shippable.yml +index 3e47fb57..d4ce5c8e 100644 +--- a/shippable.yml ++++ b/shippable.yml +@@ -13,7 +13,7 @@ env: + global: + - secure: RQ6gpJFPBDGVlnz+ZzSgeMpkcnvcA/7Lzyj/r06fMFR5iOz2cYaImCekNRw2PlhYQ+0FCQ119TLMKNOa7OUu6XxUp5LZtq7pSB6QLe3RB3YysFsosNPlY/wyyRsrW9ICEbDP/X8kPcfrDtOPGS/dGIwgeo0+R4Yl0OLDK9GrExEY45bWgvuLqoWDO89pi31kBk5LG5MAYhHZ0UTdboi5A2GRT0T8M0kr53jBRka8FGkbncXfHp9+/6IjTVJoUduRkdsk0A9RN1KRoao6rtrBNNvwIStc6zxJSOHszoaTp/K/ucGC4InZl/9GHPS/Y78SGKn7YJv3tGmcGzqAxrVaXQ== + matrix: +- - LLVM_VERSION=f13ab423 ++ - LLVM_VERSION=84863923 + HOST_LDC_VERSION=1.22.0 + EXTRA_CMAKE_FLAGS="-DBUILD_LTO_LIBS=ON -DCMAKE_EXE_LINKER_FLAGS=-static-libstdc++ -DJITRT_EXTRA_LDFLAGS=-static-libstdc++ -DLDC_INSTALL_LTOPLUGIN=ON -DLDC_INSTALL_LLVM_RUNTIME_LIBS=ON -DLDC_INSTALL_LLVM_RUNTIME_LIBS_ARCH=aarch64" + +-- +2.29.0 + diff --git a/ldc.spec b/ldc.spec index dacfb06..aca7352 100644 --- a/ldc.spec +++ b/ldc.spec @@ -1,6 +1,6 @@ %define bootstrap 1 Name: ldc -Version: 1.21.0 +Version: 1.23.0 Release: 1mamba Summary: The LLVM-based D Compiler Group: Applications/Development @@ -8,10 +8,10 @@ Vendor: openmamba Distribution: openmamba Packager: Silvan Calarco URL: http://wiki.dlang.org/LDC -## GITSOURCE https://github.com/ldc-developers/ldc.git v1.16.0 Source: https://github.com/ldc-developers/ldc.git/v%{version}/ldc-%{version}.tar.bz2 Source1: https://github.com/ldc-developers/ldc/releases/download/v%{version}/ldc2-%{version}-linux-x86_64.tar.xz Source2: https://github.com/ldc-developers/ldc/releases/download/v1.2.0/ldc2-1.2.0-linux-x86.tar.xz +Patch0: ldc-1.23.0-llvm-11.patch License: BSD ## AUTOBUILDREQ-BEGIN BuildRequires: glibc-devel @@ -21,6 +21,7 @@ BuildRequires: libllvm-devel BuildRequires: libstdc++6-devel ## AUTOBUILDREQ-END BuildRequires: cmake +BuildRequires: libllvm-devel >= 11.0.0 Requires: lib%{name} = %{?epoch:%epoch:}%{version}-%{release} BuildRoot: %{_tmppath}/%{name}-%{version}-root @@ -56,6 +57,7 @@ This package contains libraries and header files for developing applications tha %else %setup -q %endif +%patch0 -p1 %build %cmake -d build \ @@ -122,6 +124,9 @@ This package contains libraries and header files for developing applications tha %doc README.md %changelog +* Thu Oct 22 2020 Silvan Calarco 1.23.0-1mamba +- update to 1.23.0 + * Wed May 06 2020 Silvan Calarco 1.21.0-1mamba - update to 1.21.0