From af12608fcd9a92b293135c5f9d30c1f930cb7a9e Mon Sep 17 00:00:00 2001 From: Automatic Build System Date: Sat, 6 Jan 2024 03:35:19 +0100 Subject: [PATCH] automatic version update by autodist [release 1.32.2-1mamba;Sun May 28 2023] --- ldc-1.23.0-llvm-11.patch | 6728 ----------------- ldc-1.32.2-llvm-16-add_llvm_profdata.patch | 3033 ++++++++ ldc-1.32.2-llvm-16-context_usage.patch | 14 + ldc-1.32.2-llvm-16-fix-aarch64-support.patch | 29 + ldc-1.32.2-llvm-16-fix-ms_cxx_helper.patch | 31 + ...lvm-16-stop_using_aggressive_combine.patch | 29 + ldc-1.32.2-llvm-16-update_to_ir_include.patch | 25 + ldc-1.32.2-llvm-16-use_std_optional_api.patch | 204 + ...2.2-llvm-16.0.4-use_std_optional_api.patch | 30 + ldc.spec | 64 +- 10 files changed, 3432 insertions(+), 6755 deletions(-) delete mode 100644 ldc-1.23.0-llvm-11.patch create mode 100644 ldc-1.32.2-llvm-16-add_llvm_profdata.patch create mode 100644 ldc-1.32.2-llvm-16-context_usage.patch create mode 100644 ldc-1.32.2-llvm-16-fix-aarch64-support.patch create mode 100644 ldc-1.32.2-llvm-16-fix-ms_cxx_helper.patch create mode 100644 ldc-1.32.2-llvm-16-stop_using_aggressive_combine.patch create mode 100644 ldc-1.32.2-llvm-16-update_to_ir_include.patch create mode 100644 ldc-1.32.2-llvm-16-use_std_optional_api.patch create mode 100644 ldc-1.32.2-llvm-16.0.4-use_std_optional_api.patch diff --git a/ldc-1.23.0-llvm-11.patch b/ldc-1.23.0-llvm-11.patch deleted file mode 100644 index 05edec6..0000000 --- a/ldc-1.23.0-llvm-11.patch +++ /dev/null @@ -1,6728 +0,0 @@ -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-1.32.2-llvm-16-add_llvm_profdata.patch b/ldc-1.32.2-llvm-16-add_llvm_profdata.patch new file mode 100644 index 0000000..a9ce994 --- /dev/null +++ b/ldc-1.32.2-llvm-16-add_llvm_profdata.patch @@ -0,0 +1,3033 @@ +From 3713607636f887883eb31285ec52eaa7bd26a744 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Mon, 22 May 2023 12:02:28 +0100 +Subject: [PATCH] ldc-profdata: Add llvm-profdata from LLVM release/16.x + +Modified to have an explicit `main()` per the other profdata +imports in this tree. + +Signed-off-by: Ikey Doherty +--- + tools/ldc-profdata/llvm-profdata-16.0.cpp | 3013 +++++++++++++++++++++ + 1 file changed, 3013 insertions(+) + create mode 100644 tools/ldc-profdata/llvm-profdata-16.0.cpp + +diff --git a/tools/ldc-profdata/llvm-profdata-16.0.cpp b/tools/ldc-profdata/llvm-profdata-16.0.cpp +new file mode 100644 +index 0000000000..57dfd18076 +--- /dev/null ++++ b/tools/ldc-profdata/llvm-profdata-16.0.cpp +@@ -0,0 +1,3013 @@ ++//===- 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/Object/Binary.h" ++#include "llvm/ProfileData/InstrProfCorrelator.h" ++#include "llvm/ProfileData/InstrProfReader.h" ++#include "llvm/ProfileData/InstrProfWriter.h" ++#include "llvm/ProfileData/MemProf.h" ++#include "llvm/ProfileData/ProfileCommon.h" ++#include "llvm/ProfileData/RawMemProfReader.h" ++#include "llvm/ProfileData/SampleProfReader.h" ++#include "llvm/ProfileData/SampleProfWriter.h" ++#include "llvm/Support/CommandLine.h" ++#include "llvm/Support/Discriminator.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/MD5.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 ++#include ++#include ++#include ++ ++using namespace llvm; ++ ++// We use this string to indicate that there are ++// multiple static functions map to the same name. ++const std::string DuplicateNameStr = "----"; ++ ++enum ProfileFormat { ++ PF_None = 0, ++ PF_Text, ++ PF_Compact_Binary, ++ PF_Ext_Binary, ++ PF_GCC, ++ PF_Binary ++}; ++ ++enum class ShowFormat { Text, Json, Yaml }; ++ ++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 warn(Error E, StringRef Whence = "") { ++ if (E.isA()) { ++ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { ++ warn(IPE.message(), std::string(Whence), std::string("")); ++ }); ++ } ++} ++ ++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 in case user missed specifying the profile type. ++ Hint = "Perhaps you forgot to use the --sample or --memory option?"; ++ } ++ exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); ++ }); ++ return; ++ } ++ ++ 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, memory }; ++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) ++ : Writer(IsSparse), 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, ++ const InstrProfCorrelator *Correlator, ++ const StringRef ProfiledBinary, 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; ++ ++ using ::llvm::memprof::RawMemProfReader; ++ if (RawMemProfReader::hasFormat(Input.Filename)) { ++ auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary); ++ if (!ReaderOrErr) { ++ exitWithError(ReaderOrErr.takeError(), Input.Filename); ++ } ++ std::unique_ptr Reader = std::move(ReaderOrErr.get()); ++ // Check if the profile types can be merged, e.g. clang frontend profiles ++ // should not be merged with memprof profiles. ++ if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { ++ consumeError(std::move(E)); ++ WC->Errors.emplace_back( ++ make_error( ++ "Cannot merge MemProf profile with Clang generated profile.", ++ std::error_code()), ++ Filename); ++ return; ++ } ++ ++ auto MemProfError = [&](Error E) { ++ instrprof_error IPE = InstrProfError::take(std::move(E)); ++ WC->Errors.emplace_back(make_error(IPE), Filename); ++ }; ++ ++ // Add the frame mappings into the writer context. ++ const auto &IdToFrame = Reader->getFrameMapping(); ++ for (const auto &I : IdToFrame) { ++ bool Succeeded = WC->Writer.addMemProfFrame( ++ /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError); ++ // If we weren't able to add the frame mappings then it doesn't make sense ++ // to try to add the records from this profile. ++ if (!Succeeded) ++ return; ++ } ++ const auto &FunctionProfileData = Reader->getProfileData(); ++ // Add the memprof records into the writer context. ++ for (const auto &I : FunctionProfileData) { ++ WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second); ++ } ++ return; ++ } ++ ++ auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator); ++ 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()); ++ if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { ++ consumeError(std::move(E)); ++ 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); ++ } ++ ++ std::vector BinaryIds; ++ if (Error E = Reader->readBinaryIds(BinaryIds)) ++ WC->Errors.emplace_back(std::move(E), Filename); ++ WC->Writer.addBinaryIds(BinaryIds); ++} ++ ++/// 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(); ++ ++ if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind())) ++ exitWithError(std::move(E)); ++ ++ 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, ++ OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF ++ : sys::fs::OF_None); ++ if (EC) ++ exitWithErrorCode(EC, OutputFilename); ++ ++ if (OutputFormat == PF_Text) { ++ if (Error E = Writer.writeText(Output)) ++ warn(std::move(E)); ++ } else { ++ if (Output.is_displayed()) ++ exitWithError("cannot write a non-text format profile to the terminal"); ++ if (Error E = Writer.write(Output)) ++ warn(std::move(E)); ++ } ++} ++ ++static void mergeInstrProfile(const WeightedFileVector &Inputs, ++ StringRef DebugInfoFilename, ++ SymbolRemapper *Remapper, ++ StringRef OutputFilename, ++ ProfileFormat OutputFormat, bool OutputSparse, ++ unsigned NumThreads, FailureMode FailMode, ++ const StringRef ProfiledBinary) { ++ if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && ++ OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) ++ exitWithError("unknown format is specified"); ++ ++ std::unique_ptr Correlator; ++ if (!DebugInfoFilename.empty()) { ++ if (auto Err = ++ InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator)) ++ exitWithError(std::move(Err), DebugInfoFilename); ++ if (auto Err = Correlator->correlateProfileData()) ++ exitWithError(std::move(Err), DebugInfoFilename); ++ } ++ ++ 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)); ++ ++ // 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, Correlator.get(), ProfiledBinary, ++ 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, Correlator.get(), ProfiledBinary, ++ 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 profile can be merged"); ++ ++ writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); ++} ++ ++/// The profile entry for a function in instrumentation profile. ++struct InstrProfileEntry { ++ uint64_t MaxCount = 0; ++ uint64_t NumEdgeCounters = 0; ++ float ZeroCounterRatio = 0.0; ++ InstrProfRecord *ProfRecord; ++ InstrProfileEntry(InstrProfRecord *Record); ++ InstrProfileEntry() = default; ++}; ++ ++InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) { ++ ProfRecord = Record; ++ uint64_t CntNum = Record->Counts.size(); ++ uint64_t ZeroCntNum = 0; ++ for (size_t I = 0; I < CntNum; ++I) { ++ MaxCount = std::max(MaxCount, Record->Counts[I]); ++ ZeroCntNum += !Record->Counts[I]; ++ } ++ ZeroCounterRatio = (float)ZeroCntNum / CntNum; ++ NumEdgeCounters = CntNum; ++} ++ ++/// Either set all the counters in the instr profile entry \p IFE to ++/// -1 / -2 /in order to drop the profile or scale up the ++/// counters in \p IFP to be above hot / cold threshold. We use ++/// the ratio of zero counters in the profile of a function to ++/// decide the profile is helpful or harmful for performance, ++/// and to choose whether to scale up or drop it. ++static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot, ++ uint64_t HotInstrThreshold, ++ uint64_t ColdInstrThreshold, ++ float ZeroCounterThreshold) { ++ InstrProfRecord *ProfRecord = IFE.ProfRecord; ++ if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) { ++ // If all or most of the counters of the function are zero, the ++ // profile is unaccountable and should be dropped. Reset all the ++ // counters to be -1 / -2 and PGO profile-use will drop the profile. ++ // All counters being -1 also implies that the function is hot so ++ // PGO profile-use will also set the entry count metadata to be ++ // above hot threshold. ++ // All counters being -2 implies that the function is warm so ++ // PGO profile-use will also set the entry count metadata to be ++ // above cold threshold. ++ auto Kind = ++ (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm); ++ ProfRecord->setPseudoCount(Kind); ++ return; ++ } ++ ++ // Scale up the MaxCount to be multiple times above hot / cold threshold. ++ const unsigned MultiplyFactor = 3; ++ uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold); ++ uint64_t Numerator = Threshold * MultiplyFactor; ++ ++ // Make sure Threshold for warm counters is below the HotInstrThreshold. ++ if (!SetToHot && Threshold >= HotInstrThreshold) { ++ Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2; ++ } ++ ++ uint64_t Denominator = IFE.MaxCount; ++ if (Numerator <= Denominator) ++ return; ++ ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) { ++ warn(toString(make_error(E))); ++ }); ++} ++ ++const uint64_t ColdPercentileIdx = 15; ++const uint64_t HotPercentileIdx = 11; ++ ++using sampleprof::FSDiscriminatorPass; ++ ++// Internal options to set FSDiscriminatorPass. Used in merge and show ++// commands. ++static cl::opt FSDiscriminatorPassOption( ++ "fs-discriminator-pass", cl::init(PassLast), cl::Hidden, ++ cl::desc("Zero out the discriminator bits for the FS discrimiantor " ++ "pass beyond this value. The enum values are defined in " ++ "Support/Discriminator.h"), ++ cl::values(clEnumVal(Base, "Use base discriminators only"), ++ clEnumVal(Pass1, "Use base and pass 1 discriminators"), ++ clEnumVal(Pass2, "Use base and pass 1-2 discriminators"), ++ clEnumVal(Pass3, "Use base and pass 1-3 discriminators"), ++ clEnumVal(PassLast, "Use all discriminator bits (default)"))); ++ ++static unsigned getDiscriminatorMask() { ++ return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue())); ++} ++ ++/// Adjust the instr profile in \p WC based on the sample profile in ++/// \p Reader. ++static void ++adjustInstrProfile(std::unique_ptr &WC, ++ std::unique_ptr &Reader, ++ unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, ++ unsigned InstrProfColdThreshold) { ++ // Function to its entry in instr profile. ++ StringMap InstrProfileMap; ++ StringMap StaticFuncMap; ++ InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs); ++ ++ auto checkSampleProfileHasFUnique = [&Reader]() { ++ for (const auto &PD : Reader->getProfiles()) { ++ auto &FContext = PD.first; ++ if (FContext.toString().find(FunctionSamples::UniqSuffix) != ++ std::string::npos) { ++ return true; ++ } ++ } ++ return false; ++ }; ++ ++ bool SampleProfileHasFUnique = checkSampleProfileHasFUnique(); ++ ++ auto buildStaticFuncMap = [&StaticFuncMap, ++ SampleProfileHasFUnique](const StringRef Name) { ++ std::string Prefixes[] = {".cpp:", "cc:", ".c:", ".hpp:", ".h:"}; ++ size_t PrefixPos = StringRef::npos; ++ for (auto &Prefix : Prefixes) { ++ PrefixPos = Name.find_insensitive(Prefix); ++ if (PrefixPos == StringRef::npos) ++ continue; ++ PrefixPos += Prefix.size(); ++ break; ++ } ++ ++ if (PrefixPos == StringRef::npos) { ++ return; ++ } ++ ++ StringRef NewName = Name.drop_front(PrefixPos); ++ StringRef FName = Name.substr(0, PrefixPos - 1); ++ if (NewName.size() == 0) { ++ return; ++ } ++ ++ // This name should have a static linkage. ++ size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix); ++ bool ProfileHasFUnique = (PostfixPos != StringRef::npos); ++ ++ // If sample profile and instrumented profile do not agree on symbol ++ // uniqification. ++ if (SampleProfileHasFUnique != ProfileHasFUnique) { ++ // If instrumented profile uses -funique-internal-linakge-symbols, ++ // we need to trim the name. ++ if (ProfileHasFUnique) { ++ NewName = NewName.substr(0, PostfixPos); ++ } else { ++ // If sample profile uses -funique-internal-linakge-symbols, ++ // we build the map. ++ std::string NStr = ++ NewName.str() + getUniqueInternalLinkagePostfix(FName); ++ NewName = StringRef(NStr); ++ StaticFuncMap[NewName] = Name; ++ return; ++ } ++ } ++ ++ if (StaticFuncMap.find(NewName) == StaticFuncMap.end()) { ++ StaticFuncMap[NewName] = Name; ++ } else { ++ StaticFuncMap[NewName] = DuplicateNameStr; ++ } ++ }; ++ ++ // We need to flatten the SampleFDO profile as the InstrFDO ++ // profile does not have inlined callsite profiles. ++ // One caveat is the pre-inlined function -- their samples ++ // should be collapsed into the caller function. ++ // Here we do a DFS traversal to get the flatten profile ++ // info: the sum of entrycount and the max of maxcount. ++ // Here is the algorithm: ++ // recursive (FS, root_name) { ++ // name = FS->getName(); ++ // get samples for FS; ++ // if (InstrProf.find(name) { ++ // root_name = name; ++ // } else { ++ // if (name is in static_func map) { ++ // root_name = static_name; ++ // } ++ // } ++ // update the Map entry for root_name; ++ // for (subfs: FS) { ++ // recursive(subfs, root_name); ++ // } ++ // } ++ // ++ // Here is an example. ++ // ++ // SampleProfile: ++ // foo:12345:1000 ++ // 1: 1000 ++ // 2.1: 1000 ++ // 15: 5000 ++ // 4: bar:1000 ++ // 1: 1000 ++ // 2: goo:3000 ++ // 1: 3000 ++ // 8: bar:40000 ++ // 1: 10000 ++ // 2: goo:30000 ++ // 1: 30000 ++ // ++ // InstrProfile has two entries: ++ // foo ++ // bar.cc:bar ++ // ++ // After BuildMaxSampleMap, we should have the following in FlattenSampleMap: ++ // {"foo", {1000, 5000}} ++ // {"bar.cc:bar", {11000, 30000}} ++ // ++ // foo's has an entry count of 1000, and max body count of 5000. ++ // bar.cc:bar has an entry count of 11000 (sum two callsites of 1000 and ++ // 10000), and max count of 30000 (from the callsite in line 8). ++ // ++ // Note that goo's count will remain in bar.cc:bar() as it does not have an ++ // entry in InstrProfile. ++ DenseMap> FlattenSampleMap; ++ auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap, ++ &InstrProfileMap](const FunctionSamples &FS, ++ const StringRef &RootName) { ++ auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS, ++ const StringRef &RootName, ++ auto &BuildImpl) -> void { ++ const StringRef &Name = FS.getName(); ++ const StringRef *NewRootName = &RootName; ++ uint64_t EntrySample = FS.getHeadSamplesEstimate(); ++ uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true); ++ ++ auto It = InstrProfileMap.find(Name); ++ if (It != InstrProfileMap.end()) { ++ NewRootName = &Name; ++ } else { ++ auto NewName = StaticFuncMap.find(Name); ++ if (NewName != StaticFuncMap.end()) { ++ It = InstrProfileMap.find(NewName->second.str()); ++ if (NewName->second != DuplicateNameStr) { ++ NewRootName = &NewName->second; ++ } ++ } else { ++ // Here the EntrySample is of an inlined function, so we should not ++ // update the EntrySample in the map. ++ EntrySample = 0; ++ } ++ } ++ EntrySample += FlattenSampleMap[*NewRootName].first; ++ MaxBodySample = ++ std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample); ++ FlattenSampleMap[*NewRootName] = ++ std::make_pair(EntrySample, MaxBodySample); ++ ++ for (const auto &C : FS.getCallsiteSamples()) ++ for (const auto &F : C.second) ++ BuildImpl(F.second, *NewRootName, BuildImpl); ++ }; ++ BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl); ++ }; ++ ++ for (auto &PD : WC->Writer.getProfileData()) { ++ // Populate IPBuilder. ++ for (const auto &PDV : PD.getValue()) { ++ InstrProfRecord Record = PDV.second; ++ IPBuilder.addRecord(Record); ++ } ++ ++ // If a function has multiple entries in instr profile, skip it. ++ if (PD.getValue().size() != 1) ++ continue; ++ ++ // Initialize InstrProfileMap. ++ InstrProfRecord *R = &PD.getValue().begin()->second; ++ StringRef FullName = PD.getKey(); ++ InstrProfileMap[FullName] = InstrProfileEntry(R); ++ buildStaticFuncMap(FullName); ++ } ++ ++ for (auto &PD : Reader->getProfiles()) { ++ sampleprof::FunctionSamples &FS = PD.second; ++ BuildMaxSampleMap(FS, FS.getName()); ++ } ++ ++ ProfileSummary InstrPS = *IPBuilder.getSummary(); ++ ProfileSummary SamplePS = Reader->getSummary(); ++ ++ // Compute cold thresholds for instr profile and sample profile. ++ uint64_t HotSampleThreshold = ++ ProfileSummaryBuilder::getEntryForPercentile( ++ SamplePS.getDetailedSummary(), ++ ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) ++ .MinCount; ++ uint64_t ColdSampleThreshold = ++ ProfileSummaryBuilder::getEntryForPercentile( ++ SamplePS.getDetailedSummary(), ++ ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) ++ .MinCount; ++ uint64_t HotInstrThreshold = ++ ProfileSummaryBuilder::getEntryForPercentile( ++ InstrPS.getDetailedSummary(), ++ ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) ++ .MinCount; ++ uint64_t ColdInstrThreshold = ++ InstrProfColdThreshold ++ ? InstrProfColdThreshold ++ : ProfileSummaryBuilder::getEntryForPercentile( ++ InstrPS.getDetailedSummary(), ++ ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) ++ .MinCount; ++ ++ // Find hot/warm functions in sample profile which is cold in instr profile ++ // and adjust the profiles of those functions in the instr profile. ++ for (const auto &E : FlattenSampleMap) { ++ uint64_t SampleMaxCount = std::max(E.second.first, E.second.second); ++ if (SampleMaxCount < ColdSampleThreshold) ++ continue; ++ const StringRef &Name = E.first; ++ auto It = InstrProfileMap.find(Name); ++ if (It == InstrProfileMap.end()) { ++ auto NewName = StaticFuncMap.find(Name); ++ if (NewName != StaticFuncMap.end()) { ++ It = InstrProfileMap.find(NewName->second.str()); ++ if (NewName->second == DuplicateNameStr) { ++ WithColor::warning() ++ << "Static function " << Name ++ << " has multiple promoted names, cannot adjust profile.\n"; ++ } ++ } ++ } ++ if (It == InstrProfileMap.end() || ++ It->second.MaxCount > ColdInstrThreshold || ++ It->second.NumEdgeCounters < SupplMinSizeThreshold) ++ continue; ++ bool SetToHot = SampleMaxCount >= HotSampleThreshold; ++ updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold, ++ ColdInstrThreshold, ZeroCounterThreshold); ++ } ++} ++ ++/// The main function to supplement instr profile with sample profile. ++/// \Inputs contains the instr profile. \p SampleFilename specifies the ++/// sample profile. \p OutputFilename specifies the output profile name. ++/// \p OutputFormat specifies the output profile format. \p OutputSparse ++/// specifies whether to generate sparse profile. \p SupplMinSizeThreshold ++/// specifies the minimal size for the functions whose profile will be ++/// adjusted. \p ZeroCounterThreshold is the threshold to check whether ++/// a function contains too many zero counters and whether its profile ++/// should be dropped. \p InstrProfColdThreshold is the user specified ++/// cold threshold which will override the cold threshold got from the ++/// instr profile summary. ++static void supplementInstrProfile( ++ const WeightedFileVector &Inputs, StringRef SampleFilename, ++ StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, ++ unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, ++ unsigned InstrProfColdThreshold) { ++ if (OutputFilename.compare("-") == 0) ++ exitWithError("cannot write indexed profdata format to stdout"); ++ if (Inputs.size() != 1) ++ exitWithError("expect one input to be an instr profile"); ++ if (Inputs[0].Weight != 1) ++ exitWithError("expect instr profile doesn't have weight"); ++ ++ StringRef InstrFilename = Inputs[0].Filename; ++ ++ // Read sample profile. ++ LLVMContext Context; ++ auto ReaderOrErr = sampleprof::SampleProfileReader::create( ++ SampleFilename.str(), Context, FSDiscriminatorPassOption); ++ if (std::error_code EC = ReaderOrErr.getError()) ++ exitWithErrorCode(EC, SampleFilename); ++ auto Reader = std::move(ReaderOrErr.get()); ++ if (std::error_code EC = Reader->read()) ++ exitWithErrorCode(EC, SampleFilename); ++ ++ // Read instr profile. ++ std::mutex ErrorLock; ++ SmallSet WriterErrorCodes; ++ auto WC = std::make_unique(OutputSparse, ErrorLock, ++ WriterErrorCodes); ++ loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get()); ++ if (WC->Errors.size() > 0) ++ exitWithError(std::move(WC->Errors[0].first), InstrFilename); ++ ++ adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold, ++ InstrProfColdThreshold); ++ writeInstrProfile(OutputFilename, OutputFormat, WC->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()) { ++ uint32_t MaskedDiscriminator = ++ BodySample.first.Discriminator & getDiscriminatorMask(); ++ Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator, ++ BodySample.second.getSamples()); ++ for (const auto &Target : BodySample.second.getCallTargets()) { ++ Result.addCalledTargetSamples(BodySample.first.LineOffset, ++ MaskedDiscriminator, ++ 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 SymbolStr : SymbolVec) ++ PSL.add(SymbolStr.trim()); ++} ++ ++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, bool GenCSNestedProfile, ++ bool SampleMergeColdContext, bool SampleTrimColdContext, ++ bool SampleColdContextFrameDepth, FailureMode FailMode, ++ bool DropProfileSymbolList) { ++ using namespace sampleprof; ++ SampleProfileMap ProfileMap; ++ SmallVector, 5> Readers; ++ LLVMContext Context; ++ sampleprof::ProfileSymbolList WriterList; ++ std::optional ProfileIsProbeBased; ++ std::optional ProfileIsCS; ++ for (const auto &Input : Inputs) { ++ auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, ++ FSDiscriminatorPassOption); ++ 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; ++ } ++ ++ SampleProfileMap &Profiles = Reader->getProfiles(); ++ if (ProfileIsProbeBased && ++ ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased) ++ exitWithError( ++ "cannot merge probe-based profile with non-probe-based profile"); ++ ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased; ++ if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS) ++ exitWithError("cannot merge CS profile with non-CS profile"); ++ ProfileIsCS = FunctionSamples::ProfileIsCS; ++ for (SampleProfileMap::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; ++ SampleContext FContext = Samples.getContext(); ++ MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight)); ++ if (Result != sampleprof_error::success) { ++ std::error_code EC = make_error_code(Result); ++ handleMergeWriterError(errorCodeToError(EC), Input.Filename, ++ FContext.toString()); ++ } ++ } ++ ++ if (!DropProfileSymbolList) { ++ std::unique_ptr ReaderList = ++ Reader->getProfileSymbolList(); ++ if (ReaderList) ++ WriterList.merge(*ReaderList); ++ } ++ } ++ ++ if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) { ++ // Use threshold calculated from profile summary unless specified. ++ SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); ++ auto Summary = Builder.computeSummaryForProfiles(ProfileMap); ++ uint64_t SampleProfColdThreshold = ++ ProfileSummaryBuilder::getColdCountThreshold( ++ (Summary->getDetailedSummary())); ++ ++ // Trim and merge cold context profile using cold threshold above; ++ SampleContextTrimmer(ProfileMap) ++ .trimAndMergeColdContextProfiles( ++ SampleProfColdThreshold, SampleTrimColdContext, ++ SampleMergeColdContext, SampleColdContextFrameDepth, false); ++ } ++ ++ if (ProfileIsCS && GenCSNestedProfile) { ++ CSProfileConverter CSConverter(ProfileMap); ++ CSConverter.convertProfiles(); ++ ProfileIsCS = FunctionSamples::ProfileIsCS = false; ++ } ++ ++ 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); ++ if (std::error_code EC = Writer->write(ProfileMap)) ++ exitWithErrorCode(std::move(EC)); ++} ++ ++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.contains(',')) ++ 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::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 SampleMergeColdContext( ++ "sample-merge-cold-context", cl::init(false), cl::Hidden, ++ cl::desc( ++ "Merge context sample profiles whose count is below cold threshold")); ++ cl::opt SampleTrimColdContext( ++ "sample-trim-cold-context", cl::init(false), cl::Hidden, ++ cl::desc( ++ "Trim context sample profiles whose count is below cold threshold")); ++ cl::opt SampleColdContextFrameDepth( ++ "sample-frame-depth-for-cold-context", cl::init(1), ++ cl::desc("Keep the last K frames while merging cold profile. 1 means the " ++ "context-less base profile")); ++ cl::opt GenPartialProfile( ++ "gen-partial-profile", cl::init(false), cl::Hidden, ++ cl::desc("Generate a partial profile (only meaningful for -extbinary)")); ++ cl::opt SupplInstrWithSample( ++ "supplement-instr-with-sample", cl::init(""), cl::Hidden, ++ cl::desc("Supplement an instr profile with sample profile, to correct " ++ "the profile unrepresentativeness issue. The sample " ++ "profile is the input of the flag. Output will be in instr " ++ "format (The flag only works with -instr)")); ++ cl::opt ZeroCounterThreshold( ++ "zero-counter-threshold", cl::init(0.7), cl::Hidden, ++ cl::desc("For the function which is cold in instr profile but hot in " ++ "sample profile, if the ratio of the number of zero counters " ++ "divided by the total number of counters is above the " ++ "threshold, the profile of the function will be regarded as " ++ "being harmful for performance and will be dropped.")); ++ cl::opt SupplMinSizeThreshold( ++ "suppl-min-size-threshold", cl::init(10), cl::Hidden, ++ cl::desc("If the size of a function is smaller than the threshold, " ++ "assume it can be inlined by PGO early inliner and it won't " ++ "be adjusted based on sample profile.")); ++ cl::opt InstrProfColdThreshold( ++ "instr-prof-cold-threshold", cl::init(0), cl::Hidden, ++ cl::desc("User specified cold threshold for instr profile which will " ++ "override the cold threshold got from profile summary. ")); ++ cl::opt GenCSNestedProfile( ++ "gen-cs-nested-profile", cl::Hidden, cl::init(false), ++ cl::desc("Generate nested function profiles for CSSPGO")); ++ cl::opt DebugInfoFilename( ++ "debug-info", cl::init(""), ++ cl::desc("Use the provided debug info to correlate the raw profile.")); ++ cl::opt ProfiledBinary( ++ "profiled-binary", cl::init(""), ++ cl::desc("Path to binary from which the profile was collected.")); ++ cl::opt DropProfileSymbolList( ++ "drop-profile-symbol-list", cl::init(false), cl::Hidden, ++ cl::desc("Drop the profile symbol list when merging AutoFDO profiles " ++ "(only meaningful for -sample)")); ++ ++ 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 (!SupplInstrWithSample.empty()) { ++ if (ProfileKind != instr) ++ exitWithError( ++ "-supplement-instr-with-sample can only work with -instr. "); ++ ++ supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, ++ OutputFormat, OutputSparse, SupplMinSizeThreshold, ++ ZeroCounterThreshold, InstrProfColdThreshold); ++ return 0; ++ } ++ ++ if (ProfileKind == instr) ++ mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(), ++ OutputFilename, OutputFormat, OutputSparse, NumThreads, ++ FailureMode, ProfiledBinary); ++ else ++ mergeSampleProfile( ++ WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, ++ ProfileSymbolListFile, CompressAllSections, UseMD5, GenPartialProfile, ++ GenCSNestedProfile, SampleMergeColdContext, SampleTrimColdContext, ++ SampleColdContextFrameDepth, FailureMode, DropProfileSymbolList); ++ 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, nullptr, /*ProfiledBinary=*/"", &Context); ++ overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, ++ IsCS); ++ Overlap.dump(OS); ++} ++ ++namespace { ++struct SampleOverlapStats { ++ SampleContext BaseName; ++ SampleContext TestName; ++ // Number of overlap units ++ uint64_t OverlapCount; ++ // Total samples of overlap units ++ uint64_t OverlapSample; ++ // Number of and total samples of units that only present in base or test ++ // profile ++ uint64_t BaseUniqueCount; ++ uint64_t BaseUniqueSample; ++ uint64_t TestUniqueCount; ++ uint64_t TestUniqueSample; ++ // Number of units and total samples in base or test profile ++ uint64_t BaseCount; ++ uint64_t BaseSample; ++ uint64_t TestCount; ++ uint64_t TestSample; ++ // Number of and total samples of units that present in at least one profile ++ uint64_t UnionCount; ++ uint64_t UnionSample; ++ // Weighted similarity ++ double Similarity; ++ // For SampleOverlapStats instances representing functions, weights of the ++ // function in base and test profiles ++ double BaseWeight; ++ double TestWeight; ++ ++ SampleOverlapStats() ++ : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0), ++ BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0), ++ BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0), ++ UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {} ++}; ++} // end anonymous namespace ++ ++namespace { ++struct FuncSampleStats { ++ uint64_t SampleSum; ++ uint64_t MaxSample; ++ uint64_t HotBlockCount; ++ FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {} ++ FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample, ++ uint64_t HotBlockCount) ++ : SampleSum(SampleSum), MaxSample(MaxSample), ++ HotBlockCount(HotBlockCount) {} ++}; ++} // end anonymous namespace ++ ++namespace { ++enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None }; ++ ++// Class for updating merging steps for two sorted maps. The class should be ++// instantiated with a map iterator type. ++template class MatchStep { ++public: ++ MatchStep() = delete; ++ ++ MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd) ++ : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter), ++ SecondEnd(SecondEnd), Status(MS_None) {} ++ ++ bool areBothFinished() const { ++ return (FirstIter == FirstEnd && SecondIter == SecondEnd); ++ } ++ ++ bool isFirstFinished() const { return FirstIter == FirstEnd; } ++ ++ bool isSecondFinished() const { return SecondIter == SecondEnd; } ++ ++ /// Advance one step based on the previous match status unless the previous ++ /// status is MS_None. Then update Status based on the comparison between two ++ /// container iterators at the current step. If the previous status is ++ /// MS_None, it means two iterators are at the beginning and no comparison has ++ /// been made, so we simply update Status without advancing the iterators. ++ void updateOneStep(); ++ ++ T getFirstIter() const { return FirstIter; } ++ ++ T getSecondIter() const { return SecondIter; } ++ ++ MatchStatus getMatchStatus() const { return Status; } ++ ++private: ++ // Current iterator and end iterator of the first container. ++ T FirstIter; ++ T FirstEnd; ++ // Current iterator and end iterator of the second container. ++ T SecondIter; ++ T SecondEnd; ++ // Match status of the current step. ++ MatchStatus Status; ++}; ++} // end anonymous namespace ++ ++template void MatchStep::updateOneStep() { ++ switch (Status) { ++ case MS_Match: ++ ++FirstIter; ++ ++SecondIter; ++ break; ++ case MS_FirstUnique: ++ ++FirstIter; ++ break; ++ case MS_SecondUnique: ++ ++SecondIter; ++ break; ++ case MS_None: ++ break; ++ } ++ ++ // Update Status according to iterators at the current step. ++ if (areBothFinished()) ++ return; ++ if (FirstIter != FirstEnd && ++ (SecondIter == SecondEnd || FirstIter->first < SecondIter->first)) ++ Status = MS_FirstUnique; ++ else if (SecondIter != SecondEnd && ++ (FirstIter == FirstEnd || SecondIter->first < FirstIter->first)) ++ Status = MS_SecondUnique; ++ else ++ Status = MS_Match; ++} ++ ++// Return the sum of line/block samples, the max line/block sample, and the ++// number of line/block samples above the given threshold in a function ++// including its inlinees. ++static void getFuncSampleStats(const sampleprof::FunctionSamples &Func, ++ FuncSampleStats &FuncStats, ++ uint64_t HotThreshold) { ++ for (const auto &L : Func.getBodySamples()) { ++ uint64_t Sample = L.second.getSamples(); ++ FuncStats.SampleSum += Sample; ++ FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample); ++ if (Sample >= HotThreshold) ++ ++FuncStats.HotBlockCount; ++ } ++ ++ for (const auto &C : Func.getCallsiteSamples()) { ++ for (const auto &F : C.second) ++ getFuncSampleStats(F.second, FuncStats, HotThreshold); ++ } ++} ++ ++/// Predicate that determines if a function is hot with a given threshold. We ++/// keep it separate from its callsites for possible extension in the future. ++static bool isFunctionHot(const FuncSampleStats &FuncStats, ++ uint64_t HotThreshold) { ++ // We intentionally compare the maximum sample count in a function with the ++ // HotThreshold to get an approximate determination on hot functions. ++ return (FuncStats.MaxSample >= HotThreshold); ++} ++ ++namespace { ++class SampleOverlapAggregator { ++public: ++ SampleOverlapAggregator(const std::string &BaseFilename, ++ const std::string &TestFilename, ++ double LowSimilarityThreshold, double Epsilon, ++ const OverlapFuncFilters &FuncFilter) ++ : BaseFilename(BaseFilename), TestFilename(TestFilename), ++ LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon), ++ FuncFilter(FuncFilter) {} ++ ++ /// Detect 0-sample input profile and report to output stream. This interface ++ /// should be called after loadProfiles(). ++ bool detectZeroSampleProfile(raw_fd_ostream &OS) const; ++ ++ /// Write out function-level similarity statistics for functions specified by ++ /// options --function, --value-cutoff, and --similarity-cutoff. ++ void dumpFuncSimilarity(raw_fd_ostream &OS) const; ++ ++ /// Write out program-level similarity and overlap statistics. ++ void dumpProgramSummary(raw_fd_ostream &OS) const; ++ ++ /// Write out hot-function and hot-block statistics for base_profile, ++ /// test_profile, and their overlap. For both cases, the overlap HO is ++ /// calculated as follows: ++ /// Given the number of functions (or blocks) that are hot in both profiles ++ /// HCommon and the number of functions (or blocks) that are hot in at ++ /// least one profile HUnion, HO = HCommon / HUnion. ++ void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const; ++ ++ /// This function tries matching functions in base and test profiles. For each ++ /// pair of matched functions, it aggregates the function-level ++ /// similarity into a profile-level similarity. It also dump function-level ++ /// similarity information of functions specified by --function, ++ /// --value-cutoff, and --similarity-cutoff options. The program-level ++ /// similarity PS is computed as follows: ++ /// Given function-level similarity FS(A) for all function A, the ++ /// weight of function A in base profile WB(A), and the weight of function ++ /// A in test profile WT(A), compute PS(base_profile, test_profile) = ++ /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0 ++ /// meaning no-overlap. ++ void computeSampleProfileOverlap(raw_fd_ostream &OS); ++ ++ /// Initialize ProfOverlap with the sum of samples in base and test ++ /// profiles. This function also computes and keeps the sum of samples and ++ /// max sample counts of each function in BaseStats and TestStats for later ++ /// use to avoid re-computations. ++ void initializeSampleProfileOverlap(); ++ ++ /// Load profiles specified by BaseFilename and TestFilename. ++ std::error_code loadProfiles(); ++ ++ using FuncSampleStatsMap = ++ std::unordered_map; ++ ++private: ++ SampleOverlapStats ProfOverlap; ++ SampleOverlapStats HotFuncOverlap; ++ SampleOverlapStats HotBlockOverlap; ++ std::string BaseFilename; ++ std::string TestFilename; ++ std::unique_ptr BaseReader; ++ std::unique_ptr TestReader; ++ // BaseStats and TestStats hold FuncSampleStats for each function, with ++ // function name as the key. ++ FuncSampleStatsMap BaseStats; ++ FuncSampleStatsMap TestStats; ++ // Low similarity threshold in floating point number ++ double LowSimilarityThreshold; ++ // Block samples above BaseHotThreshold or TestHotThreshold are considered hot ++ // for tracking hot blocks. ++ uint64_t BaseHotThreshold; ++ uint64_t TestHotThreshold; ++ // A small threshold used to round the results of floating point accumulations ++ // to resolve imprecision. ++ const double Epsilon; ++ std::multimap> ++ FuncSimilarityDump; ++ // FuncFilter carries specifications in options --value-cutoff and ++ // --function. ++ OverlapFuncFilters FuncFilter; ++ // Column offsets for printing the function-level details table. ++ static const unsigned int TestWeightCol = 15; ++ static const unsigned int SimilarityCol = 30; ++ static const unsigned int OverlapCol = 43; ++ static const unsigned int BaseUniqueCol = 53; ++ static const unsigned int TestUniqueCol = 67; ++ static const unsigned int BaseSampleCol = 81; ++ static const unsigned int TestSampleCol = 96; ++ static const unsigned int FuncNameCol = 111; ++ ++ /// Return a similarity of two line/block sample counters in the same ++ /// function in base and test profiles. The line/block-similarity BS(i) is ++ /// computed as follows: ++ /// For an offsets i, given the sample count at i in base profile BB(i), ++ /// the sample count at i in test profile BT(i), the sum of sample counts ++ /// in this function in base profile SB, and the sum of sample counts in ++ /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB - ++ /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap. ++ double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample, ++ const SampleOverlapStats &FuncOverlap) const; ++ ++ void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample, ++ uint64_t HotBlockCount); ++ ++ void getHotFunctions(const FuncSampleStatsMap &ProfStats, ++ FuncSampleStatsMap &HotFunc, ++ uint64_t HotThreshold) const; ++ ++ void computeHotFuncOverlap(); ++ ++ /// This function updates statistics in FuncOverlap, HotBlockOverlap, and ++ /// Difference for two sample units in a matched function according to the ++ /// given match status. ++ void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample, ++ uint64_t HotBlockCount, ++ SampleOverlapStats &FuncOverlap, ++ double &Difference, MatchStatus Status); ++ ++ /// This function updates statistics in FuncOverlap, HotBlockOverlap, and ++ /// Difference for unmatched callees that only present in one profile in a ++ /// matched caller function. ++ void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func, ++ SampleOverlapStats &FuncOverlap, ++ double &Difference, MatchStatus Status); ++ ++ /// This function updates sample overlap statistics of an overlap function in ++ /// base and test profile. It also calculates a function-internal similarity ++ /// FIS as follows: ++ /// For offsets i that have samples in at least one profile in this ++ /// function A, given BS(i) returned by computeBlockSimilarity(), compute ++ /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with ++ /// 0.0 meaning no overlap. ++ double computeSampleFunctionInternalOverlap( ++ const sampleprof::FunctionSamples &BaseFunc, ++ const sampleprof::FunctionSamples &TestFunc, ++ SampleOverlapStats &FuncOverlap); ++ ++ /// Function-level similarity (FS) is a weighted value over function internal ++ /// similarity (FIS). This function computes a function's FS from its FIS by ++ /// applying the weight. ++ double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample, ++ uint64_t TestFuncSample) const; ++ ++ /// The function-level similarity FS(A) for a function A is computed as ++ /// follows: ++ /// Compute a function-internal similarity FIS(A) by ++ /// computeSampleFunctionInternalOverlap(). Then, with the weight of ++ /// function A in base profile WB(A), and the weight of function A in test ++ /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A))) ++ /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap. ++ double ++ computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc, ++ const sampleprof::FunctionSamples *TestFunc, ++ SampleOverlapStats *FuncOverlap, ++ uint64_t BaseFuncSample, ++ uint64_t TestFuncSample); ++ ++ /// Profile-level similarity (PS) is a weighted aggregate over function-level ++ /// similarities (FS). This method weights the FS value by the function ++ /// weights in the base and test profiles for the aggregation. ++ double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample, ++ uint64_t TestFuncSample) const; ++}; ++} // end anonymous namespace ++ ++bool SampleOverlapAggregator::detectZeroSampleProfile( ++ raw_fd_ostream &OS) const { ++ bool HaveZeroSample = false; ++ if (ProfOverlap.BaseSample == 0) { ++ OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n"; ++ HaveZeroSample = true; ++ } ++ if (ProfOverlap.TestSample == 0) { ++ OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n"; ++ HaveZeroSample = true; ++ } ++ return HaveZeroSample; ++} ++ ++double SampleOverlapAggregator::computeBlockSimilarity( ++ uint64_t BaseSample, uint64_t TestSample, ++ const SampleOverlapStats &FuncOverlap) const { ++ double BaseFrac = 0.0; ++ double TestFrac = 0.0; ++ if (FuncOverlap.BaseSample > 0) ++ BaseFrac = static_cast(BaseSample) / FuncOverlap.BaseSample; ++ if (FuncOverlap.TestSample > 0) ++ TestFrac = static_cast(TestSample) / FuncOverlap.TestSample; ++ return 1.0 - std::fabs(BaseFrac - TestFrac); ++} ++ ++void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample, ++ uint64_t TestSample, ++ uint64_t HotBlockCount) { ++ bool IsBaseHot = (BaseSample >= BaseHotThreshold); ++ bool IsTestHot = (TestSample >= TestHotThreshold); ++ if (!IsBaseHot && !IsTestHot) ++ return; ++ ++ HotBlockOverlap.UnionCount += HotBlockCount; ++ if (IsBaseHot) ++ HotBlockOverlap.BaseCount += HotBlockCount; ++ if (IsTestHot) ++ HotBlockOverlap.TestCount += HotBlockCount; ++ if (IsBaseHot && IsTestHot) ++ HotBlockOverlap.OverlapCount += HotBlockCount; ++} ++ ++void SampleOverlapAggregator::getHotFunctions( ++ const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc, ++ uint64_t HotThreshold) const { ++ for (const auto &F : ProfStats) { ++ if (isFunctionHot(F.second, HotThreshold)) ++ HotFunc.emplace(F.first, F.second); ++ } ++} ++ ++void SampleOverlapAggregator::computeHotFuncOverlap() { ++ FuncSampleStatsMap BaseHotFunc; ++ getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold); ++ HotFuncOverlap.BaseCount = BaseHotFunc.size(); ++ ++ FuncSampleStatsMap TestHotFunc; ++ getHotFunctions(TestStats, TestHotFunc, TestHotThreshold); ++ HotFuncOverlap.TestCount = TestHotFunc.size(); ++ HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount; ++ ++ for (const auto &F : BaseHotFunc) { ++ if (TestHotFunc.count(F.first)) ++ ++HotFuncOverlap.OverlapCount; ++ else ++ ++HotFuncOverlap.UnionCount; ++ } ++} ++ ++void SampleOverlapAggregator::updateOverlapStatsForFunction( ++ uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount, ++ SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) { ++ assert(Status != MS_None && ++ "Match status should be updated before updating overlap statistics"); ++ if (Status == MS_FirstUnique) { ++ TestSample = 0; ++ FuncOverlap.BaseUniqueSample += BaseSample; ++ } else if (Status == MS_SecondUnique) { ++ BaseSample = 0; ++ FuncOverlap.TestUniqueSample += TestSample; ++ } else { ++ ++FuncOverlap.OverlapCount; ++ } ++ ++ FuncOverlap.UnionSample += std::max(BaseSample, TestSample); ++ FuncOverlap.OverlapSample += std::min(BaseSample, TestSample); ++ Difference += ++ 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap); ++ updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount); ++} ++ ++void SampleOverlapAggregator::updateForUnmatchedCallee( ++ const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap, ++ double &Difference, MatchStatus Status) { ++ assert((Status == MS_FirstUnique || Status == MS_SecondUnique) && ++ "Status must be either of the two unmatched cases"); ++ FuncSampleStats FuncStats; ++ if (Status == MS_FirstUnique) { ++ getFuncSampleStats(Func, FuncStats, BaseHotThreshold); ++ updateOverlapStatsForFunction(FuncStats.SampleSum, 0, ++ FuncStats.HotBlockCount, FuncOverlap, ++ Difference, Status); ++ } else { ++ getFuncSampleStats(Func, FuncStats, TestHotThreshold); ++ updateOverlapStatsForFunction(0, FuncStats.SampleSum, ++ FuncStats.HotBlockCount, FuncOverlap, ++ Difference, Status); ++ } ++} ++ ++double SampleOverlapAggregator::computeSampleFunctionInternalOverlap( ++ const sampleprof::FunctionSamples &BaseFunc, ++ const sampleprof::FunctionSamples &TestFunc, ++ SampleOverlapStats &FuncOverlap) { ++ ++ using namespace sampleprof; ++ ++ double Difference = 0; ++ ++ // Accumulate Difference for regular line/block samples in the function. ++ // We match them through sort-merge join algorithm because ++ // FunctionSamples::getBodySamples() returns a map of sample counters ordered ++ // by their offsets. ++ MatchStep BlockIterStep( ++ BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(), ++ TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend()); ++ BlockIterStep.updateOneStep(); ++ while (!BlockIterStep.areBothFinished()) { ++ uint64_t BaseSample = ++ BlockIterStep.isFirstFinished() ++ ? 0 ++ : BlockIterStep.getFirstIter()->second.getSamples(); ++ uint64_t TestSample = ++ BlockIterStep.isSecondFinished() ++ ? 0 ++ : BlockIterStep.getSecondIter()->second.getSamples(); ++ updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap, ++ Difference, BlockIterStep.getMatchStatus()); ++ ++ BlockIterStep.updateOneStep(); ++ } ++ ++ // Accumulate Difference for callsite lines in the function. We match ++ // them through sort-merge algorithm because ++ // FunctionSamples::getCallsiteSamples() returns a map of callsite records ++ // ordered by their offsets. ++ MatchStep CallsiteIterStep( ++ BaseFunc.getCallsiteSamples().cbegin(), ++ BaseFunc.getCallsiteSamples().cend(), ++ TestFunc.getCallsiteSamples().cbegin(), ++ TestFunc.getCallsiteSamples().cend()); ++ CallsiteIterStep.updateOneStep(); ++ while (!CallsiteIterStep.areBothFinished()) { ++ MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus(); ++ assert(CallsiteStepStatus != MS_None && ++ "Match status should be updated before entering loop body"); ++ ++ if (CallsiteStepStatus != MS_Match) { ++ auto Callsite = (CallsiteStepStatus == MS_FirstUnique) ++ ? CallsiteIterStep.getFirstIter() ++ : CallsiteIterStep.getSecondIter(); ++ for (const auto &F : Callsite->second) ++ updateForUnmatchedCallee(F.second, FuncOverlap, Difference, ++ CallsiteStepStatus); ++ } else { ++ // There may be multiple inlinees at the same offset, so we need to try ++ // matching all of them. This match is implemented through sort-merge ++ // algorithm because callsite records at the same offset are ordered by ++ // function names. ++ MatchStep CalleeIterStep( ++ CallsiteIterStep.getFirstIter()->second.cbegin(), ++ CallsiteIterStep.getFirstIter()->second.cend(), ++ CallsiteIterStep.getSecondIter()->second.cbegin(), ++ CallsiteIterStep.getSecondIter()->second.cend()); ++ CalleeIterStep.updateOneStep(); ++ while (!CalleeIterStep.areBothFinished()) { ++ MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus(); ++ if (CalleeStepStatus != MS_Match) { ++ auto Callee = (CalleeStepStatus == MS_FirstUnique) ++ ? CalleeIterStep.getFirstIter() ++ : CalleeIterStep.getSecondIter(); ++ updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference, ++ CalleeStepStatus); ++ } else { ++ // An inlined function can contain other inlinees inside, so compute ++ // the Difference recursively. ++ Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap( ++ CalleeIterStep.getFirstIter()->second, ++ CalleeIterStep.getSecondIter()->second, ++ FuncOverlap); ++ } ++ CalleeIterStep.updateOneStep(); ++ } ++ } ++ CallsiteIterStep.updateOneStep(); ++ } ++ ++ // Difference reflects the total differences of line/block samples in this ++ // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to ++ // reflect the similarity between function profiles in [0.0f to 1.0f]. ++ return (2.0 - Difference) / 2; ++} ++ ++double SampleOverlapAggregator::weightForFuncSimilarity( ++ double FuncInternalSimilarity, uint64_t BaseFuncSample, ++ uint64_t TestFuncSample) const { ++ // Compute the weight as the distance between the function weights in two ++ // profiles. ++ double BaseFrac = 0.0; ++ double TestFrac = 0.0; ++ assert(ProfOverlap.BaseSample > 0 && ++ "Total samples in base profile should be greater than 0"); ++ BaseFrac = static_cast(BaseFuncSample) / ProfOverlap.BaseSample; ++ assert(ProfOverlap.TestSample > 0 && ++ "Total samples in test profile should be greater than 0"); ++ TestFrac = static_cast(TestFuncSample) / ProfOverlap.TestSample; ++ double WeightDistance = std::fabs(BaseFrac - TestFrac); ++ ++ // Take WeightDistance into the similarity. ++ return FuncInternalSimilarity * (1 - WeightDistance); ++} ++ ++double ++SampleOverlapAggregator::weightByImportance(double FuncSimilarity, ++ uint64_t BaseFuncSample, ++ uint64_t TestFuncSample) const { ++ ++ double BaseFrac = 0.0; ++ double TestFrac = 0.0; ++ assert(ProfOverlap.BaseSample > 0 && ++ "Total samples in base profile should be greater than 0"); ++ BaseFrac = static_cast(BaseFuncSample) / ProfOverlap.BaseSample / 2.0; ++ assert(ProfOverlap.TestSample > 0 && ++ "Total samples in test profile should be greater than 0"); ++ TestFrac = static_cast(TestFuncSample) / ProfOverlap.TestSample / 2.0; ++ return FuncSimilarity * (BaseFrac + TestFrac); ++} ++ ++double SampleOverlapAggregator::computeSampleFunctionOverlap( ++ const sampleprof::FunctionSamples *BaseFunc, ++ const sampleprof::FunctionSamples *TestFunc, ++ SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample, ++ uint64_t TestFuncSample) { ++ // Default function internal similarity before weighted, meaning two functions ++ // has no overlap. ++ const double DefaultFuncInternalSimilarity = 0; ++ double FuncSimilarity; ++ double FuncInternalSimilarity; ++ ++ // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap. ++ // In this case, we use DefaultFuncInternalSimilarity as the function internal ++ // similarity. ++ if (!BaseFunc || !TestFunc) { ++ FuncInternalSimilarity = DefaultFuncInternalSimilarity; ++ } else { ++ assert(FuncOverlap != nullptr && ++ "FuncOverlap should be provided in this case"); ++ FuncInternalSimilarity = computeSampleFunctionInternalOverlap( ++ *BaseFunc, *TestFunc, *FuncOverlap); ++ // Now, FuncInternalSimilarity may be a little less than 0 due to ++ // imprecision of floating point accumulations. Make it zero if the ++ // difference is below Epsilon. ++ FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon) ++ ? 0 ++ : FuncInternalSimilarity; ++ } ++ FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity, ++ BaseFuncSample, TestFuncSample); ++ return FuncSimilarity; ++} ++ ++void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { ++ using namespace sampleprof; ++ ++ std::unordered_map ++ BaseFuncProf; ++ const auto &BaseProfiles = BaseReader->getProfiles(); ++ for (const auto &BaseFunc : BaseProfiles) { ++ BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second)); ++ } ++ ProfOverlap.UnionCount = BaseFuncProf.size(); ++ ++ const auto &TestProfiles = TestReader->getProfiles(); ++ for (const auto &TestFunc : TestProfiles) { ++ SampleOverlapStats FuncOverlap; ++ FuncOverlap.TestName = TestFunc.second.getContext(); ++ assert(TestStats.count(FuncOverlap.TestName) && ++ "TestStats should have records for all functions in test profile " ++ "except inlinees"); ++ FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum; ++ ++ bool Matched = false; ++ const auto Match = BaseFuncProf.find(FuncOverlap.TestName); ++ if (Match == BaseFuncProf.end()) { ++ const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName]; ++ ++ProfOverlap.TestUniqueCount; ++ ProfOverlap.TestUniqueSample += FuncStats.SampleSum; ++ FuncOverlap.TestUniqueSample = FuncStats.SampleSum; ++ ++ updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount); ++ ++ double FuncSimilarity = computeSampleFunctionOverlap( ++ nullptr, nullptr, nullptr, 0, FuncStats.SampleSum); ++ ProfOverlap.Similarity += ++ weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum); ++ ++ ++ProfOverlap.UnionCount; ++ ProfOverlap.UnionSample += FuncStats.SampleSum; ++ } else { ++ ++ProfOverlap.OverlapCount; ++ ++ // Two functions match with each other. Compute function-level overlap and ++ // aggregate them into profile-level overlap. ++ FuncOverlap.BaseName = Match->second->getContext(); ++ assert(BaseStats.count(FuncOverlap.BaseName) && ++ "BaseStats should have records for all functions in base profile " ++ "except inlinees"); ++ FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum; ++ ++ FuncOverlap.Similarity = computeSampleFunctionOverlap( ++ Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample, ++ FuncOverlap.TestSample); ++ ProfOverlap.Similarity += ++ weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample, ++ FuncOverlap.TestSample); ++ ProfOverlap.OverlapSample += FuncOverlap.OverlapSample; ++ ProfOverlap.UnionSample += FuncOverlap.UnionSample; ++ ++ // Accumulate the percentage of base unique and test unique samples into ++ // ProfOverlap. ++ ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample; ++ ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample; ++ ++ // Remove matched base functions for later reporting functions not found ++ // in test profile. ++ BaseFuncProf.erase(Match); ++ Matched = true; ++ } ++ ++ // Print function-level similarity information if specified by options. ++ assert(TestStats.count(FuncOverlap.TestName) && ++ "TestStats should have records for all functions in test profile " ++ "except inlinees"); ++ if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff || ++ (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) || ++ (Matched && !FuncFilter.NameFilter.empty() && ++ FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) != ++ std::string::npos)) { ++ assert(ProfOverlap.BaseSample > 0 && ++ "Total samples in base profile should be greater than 0"); ++ FuncOverlap.BaseWeight = ++ static_cast(FuncOverlap.BaseSample) / ProfOverlap.BaseSample; ++ assert(ProfOverlap.TestSample > 0 && ++ "Total samples in test profile should be greater than 0"); ++ FuncOverlap.TestWeight = ++ static_cast(FuncOverlap.TestSample) / ProfOverlap.TestSample; ++ FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap); ++ } ++ } ++ ++ // Traverse through functions in base profile but not in test profile. ++ for (const auto &F : BaseFuncProf) { ++ assert(BaseStats.count(F.second->getContext()) && ++ "BaseStats should have records for all functions in base profile " ++ "except inlinees"); ++ const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()]; ++ ++ProfOverlap.BaseUniqueCount; ++ ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; ++ ++ updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount); ++ ++ double FuncSimilarity = computeSampleFunctionOverlap( ++ nullptr, nullptr, nullptr, FuncStats.SampleSum, 0); ++ ProfOverlap.Similarity += ++ weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0); ++ ++ ProfOverlap.UnionSample += FuncStats.SampleSum; ++ } ++ ++ // Now, ProfSimilarity may be a little greater than 1 due to imprecision ++ // of floating point accumulations. Make it 1.0 if the difference is below ++ // Epsilon. ++ ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon) ++ ? 1 ++ : ProfOverlap.Similarity; ++ ++ computeHotFuncOverlap(); ++} ++ ++void SampleOverlapAggregator::initializeSampleProfileOverlap() { ++ const auto &BaseProf = BaseReader->getProfiles(); ++ for (const auto &I : BaseProf) { ++ ++ProfOverlap.BaseCount; ++ FuncSampleStats FuncStats; ++ getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); ++ ProfOverlap.BaseSample += FuncStats.SampleSum; ++ BaseStats.emplace(I.second.getContext(), FuncStats); ++ } ++ ++ const auto &TestProf = TestReader->getProfiles(); ++ for (const auto &I : TestProf) { ++ ++ProfOverlap.TestCount; ++ FuncSampleStats FuncStats; ++ getFuncSampleStats(I.second, FuncStats, TestHotThreshold); ++ ProfOverlap.TestSample += FuncStats.SampleSum; ++ TestStats.emplace(I.second.getContext(), FuncStats); ++ } ++ ++ ProfOverlap.BaseName = StringRef(BaseFilename); ++ ProfOverlap.TestName = StringRef(TestFilename); ++} ++ ++void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const { ++ using namespace sampleprof; ++ ++ if (FuncSimilarityDump.empty()) ++ return; ++ ++ formatted_raw_ostream FOS(OS); ++ FOS << "Function-level details:\n"; ++ FOS << "Base weight"; ++ FOS.PadToColumn(TestWeightCol); ++ FOS << "Test weight"; ++ FOS.PadToColumn(SimilarityCol); ++ FOS << "Similarity"; ++ FOS.PadToColumn(OverlapCol); ++ FOS << "Overlap"; ++ FOS.PadToColumn(BaseUniqueCol); ++ FOS << "Base unique"; ++ FOS.PadToColumn(TestUniqueCol); ++ FOS << "Test unique"; ++ FOS.PadToColumn(BaseSampleCol); ++ FOS << "Base samples"; ++ FOS.PadToColumn(TestSampleCol); ++ FOS << "Test samples"; ++ FOS.PadToColumn(FuncNameCol); ++ FOS << "Function name\n"; ++ for (const auto &F : FuncSimilarityDump) { ++ double OverlapPercent = ++ F.second.UnionSample > 0 ++ ? static_cast(F.second.OverlapSample) / F.second.UnionSample ++ : 0; ++ double BaseUniquePercent = ++ F.second.BaseSample > 0 ++ ? static_cast(F.second.BaseUniqueSample) / ++ F.second.BaseSample ++ : 0; ++ double TestUniquePercent = ++ F.second.TestSample > 0 ++ ? static_cast(F.second.TestUniqueSample) / ++ F.second.TestSample ++ : 0; ++ ++ FOS << format("%.2f%%", F.second.BaseWeight * 100); ++ FOS.PadToColumn(TestWeightCol); ++ FOS << format("%.2f%%", F.second.TestWeight * 100); ++ FOS.PadToColumn(SimilarityCol); ++ FOS << format("%.2f%%", F.second.Similarity * 100); ++ FOS.PadToColumn(OverlapCol); ++ FOS << format("%.2f%%", OverlapPercent * 100); ++ FOS.PadToColumn(BaseUniqueCol); ++ FOS << format("%.2f%%", BaseUniquePercent * 100); ++ FOS.PadToColumn(TestUniqueCol); ++ FOS << format("%.2f%%", TestUniquePercent * 100); ++ FOS.PadToColumn(BaseSampleCol); ++ FOS << F.second.BaseSample; ++ FOS.PadToColumn(TestSampleCol); ++ FOS << F.second.TestSample; ++ FOS.PadToColumn(FuncNameCol); ++ FOS << F.second.TestName.toString() << "\n"; ++ } ++} ++ ++void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const { ++ OS << "Profile overlap infomation for base_profile: " ++ << ProfOverlap.BaseName.toString() ++ << " and test_profile: " << ProfOverlap.TestName.toString() ++ << "\nProgram level:\n"; ++ ++ OS << " Whole program profile similarity: " ++ << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n"; ++ ++ assert(ProfOverlap.UnionSample > 0 && ++ "Total samples in two profile should be greater than 0"); ++ double OverlapPercent = ++ static_cast(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample; ++ assert(ProfOverlap.BaseSample > 0 && ++ "Total samples in base profile should be greater than 0"); ++ double BaseUniquePercent = static_cast(ProfOverlap.BaseUniqueSample) / ++ ProfOverlap.BaseSample; ++ assert(ProfOverlap.TestSample > 0 && ++ "Total samples in test profile should be greater than 0"); ++ double TestUniquePercent = static_cast(ProfOverlap.TestUniqueSample) / ++ ProfOverlap.TestSample; ++ ++ OS << " Whole program sample overlap: " ++ << format("%.3f%%", OverlapPercent * 100) << "\n"; ++ OS << " percentage of samples unique in base profile: " ++ << format("%.3f%%", BaseUniquePercent * 100) << "\n"; ++ OS << " percentage of samples unique in test profile: " ++ << format("%.3f%%", TestUniquePercent * 100) << "\n"; ++ OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n" ++ << " total samples in test profile: " << ProfOverlap.TestSample << "\n"; ++ ++ assert(ProfOverlap.UnionCount > 0 && ++ "There should be at least one function in two input profiles"); ++ double FuncOverlapPercent = ++ static_cast(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount; ++ OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100) ++ << "\n"; ++ OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n"; ++ OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount ++ << "\n"; ++ OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount ++ << "\n"; ++} ++ ++void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap( ++ raw_fd_ostream &OS) const { ++ assert(HotFuncOverlap.UnionCount > 0 && ++ "There should be at least one hot function in two input profiles"); ++ OS << " Hot-function overlap: " ++ << format("%.3f%%", static_cast(HotFuncOverlap.OverlapCount) / ++ HotFuncOverlap.UnionCount * 100) ++ << "\n"; ++ OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n"; ++ OS << " hot functions unique in base profile: " ++ << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n"; ++ OS << " hot functions unique in test profile: " ++ << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n"; ++ ++ assert(HotBlockOverlap.UnionCount > 0 && ++ "There should be at least one hot block in two input profiles"); ++ OS << " Hot-block overlap: " ++ << format("%.3f%%", static_cast(HotBlockOverlap.OverlapCount) / ++ HotBlockOverlap.UnionCount * 100) ++ << "\n"; ++ OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n"; ++ OS << " hot blocks unique in base profile: " ++ << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n"; ++ OS << " hot blocks unique in test profile: " ++ << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n"; ++} ++ ++std::error_code SampleOverlapAggregator::loadProfiles() { ++ using namespace sampleprof; ++ ++ LLVMContext Context; ++ auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, ++ FSDiscriminatorPassOption); ++ if (std::error_code EC = BaseReaderOrErr.getError()) ++ exitWithErrorCode(EC, BaseFilename); ++ ++ auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, ++ FSDiscriminatorPassOption); ++ if (std::error_code EC = TestReaderOrErr.getError()) ++ exitWithErrorCode(EC, TestFilename); ++ ++ BaseReader = std::move(BaseReaderOrErr.get()); ++ TestReader = std::move(TestReaderOrErr.get()); ++ ++ if (std::error_code EC = BaseReader->read()) ++ exitWithErrorCode(EC, BaseFilename); ++ if (std::error_code EC = TestReader->read()) ++ exitWithErrorCode(EC, TestFilename); ++ if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased()) ++ exitWithError( ++ "cannot compare probe-based profile with non-probe-based profile"); ++ if (BaseReader->profileIsCS() != TestReader->profileIsCS()) ++ exitWithError("cannot compare CS profile with non-CS profile"); ++ ++ // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in ++ // profile summary. ++ ProfileSummary &BasePS = BaseReader->getSummary(); ++ ProfileSummary &TestPS = TestReader->getSummary(); ++ BaseHotThreshold = ++ ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary()); ++ TestHotThreshold = ++ ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary()); ++ ++ return std::error_code(); ++} ++ ++void overlapSampleProfile(const std::string &BaseFilename, ++ const std::string &TestFilename, ++ const OverlapFuncFilters &FuncFilter, ++ uint64_t SimilarityCutoff, raw_fd_ostream &OS) { ++ using namespace sampleprof; ++ ++ // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics ++ // report 2--3 places after decimal point in percentage numbers. ++ SampleOverlapAggregator OverlapAggr( ++ BaseFilename, TestFilename, ++ static_cast(SimilarityCutoff) / 1000000, 0.000005, FuncFilter); ++ if (std::error_code EC = OverlapAggr.loadProfiles()) ++ exitWithErrorCode(EC); ++ ++ OverlapAggr.initializeSampleProfileOverlap(); ++ if (OverlapAggr.detectZeroSampleProfile(OS)) ++ return; ++ ++ OverlapAggr.computeSampleProfileOverlap(OS); ++ ++ OverlapAggr.dumpProgramSummary(OS); ++ OverlapAggr.dumpHotFuncAndBlockOverlap(OS); ++ OverlapAggr.dumpFuncSimilarity(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 PGO counts. Does not work with CSSPGO.")); ++ cl::opt ValueCutoff( ++ "value-cutoff", cl::init(-1), ++ cl::desc( ++ "Function level overlap information for every function (with calling " ++ "context for csspgo) 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. For " ++ "CSSPGO this takes a a function name with calling context")); ++ cl::opt SimilarityCutoff( ++ "similarity-cutoff", cl::init(0), ++ cl::desc("For sample profiles, list function names (with calling context " ++ "for csspgo) for overlapped functions " ++ "with similarities below the cutoff (percentage times 10000).")); ++ cl::opt ProfileKind( ++ cl::desc("Profile kind:"), cl::init(instr), ++ cl::values(clEnumVal(instr, "Instrumentation profile (default)"), ++ clEnumVal(sample, "Sample profile"))); ++ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); ++ ++ std::error_code EC; ++ raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); ++ if (EC) ++ exitWithErrorCode(EC, Output); ++ ++ if (ProfileKind == instr) ++ overlapInstrProfile(BaseFilename, TestFilename, ++ OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, ++ IsCS); ++ else ++ overlapSampleProfile(BaseFilename, TestFilename, ++ OverlapFuncFilters{ValueCutoff, FuncNameFilter}, ++ SimilarityCutoff, OS); ++ ++ return 0; ++} ++ ++namespace { ++struct ValueSitesStats { ++ ValueSitesStats() ++ : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), ++ TotalNumValues(0) {} ++ uint64_t TotalNumValueSites; ++ uint64_t TotalNumValueSitesWithValueProfile; ++ uint64_t TotalNumValues; ++ std::vector ValueSitesHistogram; ++}; ++} // namespace ++ ++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, ++ bool ShowBinaryIds, bool ShowCovered, ++ bool ShowProfileVersion, ShowFormat SFormat, ++ raw_fd_ostream &OS) { ++ if (SFormat == ShowFormat::Json) ++ exitWithError("JSON output is not supported for instr profiles"); ++ if (SFormat == ShowFormat::Yaml) ++ exitWithError("YAML output is not supported for instr profiles"); ++ auto ReaderOrErr = InstrProfReader::create(Filename); ++ std::vector Cutoffs = std::move(DetailedSummaryCutoffs); ++ if (ShowDetailedSummary && Cutoffs.empty()) { ++ Cutoffs = ProfileSummaryBuilder::DefaultCutoffs; ++ } ++ 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.contains(ShowFunction)); ++ ++ 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); ++ ++ if (ShowCovered) { ++ if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; })) ++ OS << Func.Name << "\n"; ++ continue; ++ } ++ ++ uint64_t FuncMax = 0; ++ uint64_t FuncSum = 0; ++ ++ auto PseudoKind = Func.getCountPseudoKind(); ++ if (PseudoKind != InstrProfRecord::NotPseudo) { ++ if (Show) { ++ if (!ShownFunctions) ++ OS << "Counters:\n"; ++ ++ShownFunctions; ++ OS << " " << Func.Name << ":\n" ++ << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" ++ << " Counters: " << Func.Counts.size(); ++ if (PseudoKind == InstrProfRecord::PseudoHot) ++ OS << " \n"; ++ else if (PseudoKind == InstrProfRecord::PseudoWarm) ++ OS << " \n"; ++ else ++ llvm_unreachable("Unknown PseudoKind"); ++ } ++ continue; ++ } ++ ++ 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 || ShowCovered) ++ return 0; ++ std::unique_ptr PS(Builder.getSummary()); ++ bool IsIR = Reader->isIRLevelProfile(); ++ OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end"); ++ if (IsIR) ++ OS << " entry_first = " << Reader->instrEntryBBEnabled(); ++ OS << "\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); ++ } ++ ++ if (ShowBinaryIds) ++ if (Error E = Reader->printBinaryIds(OS)) ++ exitWithError(std::move(E), Filename); ++ ++ if (ShowProfileVersion) ++ OS << "Profile version: " << Reader->getVersion() << "\n"; ++ 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 { ++ std::string FuncName; ++ uint64_t TotalCount; ++ double TotalCountPercent; ++ uint64_t MaxCount; ++ uint64_t EntryCount; ++ ++ HotFuncInfo() ++ : 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.begin(), FN.end()), 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, ++ uint32_t TopNFunctions, raw_fd_ostream &OS) { ++ assert(ColumnOffset.size() == ColumnTitle.size() && ++ "ColumnOffset and ColumnTitle should have the same size"); ++ assert(ColumnTitle.size() >= 4 && ++ "ColumnTitle should have at least 4 elements"); ++ assert(TotalFuncCount > 0 && ++ "There should be at least one function in the profile"); ++ double TotalProfPercent = 0; ++ if (TotalProfCount > 0) ++ TotalProfPercent = static_cast(HotProfCount) / TotalProfCount * 100; ++ ++ formatted_raw_ostream FOS(OS); ++ FOS << HotFuncCount << " out of " << TotalFuncCount ++ << " functions with profile (" ++ << format("%.2f%%", ++ (static_cast(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"; ++ ++ uint32_t Count = 0; ++ for (const auto &R : PrintValues) { ++ if (TopNFunctions && (Count++ == TopNFunctions)) ++ break; ++ 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"; ++ } ++} ++ ++static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, ++ ProfileSummary &PS, uint32_t TopN, ++ 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; ++ } ++ } ++ ++ // 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; ++ ++ for (const auto &I : Profiles) { ++ FuncSampleStats FuncStats; ++ const FunctionSamples &FuncProf = I.second; ++ ProfileTotalSample += FuncProf.getTotalSamples(); ++ getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold); ++ ++ if (isFunctionHot(FuncStats, MinCountThreshold)) { ++ HotFunc.emplace(FuncProf.getTotalSamples(), ++ std::make_pair(&(I.second), FuncStats.MaxSample)); ++ 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.getContext().toString(), Func.getTotalSamples(), ++ TotalSamplePercent, FuncPair.second.second, ++ Func.getHeadSamplesEstimate())); ++ } ++ dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, ++ Profiles.size(), HotFuncSample, ProfileTotalSample, ++ Metric, TopN, OS); ++ ++ return 0; ++} ++ ++static int showSampleProfile(const std::string &Filename, bool ShowCounts, ++ uint32_t TopN, bool ShowAllFunctions, ++ bool ShowDetailedSummary, ++ const std::string &ShowFunction, ++ bool ShowProfileSymbolList, ++ bool ShowSectionInfoOnly, bool ShowHotFuncList, ++ ShowFormat SFormat, raw_fd_ostream &OS) { ++ if (SFormat == ShowFormat::Yaml) ++ exitWithError("YAML output is not supported for sample profiles"); ++ using namespace sampleprof; ++ LLVMContext Context; ++ auto ReaderOrErr = ++ SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption); ++ 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()) { ++ if (SFormat == ShowFormat::Json) ++ Reader->dumpJson(OS); ++ else ++ Reader->dump(OS); ++ } else { ++ if (SFormat == ShowFormat::Json) ++ exitWithError( ++ "the JSON format is supported only when all functions are to " ++ "be printed"); ++ ++ // TODO: parse context string to support filtering by contexts. ++ Reader->dumpFunctionProfile(StringRef(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 || TopN) ++ showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS); ++ ++ return 0; ++} ++ ++static int showMemProfProfile(const std::string &Filename, ++ const std::string &ProfiledBinary, ++ ShowFormat SFormat, raw_fd_ostream &OS) { ++ if (SFormat == ShowFormat::Json) ++ exitWithError("JSON output is not supported for MemProf"); ++ auto ReaderOr = llvm::memprof::RawMemProfReader::create( ++ Filename, ProfiledBinary, /*KeepNames=*/true); ++ if (Error E = ReaderOr.takeError()) ++ // Since the error can be related to the profile or the binary we do not ++ // pass whence. Instead additional context is provided where necessary in ++ // the error message. ++ exitWithError(std::move(E), /*Whence*/ ""); ++ ++ std::unique_ptr Reader( ++ ReaderOr.get().release()); ++ ++ Reader->printYAML(OS); ++ return 0; ++} ++ ++static int showDebugInfoCorrelation(const std::string &Filename, ++ bool ShowDetailedSummary, ++ bool ShowProfileSymbolList, ++ ShowFormat SFormat, raw_fd_ostream &OS) { ++ if (SFormat == ShowFormat::Json) ++ exitWithError("JSON output is not supported for debug info correlation"); ++ std::unique_ptr Correlator; ++ if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator)) ++ exitWithError(std::move(Err), Filename); ++ if (SFormat == ShowFormat::Yaml) { ++ if (auto Err = Correlator->dumpYaml(OS)) ++ exitWithError(std::move(Err), Filename); ++ return 0; ++ } ++ ++ if (auto Err = Correlator->correlateProfileData()) ++ exitWithError(std::move(Err), Filename); ++ ++ InstrProfSymtab Symtab; ++ if (auto Err = Symtab.create( ++ StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize()))) ++ exitWithError(std::move(Err), Filename); ++ ++ if (ShowProfileSymbolList) ++ Symtab.dumpNames(OS); ++ // TODO: Read "Profile Data Type" from debug info to compute and show how many ++ // counters the section holds. ++ if (ShowDetailedSummary) ++ OS << "Counters section size: 0x" ++ << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n"; ++ OS << "Found " << Correlator->getDataSize() << " functions\n"; ++ ++ return 0; ++} ++ ++static int show_main(int argc, const char *argv[]) { ++ cl::opt Filename(cl::Positional, cl::desc("")); ++ ++ cl::opt ShowCounts("counts", cl::init(false), ++ cl::desc("Show counter values for shown functions")); ++ cl::opt SFormat( ++ "show-format", cl::init(ShowFormat::Text), ++ cl::desc("Emit output in the selected format if supported"), ++ cl::values(clEnumValN(ShowFormat::Text, "text", ++ "emit normal text output (default)"), ++ clEnumValN(ShowFormat::Json, "json", "emit JSON"), ++ clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); ++ // TODO: Consider replacing this with `--show-format=text-encoding`. ++ cl::opt TextFormat( ++ "text", cl::init(false), ++ cl::desc("Show instr profile data in text dump format")); ++ cl::opt JsonFormat( ++ "json", cl::desc("Show sample profile data in the JSON format " ++ "(deprecated, please use --show-format=json)")); ++ 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"), ++ clEnumVal(memory, "MemProf memory access 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::opt ShowBinaryIds("binary-ids", cl::init(false), ++ cl::desc("Show binary ids in the profile. ")); ++ cl::opt DebugInfoFilename( ++ "debug-info", cl::init(""), ++ cl::desc("Read and extract profile metadata from debug info and show " ++ "the functions it found.")); ++ cl::opt ShowCovered( ++ "covered", cl::init(false), ++ cl::desc("Show only the functions that have been executed.")); ++ cl::opt ProfiledBinary( ++ "profiled-binary", cl::init(""), ++ cl::desc("Path to binary from which the profile was collected.")); ++ cl::opt ShowProfileVersion("profile-version", cl::init(false), ++ cl::desc("Show profile version. ")); ++ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); ++ ++ if (Filename.empty() && DebugInfoFilename.empty()) ++ exitWithError( ++ "the positional argument '' is required unless '--" + ++ DebugInfoFilename.ArgStr + "' is provided"); ++ ++ if (Filename == OutputFilename) { ++ errs() << sys::path::filename(argv[0]) ++ << ": Input file name cannot be the same as the output file name!\n"; ++ return 1; ++ } ++ if (JsonFormat) ++ SFormat = ShowFormat::Json; ++ ++ std::error_code EC; ++ raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); ++ if (EC) ++ exitWithErrorCode(EC, OutputFilename); ++ ++ if (ShowAllFunctions && !ShowFunction.empty()) ++ WithColor::warning() << "-function argument ignored: showing all functions\n"; ++ ++ if (!DebugInfoFilename.empty()) ++ return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, ++ ShowProfileSymbolList, SFormat, OS); ++ ++ if (ProfileKind == instr) ++ return showInstrProfile( ++ Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, ++ ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, ++ ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, ++ TextFormat, ShowBinaryIds, ShowCovered, ShowProfileVersion, SFormat, ++ OS); ++ if (ProfileKind == sample) ++ return showSampleProfile(Filename, ShowCounts, TopNFunctions, ++ ShowAllFunctions, ShowDetailedSummary, ++ ShowFunction, ShowProfileSymbolList, ++ ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); ++ return showMemProfProfile(Filename, ProfiledBinary, SFormat, 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/ldc-1.32.2-llvm-16-context_usage.patch b/ldc-1.32.2-llvm-16-context_usage.patch new file mode 100644 index 0000000..e055756 --- /dev/null +++ b/ldc-1.32.2-llvm-16-context_usage.patch @@ -0,0 +1,14 @@ +--- ldc-1.32.2/gen/optimizer.cpp.orig 2023-05-28 16:28:05.653166497 +0200 ++++ ldc-1.32.2/gen/optimizer.cpp 2023-05-28 16:28:54.543949601 +0200 +@@ -654,7 +654,11 @@ + bool debugLogging = false; + ppo.Indent = false; + ppo.SkipAnalyses = false; ++ #if LDC_LLVM_VER < 1600 + StandardInstrumentations si(debugLogging, verifyEach, ppo); ++ #else ++ StandardInstrumentations si(M->getContext(), debugLogging, /*VerifyEach=*/false, ppo); ++ #endif + + si.registerCallbacks(pic, &fam); + diff --git a/ldc-1.32.2-llvm-16-fix-aarch64-support.patch b/ldc-1.32.2-llvm-16-fix-aarch64-support.patch new file mode 100644 index 0000000..d54c9a5 --- /dev/null +++ b/ldc-1.32.2-llvm-16-fix-aarch64-support.patch @@ -0,0 +1,29 @@ +From 25cf7f83ce7e1b071912a015b366ce8f71783306 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Mon, 22 May 2023 09:06:43 +0100 +Subject: [PATCH] driver/targetmachine: Fix aarch64 support for LLVM >= 1600 + +This isn't *the* most ideal approach and in future we may want +to select specific fallbacks other than "generic". + +Signed-off-by: Ikey Doherty +--- + driver/targetmachine.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/driver/targetmachine.cpp b/driver/targetmachine.cpp +index be47a1dc93..3f16e20813 100644 +--- a/driver/targetmachine.cpp ++++ b/driver/targetmachine.cpp +@@ -203,9 +203,11 @@ static std::string getARMTargetCPU(const llvm::Triple &triple) { + } + + static std::string getAArch64TargetCPU(const llvm::Triple &triple) { ++#if LDC_LLVM_VER < 1600 + auto defaultCPU = llvm::AArch64::getDefaultCPU(triple.getArchName()); + if (!defaultCPU.empty()) + return std::string(defaultCPU); ++#endif + + return "generic"; + } diff --git a/ldc-1.32.2-llvm-16-fix-ms_cxx_helper.patch b/ldc-1.32.2-llvm-16-fix-ms_cxx_helper.patch new file mode 100644 index 0000000..1312f98 --- /dev/null +++ b/ldc-1.32.2-llvm-16-fix-ms_cxx_helper.patch @@ -0,0 +1,31 @@ +From 4d5b7dc86e5a282914b46c4982f612228bb75bb0 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Mon, 22 May 2023 08:48:56 +0100 +Subject: [PATCH] gen/ms-cxx-helper: Use new `insertInto` LLVM16 API + +Instructions now use friend APIs to add themselves into a +BasicBlock, rather than a BasicBlock having an API to add +the instructions. + +Signed-off-by: Ikey Doherty +--- + gen/ms-cxx-helper.cpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/gen/ms-cxx-helper.cpp b/gen/ms-cxx-helper.cpp +index 52814fd5cf..9f4eb7627f 100644 +--- a/gen/ms-cxx-helper.cpp ++++ b/gen/ms-cxx-helper.cpp +@@ -114,7 +114,12 @@ void cloneBlocks(const std::vector &srcblocks, + if (!newInst) + newInst = Inst->clone(); + ++ #if LDC_LLVM_VER < 1600 + nbb->getInstList().push_back(newInst); ++ #else ++ newInst->insertInto(nbb, nbb->end()); ++ #endif ++ + VMap[Inst] = newInst; // Add instruction map to value. + if (unwindTo) + if (auto dest = getUnwindDest(Inst)) diff --git a/ldc-1.32.2-llvm-16-stop_using_aggressive_combine.patch b/ldc-1.32.2-llvm-16-stop_using_aggressive_combine.patch new file mode 100644 index 0000000..9418011 --- /dev/null +++ b/ldc-1.32.2-llvm-16-stop_using_aggressive_combine.patch @@ -0,0 +1,29 @@ +From 097c9787d6d76cd069e90fdeccc083ccec5b9631 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Mon, 22 May 2023 09:01:59 +0100 +Subject: [PATCH] driver/main: For LLVM > 16, stop using AggressiveInstCombine + +This was killed upstream in the codegen pipeline as part of a bunch of +legacy PM removals: + + - https://reviews.llvm.org/D137116 + +Signed-off-by: Ikey Doherty +--- + driver/main.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/driver/main.cpp b/driver/main.cpp +index 6d1c3441eb..fcaaf28983 100644 +--- a/driver/main.cpp ++++ b/driver/main.cpp +@@ -579,7 +579,9 @@ void initializePasses() { + #endif + initializeVectorization(Registry); + initializeInstCombine(Registry); ++#if LDC_LLVM_VER < 1600 + initializeAggressiveInstCombine(Registry); ++#endif + initializeIPO(Registry); + #if LDC_LLVM_VER < 1600 + initializeInstrumentation(Registry); diff --git a/ldc-1.32.2-llvm-16-update_to_ir_include.patch b/ldc-1.32.2-llvm-16-update_to_ir_include.patch new file mode 100644 index 0000000..62c69dd --- /dev/null +++ b/ldc-1.32.2-llvm-16-update_to_ir_include.patch @@ -0,0 +1,25 @@ +From 815684cd36cc438c7b0ebfea672abdd107dd7c96 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Mon, 22 May 2023 08:40:52 +0100 +Subject: [PATCH] gen/runtime: Use the correct header path for ModRef + +In stable LLVM 16 this is now under `Support` and not `IR` + +Signed-off-by: Ikey Doherty +--- + gen/runtime.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/gen/runtime.cpp b/gen/runtime.cpp +index 37cd6dc741..c2040341f2 100644 +--- a/gen/runtime.cpp ++++ b/gen/runtime.cpp +@@ -33,7 +33,7 @@ + #include "llvm/Bitcode/BitcodeWriter.h" + #include "llvm/IR/Attributes.h" + #if LDC_LLVM_VER >= 1600 +-#include "llvm/IR/ModRef.h" ++#include "llvm/Support/ModRef.h" + #endif + #include "llvm/IR/Module.h" + #include "llvm/Support/CommandLine.h" diff --git a/ldc-1.32.2-llvm-16-use_std_optional_api.patch b/ldc-1.32.2-llvm-16-use_std_optional_api.patch new file mode 100644 index 0000000..0fd80e7 --- /dev/null +++ b/ldc-1.32.2-llvm-16-use_std_optional_api.patch @@ -0,0 +1,204 @@ +From 8e1dffa5203439996d6c461485cad566ec321884 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Mon, 22 May 2023 10:53:55 +0100 +Subject: [PATCH] For LLVM >= 16, use std::optional APIs + +Unfortunately when building with LLVM < 15, we're using C++-11, +so the std::optional bits are only available in C++-17. + +Even more unfortunately we can't really override this, rather +inherit the fact of using C++-17 when using LLVM > 16. + +Thus, we add a slightly messy conditional compilation situation +to allow usage of LDC on older LLVM versions, and with newer +LLVM/libcxx. + +Signed-off-by: Ikey Doherty +--- + driver/args.cpp | 4 ++++ + driver/linker.cpp | 4 ++++ + driver/linker.h | 4 ++++ + gen/dcompute/druntime.cpp | 7 ++++++- + gen/dibuilder.cpp | 21 +++++++++++++++++++++ + gen/optimizer.cpp | 5 +++++ + gen/uda.cpp | 4 ++++ + utils/not.cpp | 4 ++++ + 8 files changed, 52 insertions(+), 1 deletion(-) + +diff --git a/driver/args.cpp b/driver/args.cpp +index da291202d1..bad8e45dee 100644 +--- a/driver/args.cpp ++++ b/driver/args.cpp +@@ -175,7 +175,11 @@ int executeAndWait(std::vector fullArgs, + } + + const std::vector argv = toRefsVector(fullArgs); ++#if LDC_LLVM_VER < 1600 + auto envVars = llvm::None; ++#else ++ auto envVars = std::nullopt; ++#endif + + return llvm::sys::ExecuteAndWait(argv[0], argv, envVars, {}, 0, 0, errorMsg); + } +diff --git a/driver/linker.cpp b/driver/linker.cpp +index 76266379d2..803d5e883e 100644 +--- a/driver/linker.cpp ++++ b/driver/linker.cpp +@@ -195,7 +195,11 @@ static std::vector getDefaultLibNames() { + llvm::Optional> getExplicitPlatformLibs() { + if (platformLib.getNumOccurrences() > 0) + return parseLibNames(platformLib); ++#if LDC_LLVM_VER < 1600 + return llvm::None; ++#else ++ return std::nullopt; ++#endif + } + + ////////////////////////////////////////////////////////////////////////////// +diff --git a/driver/linker.h b/driver/linker.h +index f962054efa..28362d8652 100644 +--- a/driver/linker.h ++++ b/driver/linker.h +@@ -42,7 +42,11 @@ bool linkAgainstSharedDefaultLibs(); + /** + * Returns the -platformlib library names, if specified. + */ ++#if LDC_LLVM_VER < 1600 + llvm::Optional> getExplicitPlatformLibs(); ++#else ++std::optional> getExplicitPlatformLibs(); ++#endif + + /** + * Returns the value of -mscrtlib. +diff --git a/gen/dcompute/druntime.cpp b/gen/dcompute/druntime.cpp +index 6867765466..aee93f4c53 100644 +--- a/gen/dcompute/druntime.cpp ++++ b/gen/dcompute/druntime.cpp +@@ -42,8 +42,13 @@ bool isFromLDC_OpenCL(Dsymbol *sym) { + } + + llvm::Optional toDcomputePointer(StructDeclaration *sd) { +- if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) ++ if (sd->ident != Id::dcPointer || !isFromLDC_DCompute(sd)) { ++#if LDC_LLVM_VER < 1600 + return llvm::Optional(llvm::None); ++#else ++ return std::optional(std::nullopt); ++#endif ++ } + + TemplateInstance *ti = sd->isInstantiated(); + int addrspace = isExpression((*ti->tiargs)[0])->toInteger(); +diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp +index 1e817ee3f4..f7068294dd 100644 +--- a/gen/dibuilder.cpp ++++ b/gen/dibuilder.cpp +@@ -372,7 +372,11 @@ DIType DIBuilder::CreateEnumType(TypeEnum *type) { + DIType DIBuilder::CreatePointerType(TypePointer *type) { + // TODO: The addressspace is important for dcompute targets. See e.g. + // https://www.mail-archive.com/dwarf-discuss@lists.dwarfstd.org/msg00326.html ++#if LDC_LLVM_VER < 1600 + const llvm::Optional DWARFAddressSpace = llvm::None; ++#else ++ const std::optional DWARFAddressSpace = std::nullopt; ++#endif + + const auto name = processDIName(type->toPrettyChars(true)); + +@@ -730,7 +734,11 @@ DISubroutineType DIBuilder::CreateFunctionType(Type *type) { + } + + DISubroutineType DIBuilder::CreateEmptyFunctionType() { ++#if LDC_LLVM_VER < 1600 + auto paramsArray = DBuilder.getOrCreateTypeArray(llvm::None); ++#else ++ auto paramsArray = DBuilder.getOrCreateTypeArray(std::nullopt); ++#endif + return DBuilder.createSubroutineType(paramsArray); + } + +@@ -774,9 +782,16 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { + return nullptr; + if (t->ty == TY::Tnull) { + // display null as void* ++#if LDC_LLVM_VER < 1600 + return DBuilder.createPointerType( + CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0, + /* DWARFAddressSpace */ llvm::None, "typeof(null)"); ++#else ++ return DBuilder.createPointerType( ++ CreateTypeDescription(Type::tvoid), target.ptrsize * 8, 0, ++ /* DWARFAddressSpace */ std::nullopt, "typeof(null)"); ++#endif ++ + } + if (auto te = t->isTypeEnum()) + return CreateEnumType(te); +@@ -798,8 +813,14 @@ DIType DIBuilder::CreateTypeDescription(Type *t, bool voidToUbyte) { + const auto aggregateDIType = CreateCompositeType(t); + const auto name = + (tc->sym->toPrettyChars(true) + llvm::StringRef("*")).str(); ++#if LDC_LLVM_VER < 1600 + return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0, + llvm::None, processDIName(name)); ++#else ++ return DBuilder.createPointerType(aggregateDIType, target.ptrsize * 8, 0, ++ std::nullopt, processDIName(name)); ++#endif ++ + } + if (auto tf = t->isTypeFunction()) + return CreateFunctionType(tf); +diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp +index 86146bf6f3..13bad60a04 100644 +--- a/gen/optimizer.cpp ++++ b/gen/optimizer.cpp +@@ -572,8 +572,13 @@ static llvm::Optional getPGOOptions() { + PGOOptions::CSPGOAction::NoCSAction, + debugInfoForProfiling, pseudoProbeForProfiling); + } ++#if LDC_LLVM_VER < 1600 + return None; ++#else ++ return std::nullopt; ++#endif + } ++ + static PipelineTuningOptions getPipelineTuningOptions(unsigned optLevelVal, unsigned sizeLevelVal) { + PipelineTuningOptions pto; + +diff --git a/gen/uda.cpp b/gen/uda.cpp +index 371b565b38..521205c5da 100644 +--- a/gen/uda.cpp ++++ b/gen/uda.cpp +@@ -219,7 +219,11 @@ void applyAttrAllocSize(StructLiteralExp *sle, IrFunction *irFunc) { + if (numArgIdx >= 0) { + builder.addAllocSizeAttr(llvmSizeIdx, llvmNumIdx); + } else { ++#if LDC_LLVM_VER < 1600 + builder.addAllocSizeAttr(llvmSizeIdx, llvm::Optional()); ++#else ++ builder.addAllocSizeAttr(llvmSizeIdx, std::optional()); ++#endif + } + + llvm::Function *func = irFunc->getLLVMFunc(); +diff --git a/utils/not.cpp b/utils/not.cpp +index b434af7331..7ca8d1d8a6 100644 +--- a/utils/not.cpp ++++ b/utils/not.cpp +@@ -43,7 +43,11 @@ int main(int argc, const char **argv) { + Argv.reserve(argc); + for (int i = 0; i < argc; ++i) + Argv.push_back(argv[i]); ++#if LDC_LLVM_VER < 1600 + auto Env = llvm::None; ++#else ++ auto Env = std::nullopt; ++#endif + + std::string ErrMsg; + int Result = sys::ExecuteAndWait(*Program, Argv, Env, {}, 0, 0, &ErrMsg); diff --git a/ldc-1.32.2-llvm-16.0.4-use_std_optional_api.patch b/ldc-1.32.2-llvm-16.0.4-use_std_optional_api.patch new file mode 100644 index 0000000..95412b3 --- /dev/null +++ b/ldc-1.32.2-llvm-16.0.4-use_std_optional_api.patch @@ -0,0 +1,30 @@ +--- ldc-1.32.2/driver/args.h.orig 2023-05-28 16:39:55.277642880 +0200 ++++ ldc-1.32.2/driver/args.h 2023-05-28 16:43:17.582551616 +0200 +@@ -40,8 +40,8 @@ + // Executes a command line and returns its exit code. + // Optionally uses a response file to overcome cmdline length limitations. + int executeAndWait(std::vector fullArgs, +- llvm::Optional +- responseFileEncoding = {llvm::None}, ++ std::optional ++ responseFileEncoding = {std::nullopt}, + std::string *errorMsg = nullptr); + } // namespace args + +--- ldc-1.32.2/driver/args.cpp.orig 2023-05-28 16:43:44.700403861 +0200 ++++ ldc-1.32.2/driver/args.cpp 2023-05-28 16:46:39.739443702 +0200 +@@ -159,11 +159,11 @@ + + int executeAndWait( + std::vector fullArgs, +- llvm::Optional responseFileEncoding, ++ std::optional responseFileEncoding, + std::string *errorMsg) { + args::ResponseFile rspFile; +- if (responseFileEncoding.hasValue() && +- !rspFile.setup(fullArgs, responseFileEncoding.getValue())) { ++ if (responseFileEncoding.has_value() && ++ !rspFile.setup(fullArgs, responseFileEncoding.value())) { + if (errorMsg) + *errorMsg = "could not write temporary response file"; + return -1; diff --git a/ldc.spec b/ldc.spec index 8360871..1916add 100644 --- a/ldc.spec +++ b/ldc.spec @@ -1,7 +1,7 @@ -%define bootstrap 0 +#% define bootstrap 0 Name: ldc -Version: 1.31.0 -Release: 2mamba +Version: 1.32.2 +Release: 1mamba Summary: The LLVM-based D Compiler Group: Applications/Development Vendor: openmamba @@ -12,17 +12,24 @@ Source: https://github.com/ldc-developers/ldc.git/v%{version}/ldc-%{versi 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 Source3: https://github.com/ldc-developers/ldc/releases/download/v%{version}/ldc2-%{version}-linux-aarch64.tar.xz -Patch0: ldc-1.23.0-llvm-11.patch +Patch0: ldc-1.32.2-llvm-16-update_to_ir_include.patch +Patch1: ldc-1.32.2-llvm-16-context_usage.patch +Patch2: ldc-1.32.2-llvm-16-fix-ms_cxx_helper.patch +Patch3: ldc-1.32.2-llvm-16-stop_using_aggressive_combine.patch +Patch4: ldc-1.32.2-llvm-16-fix-aarch64-support.patch +Patch5: ldc-1.32.2-llvm-16-use_std_optional_api.patch +Patch6: ldc-1.32.2-llvm-16-add_llvm_profdata.patch +Patch7: ldc-1.32.2-llvm-16.0.4-use_std_optional_api.patch License: BSD ## AUTOBUILDREQ-BEGIN BuildRequires: glibc-devel BuildRequires: libgcc +BuildRequires: liblld-devel BuildRequires: libllvm-devel -BuildRequires: libspirv-llvm-translator-devel BuildRequires: libstdc++6-devel ## AUTOBUILDREQ-END BuildRequires: cmake -BuildRequires: libllvm-devel >= 14.0.3 +BuildRequires: libllvm-devel >= 16.0.4 Requires: lib%{name} = %{?epoch:%epoch:}%{version}-%{release} %description @@ -46,7 +53,7 @@ This package contains libraries and header files for developing applications tha %debug_package %prep -%if "0%{?bootstrap}" +%if "%{?bootstrap}" != "" %ifarch x86_64 %setup -q -a1 %endif @@ -59,21 +66,24 @@ This package contains libraries and header files for developing applications tha %else %setup -q %endif -#%patch0 -p1 + +%patch 0 -p1 -b .llvm-16-update_to_ir_include +%patch 1 -p1 -b .llvm-16-context_usage +%patch 2 -p1 -b .llvm-16-add_llvm_profdata +%patch 3 -p1 -b .llvm-16-fix-ms_cxx_helper +%patch 4 -p1 -b .llvm-16-stop_using_aggressive_combine +%patch 5 -p1 -b .llvm-16-fix-aarch64-support +%patch 6 -p1 -b .llvm-16-use_std_optional_api +%patch 7 -p1 -b .llvm-16.0.4-use_std_optional_api %build %cmake -d build \ -DBUILD_SHARED_LIBS=BOTH \ -DBUILD_LTO_LIBS=ON \ - -DLDC_WITH_LLD=OFF \ -%ifarch x86_64 aarch64 - -DD_COMPILER_FLAGS="-link-defaultlib-shared=false -linker=gold --flto=thin" \ -%else -DD_COMPILER_FLAGS="-link-defaultlib-shared=false --flto=thin" \ -%endif -DADDITIONAL_DEFAULT_LDC_SWITCHES="\"-link-defaultlib-shared\"" \ - -DINCLUDE_INSTALL_DIR=%{_includedir}/d \ -%if "0%{?bootstrap}" != "0" + -DINCLUDE_INSTALL_DIR=%{_includedir}/dlang/ldc \ +%if "%{?bootstrap}" != "" %ifarch x86_64 -DD_COMPILER=`pwd`/../ldc2-%{version}-linux-x86_64/bin/ldmd2 %endif @@ -106,6 +116,7 @@ This package contains libraries and header files for developing applications tha %{_bindir}/ldc-prune-cache %{_bindir}/ldc2 %{_bindir}/ldmd2 +%{_bindir}/timetrace2txt %{_datadir}/bash-completion/completions/ldc2 %files -n lib%{name} @@ -119,18 +130,8 @@ This package contains libraries and header files for developing applications tha %files -n lib%{name}-devel %defattr(-,root,root) -%dir %{_includedir}/d -%dir %{_includedir}/d/core -%{_includedir}/d/core/* -%dir %{_includedir}/d/etc -%{_includedir}/d/etc/* -%dir %{_includedir}/d/ldc -%{_includedir}/d/ldc/* -%{_includedir}/d/object.d -%dir %{_includedir}/d/std -%{_includedir}/d/std/* -%{_includedir}/d/__builtins.di -%{_includedir}/d/importc.h +%dir %{_includedir}/dlang/ldc +%{_includedir}/dlang/ldc/* %{_libdir}/ldc_rt.dso.o %{_libdir}/libdruntime-ldc-debug-shared.so %{_libdir}/libdruntime-ldc-shared.so @@ -141,6 +142,15 @@ This package contains libraries and header files for developing applications tha %doc README.md %changelog +* Sun May 28 2023 Automatic Build System 1.32.2-1mamba +- automatic version update by autodist + +* Tue Apr 18 2023 Automatic Build System 1.32.1-1mamba +- automatic version update by autodist + +* Tue Mar 14 2023 Automatic Build System 1.32.0-1mamba +- automatic version update by autodist + * Sun Mar 12 2023 Silvan Calarco 1.31.0-2mamba - rebuiult with more cmake flags as attempt to fix 'Error: unrecognized trait classInstanceAlignment'