diff --git a/.github/workflows/supported_llvm_versions.yml b/.github/workflows/supported_llvm_versions.yml index 9b9ff4021f0..6be3c8b38b9 100644 --- a/.github/workflows/supported_llvm_versions.yml +++ b/.github/workflows/supported_llvm_versions.yml @@ -29,22 +29,17 @@ jobs: os: ubuntu-20.04 host_dc: ldc-beta llvm_version: 12.0.1 - cmake_flags: -DLIB_SUFFIX=64 - - job_name: Ubuntu 20.04, LLVM 11, latest LDC beta - os: ubuntu-20.04 - host_dc: ldc-beta - llvm_version: 11.1.0 - cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON - - job_name: Ubuntu 20.04, LLVM 9, latest DMD beta + cmake_flags: -DBUILD_SHARED_LIBS=ON -DLIB_SUFFIX=64 + - job_name: Ubuntu 20.04, LLVM 11, latest DMD beta os: ubuntu-20.04 host_dc: dmd-beta - llvm_version: 9.0.1 + llvm_version: 11.1.0 cmake_flags: -DBUILD_SHARED_LIBS=OFF -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON - - job_name: macOS 11, LLVM 10, latest DMD beta - os: macos-11 - host_dc: dmd-beta - llvm_version: 10.0.1 - cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 + #- job_name: macOS 11, LLVM 10, latest DMD beta + # os: macos-11 + # host_dc: dmd-beta + # llvm_version: 10.0.1 + # cmake_flags: -DBUILD_SHARED_LIBS=ON -DRT_SUPPORT_SANITIZERS=ON -DLDC_LINK_MANUALLY=ON -DCMAKE_CXX_COMPILER=/usr/bin/c++ -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 - job_name: macOS 11, LLVM 13, latest LDC beta os: macos-11 host_dc: ldc-beta @@ -132,10 +127,6 @@ jobs: if: success() || failure() run: | set -eux - # LLVM 9: libclang_rt.fuzzer-x86_64.a not compiled with -fPIC - if [[ '${{ matrix.llvm_version }}' = 9.* ]]; then - echo "config.available_features.remove('Fuzzer')" >> tests/sanitizers/lit.local.cfg - fi # LLVM 14+ on Linux: don't use vanilla llvm-symbolizer (no support for zlib-compressed debug sections => failing ASan tests) if [[ '${{ runner.os }}' == 'Linux' && '${{ matrix.llvm_version }}' =~ ^1[4-9]\. ]]; then mv llvm/bin/llvm-symbolizer llvm/bin/llvm-symbolizer.bak diff --git a/CHANGELOG.md b/CHANGELOG.md index 9356f42fed2..de9002914d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) #### Platform support +- Supports LLVM 11.0 - 15.0. #### Bug fixes diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c34a3e7710..5973ad29a67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ endfunction() # Locate LLVM. # -find_package(LLVM 9.0 REQUIRED +find_package(LLVM 11.0 REQUIRED all-targets analysis asmparser asmprinter bitreader bitwriter codegen core debuginfodwarf debuginfomsf debuginfopdb demangle instcombine ipo instrumentation irreader libdriver linker lto mc @@ -46,9 +46,9 @@ foreach(LLVM_SUPPORTED_TARGET ${LLVM_TARGETS_TO_BUILD}) add_definitions("-DLDC_LLVM_SUPPORTED_TARGET_${LLVM_SUPPORTED_TARGET}=1") endforeach() -# Set MLIR support variables if it is found. Only try for LLVM >= 10.0. +# Set MLIR support variables if it is found. # FIXME: LLVM 14+ (`mlir::OwningModuleRef` replacement) -if(NOT (LDC_LLVM_VER LESS 1000) AND NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400) +if(NOT LDC_WITH_MLIR STREQUAL "OFF" AND LDC_LLVM_VER LESS 1400) include(FindMLIR) if(MLIR_FOUND) message(STATUS "-- Building LDC with MLIR support (${MLIR_ROOT_DIR})") @@ -593,12 +593,10 @@ if(MSVC) endif() endif() if(LDC_WITH_LLD) - if(NOT (LDC_LLVM_VER LESS 1000)) - if(MSVC) - list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib) - else() - set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST}) - endif() + if(MSVC) + list(APPEND LDC_LINKERFLAG_LIST LLVMSymbolize.lib) + else() + set(LDC_LINKERFLAG_LIST -lLLVMSymbolize ${LDC_LINKERFLAG_LIST}) endif() if (LDC_LLVM_VER LESS 1200 OR NOT LDC_LLVM_VER LESS 1400) set(LLD_MACHO lldMachO) diff --git a/cmake/Modules/FindLLVM.cmake b/cmake/Modules/FindLLVM.cmake index 693227b5b08..e55fb6301ab 100644 --- a/cmake/Modules/FindLLVM.cmake +++ b/cmake/Modules/FindLLVM.cmake @@ -37,8 +37,6 @@ set(llvm_config_names llvm-config-15.0 llvm-config150 llvm-config-15 llvm-config-13.0 llvm-config130 llvm-config-13 llvm-config-12.0 llvm-config120 llvm-config-12 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) find_program(LLVM_CONFIG NAMES ${llvm_config_names} @@ -51,11 +49,11 @@ if(APPLE) NAMES ${llvm_config_names} PATHS /opt/local/libexec/llvm-15/bin /opt/local/libexec/llvm-14/bin /opt/local/libexec/llvm-13/bin /opt/local/libexec/llvm-12/bin - /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm-10/bin /opt/local/libexec/llvm-9.0/bin + /opt/local/libexec/llvm-11/bin /opt/local/libexec/llvm/bin /usr/local/opt/llvm@15/bin /usr/local/opt/llvm@14/bin /usr/local/opt/llvm@13/bin /usr/local/opt/llvm@12/bin - /usr/local/opt/llvm@11/bin /usr/local/opt/llvm@10/bin /usr/local/opt/llvm@9/bin + /usr/local/opt/llvm@11/bin /usr/local/opt/llvm/bin NO_DEFAULT_PATH) endif() diff --git a/driver/archiver.cpp b/driver/archiver.cpp index bf5ce937d14..6ff14f22a8d 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -20,9 +20,7 @@ #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 bb5af90b1a1..9e9278847da 100644 --- a/driver/cache.cpp +++ b/driver/cache.cpp @@ -73,20 +73,12 @@ static std::error_code 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 @@ -100,11 +92,7 @@ std::error_code 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; diff --git a/driver/cl_options-llvm.cpp b/driver/cl_options-llvm.cpp index dd2e18f5268..657029f81fd 100644 --- a/driver/cl_options-llvm.cpp +++ b/driver/cl_options-llvm.cpp @@ -15,51 +15,28 @@ // Pull in command-line options and helper functions from special LLVM header // shared by multiple LLVM tools. -#if LDC_LLVM_VER >= 1100 #include "llvm/CodeGen/CommandFlags.h" static llvm::codegen::RegisterCodeGenFlags CGF; using namespace llvm; -#else -#include "llvm/CodeGen/CommandFlags.inc" -#endif static cl::opt DisableRedZone("disable-red-zone", cl::ZeroOrMore, cl::desc("Do not emit code that uses the red zone.")); -#if LDC_LLVM_VER < 1100 -// legacy option -static cl::opt - disableFPElim("disable-fp-elim", cl::ZeroOrMore, cl::ReallyHidden, - cl::desc("Disable frame pointer elimination optimization")); -#endif - // Now expose the helper functions (with static linkage) via external wrappers // in the opts namespace, including some additional helper functions. namespace opts { std::string getArchStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getMArch(); -#else - return ::MArch; -#endif } Optional getRelocModel() { -#if LDC_LLVM_VER >= 1100 return codegen::getExplicitRelocModel(); -#else - return ::getRelocModel(); -#endif } Optional getCodeModel() { -#if LDC_LLVM_VER >= 1100 return codegen::getExplicitCodeModel(); -#else - return ::getCodeModel(); -#endif } #if LDC_LLVM_VER >= 1300 @@ -69,26 +46,16 @@ using FPK = llvm::FramePointer::FP; #endif 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) - return ::FramePointerUsage.getValue(); - if (disableFPElim.getNumOccurrences() > 0) - return disableFPElim ? llvm::FramePointer::All : llvm::FramePointer::None; - return llvm::None; -#endif } 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(), @@ -98,39 +65,23 @@ bool printTargetFeaturesHelp() { TargetOptions InitTargetOptionsFromCodeGenFlags(const llvm::Triple &triple) { #if LDC_LLVM_VER >= 1200 return codegen::InitTargetOptionsFromCodeGenFlags(triple); -#elif LDC_LLVM_VER >= 1100 - return codegen::InitTargetOptionsFromCodeGenFlags(); #else - return ::InitTargetOptionsFromCodeGenFlags(); + return codegen::InitTargetOptionsFromCodeGenFlags(); #endif } std::string getCPUStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getCPUStr(); -#else - return ::getCPUStr(); -#endif } std::string getFeaturesStr() { -#if LDC_LLVM_VER >= 1100 return codegen::getFeaturesStr(); -#else - 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 @@ -142,11 +93,9 @@ TargetOptions initTargetOptionsFromCodeGenFlags() { return ::opts::InitTargetOptionsFromCodeGenFlags(llvm::Triple()); } -#if LDC_LLVM_VER >= 1000 Optional getRelocModelFromCMModel() { return ::opts::getRelocModel(); } -#endif Optional getCodeModelFromCMModel() { return ::opts::getCodeModel(); @@ -154,10 +103,6 @@ Optional getCodeModelFromCMModel() { std::string getCPUStr() { return ::opts::getCPUStr(); } -#if LDC_LLVM_VER >= 1100 std::vector getMAttrs() { return codegen::getMAttrs(); } -#else -std::vector getMAttrs() { return ::MAttrs; } -#endif } // namespace lld #endif // LDC_WITH_LLD diff --git a/driver/cl_options-llvm.h b/driver/cl_options-llvm.h index ba6379f144f..a0ed68613d4 100644 --- a/driver/cl_options-llvm.h +++ b/driver/cl_options-llvm.h @@ -38,8 +38,6 @@ InitTargetOptionsFromCodeGenFlags(const llvm::Triple &triple); 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/driver/cl_options_sanitizers.cpp b/driver/cl_options_sanitizers.cpp index 416568b209c..4994e8b162a 100644 --- a/driver/cl_options_sanitizers.cpp +++ b/driver/cl_options_sanitizers.cpp @@ -206,12 +206,8 @@ void initializeSanitizerOptionsFromCmdline() if (isAnySanitizerEnabled() && !fSanitizeBlacklist.empty()) { std::string loadError; - sanitizerBlacklist = - llvm::SpecialCaseList::create(fSanitizeBlacklist, -#if LDC_LLVM_VER >= 1000 - *llvm::vfs::getRealFileSystem(), -#endif - loadError); + sanitizerBlacklist = llvm::SpecialCaseList::create( + fSanitizeBlacklist, *llvm::vfs::getRealFileSystem(), loadError); if (!sanitizerBlacklist) error(Loc(), "-fsanitize-blacklist error: %s", loadError.c_str()); } diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index e15cfe22822..af3b19ce075 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -28,11 +28,7 @@ #if LDC_LLVM_VER >= 1400 #include "llvm/IR/DiagnosticInfo.h" #endif -#if LDC_LLVM_VER >= 1100 #include "llvm/IR/LLVMRemarkStreamer.h" -#else -#include "llvm/IR/RemarkStreamer.h" -#endif #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ToolOutputFile.h" @@ -66,13 +62,8 @@ createAndSetDiagnosticsOutputFile(IRState &irs, llvm::LLVMContext &ctx, // If there is instrumentation data available, also output function hotness const bool withHotness = opts::isUsingPGOProfile(); - auto remarksFileOrError = -#if LDC_LLVM_VER >= 1100 - llvm::setupLLVMOptimizationRemarks( -#else - llvm::setupOptimizationRemarks( -#endif - ctx, diagnosticsFilename, "", "", withHotness); + auto remarksFileOrError = llvm::setupLLVMOptimizationRemarks( + 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 888b2efc5bd..c25938a1c51 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -512,11 +512,7 @@ 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 f755258c95b..938dc25cb1e 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -759,10 +759,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -778,10 +776,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -789,14 +785,12 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, ); } else if (global.params.targetTriple->isOSBinFormatCOFF()) { success = lld::mingw::link(fullArgs -#if LDC_LLVM_VER >= 1000 && LDC_LLVM_VER < 1400 +#if LDC_LLVM_VER < 1400 , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false @@ -813,10 +807,8 @@ int linkObjToBinaryGcc(llvm::StringRef outputPath, , CanExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , CanExitEarly, false diff --git a/driver/linker-msvc.cpp b/driver/linker-msvc.cpp index 43c162e6444..15830d5182a 100644 --- a/driver/linker-msvc.cpp +++ b/driver/linker-msvc.cpp @@ -276,10 +276,8 @@ int linkObjToBinaryMSVC(llvm::StringRef outputPath, , canExitEarly #endif -#if LDC_LLVM_VER >= 1000 , llvm::outs(), llvm::errs() -#endif #if LDC_LLVM_VER >= 1400 , canExitEarly, false diff --git a/driver/toobj.cpp b/driver/toobj.cpp index b25a8773cce..48c8d6407ad 100644 --- a/driver/toobj.cpp +++ b/driver/toobj.cpp @@ -45,13 +45,7 @@ #include #include -#if LDC_LLVM_VER < 1000 -using CodeGenFileType = llvm::TargetMachine::CodeGenFileType; -constexpr CodeGenFileType CGFT_AssemblyFile = llvm::TargetMachine::CGFT_AssemblyFile; -constexpr CodeGenFileType CGFT_ObjectFile = llvm::TargetMachine::CGFT_ObjectFile; -#else using CodeGenFileType = llvm::CodeGenFileType; -#endif static llvm::cl::opt NoIntegratedAssembler("no-integrated-as", llvm::cl::ZeroOrMore, diff --git a/gen/abi/generic.h b/gen/abi/generic.h index e1e6f452f44..19b7aee0362 100644 --- a/gen/abi/generic.h +++ b/gen/abi/generic.h @@ -36,12 +36,8 @@ struct LLTypeMemoryLayout { const size_t sizeInBits = getTypeBitSize(type); assert(sizeInBits % 8 == 0); return llvm::VectorType::get(LLIntegerType::get(gIR->context(), 8), - sizeInBits / 8 -#if LDC_LLVM_VER >= 1100 - , - /*Scalable=*/false -#endif - ); + sizeInBits / 8, + /*Scalable=*/false); } if (LLStructType *structType = isaStruct(type)) { diff --git a/gen/ctfloat.cpp b/gen/ctfloat.cpp index 0ba8a5e838c..ef033c018df 100644 --- a/gen/ctfloat.cpp +++ b/gen/ctfloat.cpp @@ -26,11 +26,8 @@ union CTFloatUnion { APFloat parseLiteral(const llvm::fltSemantics &semantics, const char *literal, bool *isOutOfRange = nullptr) { APFloat ap(semantics, APFloat::uninitialized); - auto r = -#if LDC_LLVM_VER >= 1000 - llvm::cantFail -#endif - (ap.convertFromString(literal, APFloat::rmNearestTiesToEven)); + auto r = llvm::cantFail( + ap.convertFromString(literal, APFloat::rmNearestTiesToEven)); if (isOutOfRange) { *isOutOfRange = (r & (APFloat::opOverflow | APFloat::opUnderflow)) != 0; } diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 1e817ee3f4a..f32b3631235 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -385,12 +385,8 @@ DIType DIBuilder::CreateVectorType(TypeVector *type) { LLType *T = DtoType(type); const auto dim = type->basetype->isTypeSArray()->dim->toInteger(); -#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) @@ -669,13 +665,9 @@ DIType DIBuilder::CreateSArrayType(TypeSArray *type) { for (; te->ty == TY::Tsarray; te = te->nextOf()) { TypeSArray *tsa = static_cast(te); 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); } @@ -1292,9 +1284,7 @@ void DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar, vd->loc.linnum, // line num CreateTypeDescription(vd->type), // type vd->visibility.kind == Visibility::private_, // is local to unit -#if LDC_LLVM_VER >= 1000 !(vd->storage_class & STCextern), // bool isDefined -#endif nullptr, // DIExpression *Expr Decl // declaration ); diff --git a/gen/dynamiccompile.cpp b/gen/dynamiccompile.cpp index f0a0906c3f9..b982013c4f3 100644 --- a/gen/dynamiccompile.cpp +++ b/gen/dynamiccompile.cpp @@ -766,12 +766,8 @@ 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/gen/funcgenstate.cpp b/gen/funcgenstate.cpp index a59ec360e80..52cf698e40b 100644 --- a/gen/funcgenstate.cpp +++ b/gen/funcgenstate.cpp @@ -124,11 +124,7 @@ LLCallBasePtr 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()) { auto call = irs.ir->CreateCall(calleeArg, args, BundleList, name); diff --git a/gen/functions.cpp b/gen/functions.cpp index 701cd602ea4..adbdf4c5b7c 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -217,9 +217,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, // opaque struct if (!opts::fNullPointerIsValid) attrs.addAttribute(LLAttribute::NonNull); -#if LDC_LLVM_VER >= 1100 attrs.addAttribute(LLAttribute::NoUndef); -#endif } else { attrs.addDereferenceableAttr(loweredDType->size()); } @@ -498,42 +496,11 @@ void applyTargetMachineAttributes(llvm::Function &func, 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). - // TODO: implement interface for this option. - const bool lessPreciseFPMADOption = false; - func.addFnAttr("less-precise-fpmad", - lessPreciseFPMADOption ? "true" : "false"); - func.addFnAttr("no-infs-fp-math", TO.NoInfsFPMath ? "true" : "false"); - func.addFnAttr("no-nans-fp-math", TO.NoNaNsFPMath ? "true" : "false"); - - switch (whichFramePointersToEmit()) { - case llvm::FramePointer::None: - func.addFnAttr("frame-pointer", "none"); - break; - case llvm::FramePointer::NonLeaf: - func.addFnAttr("frame-pointer", "non-leaf"); - break; - case llvm::FramePointer::All: - func.addFnAttr("frame-pointer", "all"); - break; - } -#endif // LDC_LLVM_VER < 1000 } void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { @@ -1255,11 +1222,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { } applyXRayAttributes(*fd, *func); if (opts::fNullPointerIsValid) { -#if LDC_LLVM_VER >= 1100 func->addFnAttr(LLAttribute::NullPointerIsValid); -#else - func->addFnAttr("null-pointer-is-valid", "true"); -#endif } if (opts::fSplitStack && !hasNoSplitStackUDA(fd)) { func->addFnAttr("split-stack"); diff --git a/gen/llvm.h b/gen/llvm.h index daebd1f2e7c..86dd6cdd47c 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -31,30 +31,19 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/DebugInfo.h" -#if LDC_LLVM_VER >= 1000 // LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back // and point to std::make_unique. #include namespace llvm { using std::make_unique; } -#endif using llvm::APFloat; 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 #define GET_INTRINSIC_DECL(_X) \ (llvm::Intrinsic::getDeclaration(&gIR->module, llvm::Intrinsic::_X)) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 46488f5d903..ae9bed70cdc 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1202,10 +1202,8 @@ LLConstant *DtoConstExpInit(const Loc &loc, Type *targetType, Expression *exp) { static_cast(tv->basetype)->dim->toInteger(); #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(elemCount); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(elemCount, false); #else - const auto elementCount = elemCount; + const auto elementCount = llvm::ElementCount(elemCount, false); #endif return llvm::ConstantVector::getSplat(elementCount, val); } @@ -1281,11 +1279,7 @@ 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' << vectorType->getNumElements() << prefix << gDataLayout->getTypeSizeInBits(vectorType->getElementType()); diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index 29cc8c2f43d..2997e52ae38 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -49,9 +49,7 @@ #include "llvm/Transforms/Scalar/LICM.h" #include "llvm/Transforms/Scalar/Reassociate.h" #endif -#if LDC_LLVM_VER >= 1000 #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" -#endif extern llvm::TargetMachine *gTargetMachine; using namespace llvm; @@ -141,15 +139,6 @@ bool willCrossModuleInline() { return enableCrossModuleInlining == llvm::cl::BOU_TRUE && willInline(); } -#if LDC_LLVM_VER < 1000 -llvm::FramePointer::FP whichFramePointersToEmit() { - if (auto option = opts::framePointerUsage()) - return *option; - return isOptimizationEnabled() ? llvm::FramePointer::None - : llvm::FramePointer::All; -} -#endif - bool isOptimizationEnabled() { return optimizeLevel != 0; } llvm::CodeGenOpt::Level codeGenOptLevel() { @@ -238,13 +227,8 @@ static void legacyAddThreadSanitizerPass(const PassManagerBuilder &Builder, static void legacyAddSanitizerCoveragePass(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { -#if LDC_LLVM_VER >= 1000 PM.add(createModuleSanitizerCoverageLegacyPassPass( opts::getSanitizerCoverageOptions())); -#else - PM.add( - createSanitizerCoverageModulePass(opts::getSanitizerCoverageOptions())); -#endif } // Adds PGO instrumentation generation and use passes. diff --git a/gen/optimizer.h b/gen/optimizer.h index cbe3adb8fa2..0b3da829fb6 100644 --- a/gen/optimizer.h +++ b/gen/optimizer.h @@ -35,10 +35,6 @@ bool willInline(); bool willCrossModuleInline(); -#if LDC_LLVM_VER < 1000 -llvm::FramePointer::FP whichFramePointersToEmit(); -#endif - unsigned optLevel(); bool isOptimizationEnabled(); diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 7684906a7de..a6946dbfc73 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -71,11 +71,7 @@ void EmitMemSet(IRBuilder<> &B, Value *Dst, Value *Val, Value *Len, const G2StackAnalysis &A) { Dst = B.CreateBitCast(Dst, PointerType::getUnqual(B.getInt8Ty())); -#if LDC_LLVM_VER >= 1000 MaybeAlign Align(1); -#else - unsigned Align = 1; -#endif auto CS = B.CreateMemSet(Dst, Val, Len, Align, false /*isVolatile*/); if (A.CGNode) { diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 918cd5d91fb..4a4e8422772 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -708,10 +708,8 @@ class ToConstElemVisitor : public Visitor { // constructed. #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(elemCount); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(elemCount, false); #else - const auto elementCount = elemCount; + const auto elementCount = llvm::ElementCount(elemCount, false); #endif result = llvm::ConstantVector::getSplat( elementCount, toConstElem(e->e1->optimize(WANTvalue), p)); diff --git a/gen/toir.cpp b/gen/toir.cpp index 403ff996d8b..5433060b144 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2723,10 +2723,8 @@ class ToElemVisitor : public Visitor { if (auto llConstant = isaConstant(llElement)) { #if LDC_LLVM_VER >= 1200 const auto elementCount = llvm::ElementCount::getFixed(N); -#elif LDC_LLVM_VER >= 1100 - const auto elementCount = llvm::ElementCount(N, false); #else - const auto elementCount = N; + const auto elementCount = llvm::ElementCount(N, false); #endif auto vectorConstant = llvm::ConstantVector::getSplat(elementCount, llConstant); diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp index f27e42ad656..4b6dfe05963 100644 --- a/gen/trycatchfinally.cpp +++ b/gen/trycatchfinally.cpp @@ -371,11 +371,8 @@ 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( -#if LDC_LLVM_VER >= 1100 - branchSelectorType, -#endif - branchSelector, "", endBlock()); + llvm::Value *sel = + new llvm::LoadInst(branchSelectorType, 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 371b565b38a..1cad8a21349 100644 --- a/gen/uda.cpp +++ b/gen/uda.cpp @@ -17,15 +17,6 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" -#if LDC_LLVM_VER < 1100 -namespace llvm { -// Auto-generate: -// Attribute::AttrKind getAttrKindFromName(StringRef AttrName) { ... } -#define GET_ATTR_KIND_FROM_NAME -#include "llvm/IR/Attributes.inc" -} -#endif - namespace { /// Checks whether `moduleDecl` is in the ldc package and it's identifier is @@ -238,11 +229,7 @@ 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 { @@ -423,9 +410,7 @@ bool parseCallingConvention(llvm::StringRef name, .Case("ccc", llvm::CallingConv::C) .Case("fastcc", llvm::CallingConv::Fast) .Case("coldcc", llvm::CallingConv::Cold) -#if LDC_LLVM_VER >= 1000 .Case("cfguard_checkcc", llvm::CallingConv::CFGuard_Check) -#endif .Case("x86_stdcallcc", llvm::CallingConv::X86_StdCall) .Case("x86_fastcallcc", llvm::CallingConv::X86_FastCall) .Case("x86_regcallcc", llvm::CallingConv::X86_RegCall) @@ -435,10 +420,8 @@ bool parseCallingConvention(llvm::StringRef name, .Case("arm_aapcscc", llvm::CallingConv::ARM_AAPCS) .Case("arm_aapcs_vfpcc", llvm::CallingConv::ARM_AAPCS_VFP) .Case("aarch64_vector_pcs", llvm::CallingConv::AArch64_VectorCall) -#if LDC_LLVM_VER >= 1000 .Case("aarch64_sve_vector_pcs", llvm::CallingConv::AArch64_SVE_VectorCall) -#endif .Case("msp430_intrcc", llvm::CallingConv::MSP430_INTR) .Case("avr_intrcc", llvm::CallingConv::AVR_INTR) .Case("avr_signalcc", llvm::CallingConv::AVR_SIGNAL) @@ -473,9 +456,7 @@ bool parseCallingConvention(llvm::StringRef name, .Case("amdgpu_ps", llvm::CallingConv::AMDGPU_PS) .Case("amdgpu_cs", llvm::CallingConv::AMDGPU_CS) .Case("amdgpu_kernel", llvm::CallingConv::AMDGPU_KERNEL) -#if LDC_LLVM_VER >= 1000 .Case("tailcc", llvm::CallingConv::Tail) -#endif .Case("default", llvm::CallingConv::MaxID - 1) .Default(llvm::CallingConv::MaxID); diff --git a/ir/irtype.cpp b/ir/irtype.cpp index 0553fbd7b3f..0cf987d9284 100644 --- a/ir/irtype.cpp +++ b/ir/irtype.cpp @@ -199,12 +199,8 @@ IrTypeVector *IrTypeVector::get(Type *dt) { // Could have already built the type as part of a struct forward reference, // just as for pointers and arrays. if (!ctype) { - LLType *lt = llvm::VectorType::get(elemType, tsa->dim->toUInteger() -#if LDC_LLVM_VER >= 1100 - , - /*Scalable=*/false -#endif - ); + LLType *lt = llvm::VectorType::get(elemType, tsa->dim->toUInteger(), + /*Scalable=*/false); ctype = new IrTypeVector(dt, lt); } diff --git a/runtime/druntime/src/ldc/intrinsics.di b/runtime/druntime/src/ldc/intrinsics.di index c1950e4fcca..f18bf5035dc 100644 --- a/runtime/druntime/src/ldc/intrinsics.di +++ b/runtime/druntime/src/ldc/intrinsics.di @@ -19,9 +19,7 @@ else static assert(false, "This module is only valid for LDC"); } - version (LDC_LLVM_900) enum LLVM_version = 900; -else version (LDC_LLVM_1000) enum LLVM_version = 1000; -else version (LDC_LLVM_1100) enum LLVM_version = 1100; + version (LDC_LLVM_1100) enum LLVM_version = 1100; else version (LDC_LLVM_1101) enum LLVM_version = 1101; else version (LDC_LLVM_1200) enum LLVM_version = 1200; else version (LDC_LLVM_1300) enum LLVM_version = 1300; @@ -56,9 +54,7 @@ pragma(LDC_intrinsic, "llvm.returnaddress") /// The 'llvm.frameaddress' intrinsic attempts to return the target-specific /// frame pointer value for the specified stack frame. -pragma(LDC_intrinsic, - LLVM_version >= 1000 ? "llvm.frameaddress."~p0i8 : - "llvm.frameaddress") +pragma(LDC_intrinsic, "llvm.frameaddress."~p0i8) void* llvm_frameaddress(uint level); /// The 'llvm.stacksave' intrinsic is used to remember the current state of the @@ -85,9 +81,7 @@ pragma(LDC_intrinsic, "llvm.stackrestore") /// keep in cache. The cache type specifies whether the prefetch is performed on /// the data (1) or instruction (0) cache. The rw, locality and cache type /// arguments must be constant integers. -pragma(LDC_intrinsic, - LLVM_version >= 1000 ? "llvm.prefetch."~p0i8 : - "llvm.prefetch") +pragma(LDC_intrinsic, "llvm.prefetch."~p0i8) void llvm_prefetch(const(void)* ptr, uint rw, uint locality, uint cachetype) pure @safe; /// The 'llvm.pcmarker' intrinsic is a method to export a Program Counter (PC) diff --git a/runtime/jit-rt/cpp-so/bind.cpp b/runtime/jit-rt/cpp-so/bind.cpp index 05d2b260c0e..7f0347d9ab8 100644 --- a/runtime/jit-rt/cpp-so/bind.cpp +++ b/runtime/jit-rt/cpp-so/bind.cpp @@ -18,17 +18,8 @@ #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 namespace { enum { SmallParamsCount = 5 }; diff --git a/runtime/jit-rt/cpp-so/disassembler.cpp b/runtime/jit-rt/cpp-so/disassembler.cpp index 44bffba7c14..32a817c570d 100644 --- a/runtime/jit-rt/cpp-so/disassembler.cpp +++ b/runtime/jit-rt/cpp-so/disassembler.cpp @@ -35,11 +35,9 @@ #endif #include "llvm/Target/TargetMachine.h" -#if LDC_LLVM_VER >= 1000 namespace llvm { using std::make_unique; } -#endif namespace { template std::unique_ptr unique(T *ptr) { @@ -132,28 +130,15 @@ void printFunction(const llvm::MCDisassembler &disasm, std::string comment; llvm::raw_string_ostream commentStream(comment); auto status = disasm.getInstruction(inst, size, data.slice(pos), pos, -#if LDC_LLVM_VER < 1000 - llvm::nulls(), -#endif commentStream); switch (status) { case llvm::MCDisassembler::Fail: -#if LDC_LLVM_VER >= 1100 - streamer.emitRawText( -#else - streamer.EmitRawText( -#endif - "failed to disassemble"); + streamer.emitRawText("failed to disassemble"); return; case llvm::MCDisassembler::SoftFail: -#if LDC_LLVM_VER >= 1100 - streamer.emitRawText( -#else - streamer.EmitRawText( -#endif - "potentially undefined instruction encoding:"); + streamer.emitRawText("potentially undefined instruction encoding:"); LLVM_FALLTHROUGH; case llvm::MCDisassembler::Success: @@ -166,22 +151,14 @@ 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; } @@ -320,11 +297,7 @@ 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); } } @@ -346,21 +319,13 @@ 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 for (const auto &globalSec : object.sections()) { -#if LDC_LLVM_VER >= 1000 auto rs = globalSec.getRelocatedSection(); if (rs && *rs == secIt) { -#else - if (globalSec.getRelocatedSection() == secIt) { -#endif processRelocations(symTable, offset, object, globalSec); } } @@ -377,11 +342,7 @@ 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 5e9c0cf4626..535647b17e2 100644 --- a/runtime/jit-rt/cpp-so/jit_context.cpp +++ b/runtime/jit-rt/cpp-so/jit_context.cpp @@ -185,12 +185,8 @@ 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()) { diff --git a/tests/PGO/lit.local.cfg b/tests/PGO/lit.local.cfg index 3cf5779cfbc..d5d1d48297c 100644 --- a/tests/PGO/lit.local.cfg +++ b/tests/PGO/lit.local.cfg @@ -1,7 +1,4 @@ # Add "PGO_RT" feature, assuming the `profile` compiler-rt library is available config.available_features.add('PGO_RT') -if (config.llvm_version >= 700): - config.substitutions.append( ('%allow-deprecated-dag-overlap ', '-allow-deprecated-dag-overlap ') ) -else: - config.substitutions.append( ('%allow-deprecated-dag-overlap ', '') ) +config.substitutions.append( ('%allow-deprecated-dag-overlap ', '-allow-deprecated-dag-overlap ') ) diff --git a/tests/instrument/xray_link.d b/tests/instrument/xray_link.d index 83c498c2747..d05d174ea1e 100644 --- a/tests/instrument/xray_link.d +++ b/tests/instrument/xray_link.d @@ -2,7 +2,7 @@ // 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 +// XFAIL: Darwin // RUN: %ldc -fxray-instrument -fxray-instruction-threshold=1 -of=%t%exe %s -vv 2>&1 | FileCheck %s diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index f18b6f4d3d9..484b7246de2 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -217,12 +217,8 @@ if (platform.system() != 'Windows') and lit.util.which('gdb', config.environment config.substitutions.append( ('%_gdb_dflags', gdb_dflags) ) # Add substitutions for functionality across different LLVM versions -if config.llvm_version >= 800: - config.substitutions.append( ('%disable_fp_elim', '-frame-pointer=all') ) - config.substitutions.append( ('%enable_fp_elim', '-frame-pointer=none') ) -else: - config.substitutions.append( ('%disable_fp_elim', '-disable-fp-elim') ) - config.substitutions.append( ('%enable_fp_elim', '-disable-fp-elim=false') ) +config.substitutions.append( ('%disable_fp_elim', '-frame-pointer=all') ) +config.substitutions.append( ('%enable_fp_elim', '-frame-pointer=none') ) if 'LD_LIBRARY_PATH' in os.environ: libs = [] diff --git a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp index 065b3b086a1..18b6ab66c61 100644 --- a/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp +++ b/tests/plugins/addFuncEntryCall/addFuncEntryCallPass.cpp @@ -37,11 +37,7 @@ bool FuncEntryCallPass::doInitialization(Module &M) { // Add fwd declaration of the `void __test_funcentrycall(void)` function. auto functionType = FunctionType::get(Type::getVoidTy(M.getContext()), false); funcToCallUponEntry = - M.getOrInsertFunction("__test_funcentrycall", functionType) -#if LLVM_VERSION >= 900 - .getCallee() -#endif - ; + M.getOrInsertFunction("__test_funcentrycall", functionType).getCallee(); return true; } @@ -50,11 +46,7 @@ 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; } diff --git a/tests/sanitizers/lsan_memleak.d b/tests/sanitizers/lsan_memleak.d index 6fc7c16ac35..de9a654a13a 100644 --- a/tests/sanitizers/lsan_memleak.d +++ b/tests/sanitizers/lsan_memleak.d @@ -1,6 +1,6 @@ // Test leak detection with LSan -// REQUIRES: LSan && atleast_llvm1000 +// REQUIRES: LSan // UNSUPPORTED: Windows, FreeBSD diff --git a/tools/ldc-profdata/llvm-profdata-10.0.cpp b/tools/ldc-profdata/llvm-profdata-10.0.cpp deleted file mode 100644 index 41e9abb82b1..00000000000 --- a/tools/ldc-profdata/llvm-profdata-10.0.cpp +++ /dev/null @@ -1,1178 +0,0 @@ -//===- 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/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Threading.h" -#include "llvm/Support/ThreadPool.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(), Whence, Hint); - }); - } - - exitWithError(toString(std::move(E)), Whence); -} - -static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), 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(), 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 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(), 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, Contexts[0].get()); - } else { - ThreadPool Pool(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."); - - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } -} - -/// 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[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) { - 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 { - auto ExtBinaryWriter = - static_cast(&Writer); - ExtBinaryWriter->setToCompressAllSections(); - } - } -} - -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, - StringRef ProfileSymbolListFile, - bool CompressAllSections, 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); - 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 {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({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({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, {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::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - - WeightedFileVector WeightedInputs; - for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {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, - 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 << "Detailed summary:\n"; - OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; - OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\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; - } -} - -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, - const std::string &ShowFunction, - bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, 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); - } - - 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 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, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, 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/tools/ldc-profdata/llvm-profdata-9.0.cpp b/tools/ldc-profdata/llvm-profdata-9.0.cpp deleted file mode 100644 index 16d3ebe3fcb..00000000000 --- a/tools/ldc-profdata/llvm-profdata-9.0.cpp +++ /dev/null @@ -1,1087 +0,0 @@ -//===- 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/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/ThreadPool.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_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(), Whence, Hint); - }); - } - - exitWithError(toString(std::move(E)), Whence); -} - -static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); -} - -namespace { -enum ProfileKinds { instr, sample }; -} - -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 = llvm::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; - Error Err; - std::string ErrWhence; - std::mutex &ErrLock; - SmallSet &WriterErrorCodes; - - WriterContext(bool IsSparse, std::mutex &ErrLock, - SmallSet &WriterErrorCodes) - : Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""), - ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} -}; - -/// Determine whether an error is fatal for profile merging. -static bool isFatalError(instrprof_error IPE) { - switch (IPE) { - default: - return true; - case instrprof_error::success: - case instrprof_error::eof: - case instrprof_error::unknown_function: - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::counter_overflow: - case instrprof_error::value_site_count_mismatch: - return false; - } -} - -/// 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->Err = make_error(IPE); - 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}; - - // If there's a pending hard error, don't do more work. - if (WC->Err) - return; - - // 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. - WC->ErrWhence = 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->Err = make_error(IPE); - return; - } - - auto Reader = std::move(ReaderOrErr.get()); - bool IsIRProfile = Reader->isIRLevelProfile(); - bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { - WC->Err = make_error( - "Merge IR generated profile with Clang generated profile.", - std::error_code()); - 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()) { - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (isFatalError(IPE)) - WC->Err = make_error(IPE); - } - } -} - -/// Merge the \p Src writer context into \p Dst. -static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - // If we've already seen a hard error, continuing with the merge would - // clobber it. - if (Dst->Err || Src->Err) - return; - - bool Reported = false; - Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - Dst->Err = std::move(E); - }); -} - -static void mergeInstrProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, bool OutputSparse, - unsigned NumThreads) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_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(), unsigned((Inputs.size() + 1) / 2)); - - // Initialize the writer contexts. - SmallVector, 4> Contexts; - for (unsigned I = 0; I < NumThreads; ++I) - Contexts.emplace_back(llvm::make_unique( - OutputSparse, ErrorLock, WriterErrorCodes)); - - if (NumThreads == 1) { - for (const auto &Input : Inputs) - loadInput(Input, Remapper, Contexts[0].get()); - } else { - ThreadPool Pool(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 hard errors encountered during merging. - for (std::unique_ptr &WC : Contexts) { - if (!WC->Err) - continue; - if (!WC->Err.isA()) - exitWithError(std::move(WC->Err), WC->ErrWhence); - - instrprof_error IPE = InstrProfError::take(std::move(WC->Err)); - if (isFatalError(IPE)) - exitWithError(make_error(IPE), WC->ErrWhence); - else - warn(toString(make_error(IPE)), - WC->ErrWhence); - } - - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } -} - -/// 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[Remapped.getName()].merge(Remapped)); - } - } - return Result; -} - -static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, - sampleprof::SPF_GCC, sampleprof::SPF_Binary}; - -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat) { - using namespace sampleprof; - StringMap ProfileMap; - SmallVector, 5> Readers; - LLVMContext Context; - for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Input.Filename); - - // 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()) - exitWithErrorCode(EC, Input.Filename); - - 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); - } - } - } - auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); - if (std::error_code EC = WriterOrErr.getError()) - exitWithErrorCode(EC, OutputFilename); - - auto Writer = std::move(WriterOrErr.get()); - 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 {FileName, Weight}; -} - -static std::unique_ptr -getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) { - if (InputFilenamesFile == "") - return {}; - - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFilenamesFile); - - return std::move(*BufOrError); -} - -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({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({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, {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_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); - 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::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - - WeightedFileVector WeightedInputs; - for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {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 = getInputFilenamesFileBuf(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); - else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat); - - 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.accumuateCounts(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::F_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 << "Detailed summary:\n"; - OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; - OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } - } - return 0; -} - -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, - const std::string &ShowFunction, - 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 (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Filename); - - if (ShowAllFunctions || ShowFunction.empty()) - Reader->dump(OS); - else - Reader->dumpFunctionProfile(ShowFunction, 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 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::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::F_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, - ShowFunction, 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-10.cpp b/utils/FileCheck-10.cpp deleted file mode 100644 index 6f5791354ec..00000000000 --- a/utils/FileCheck-10.cpp +++ /dev/null @@ -1,664 +0,0 @@ -//===- 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::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::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::desc("Print information helpful in diagnosing internal FileCheck\n" - "issues, or add it to the input dump if enabled. Implies\n" - "-v.\n")); -static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; - -static cl::opt DumpInputOnFailure( - "dump-input-on-failure", - cl::init(std::getenv(DumpInputEnv) && *std::getenv(DumpInputEnv)), - cl::desc("Dump original input to stderr before failing.\n" - "The value can be also controlled using\n" - "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" - "This option is deprecated in favor of -dump-input=fail.\n")); - -// The order of DumpInputValue members affects their precedence, as documented -// for -dump-input below. -enum DumpInputValue { - DumpInputDefault, - 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.\n"), - cl::value_desc("mode"), - cl::values(clEnumValN(DumpInputHelp, "help", - "Explain dump format and quit"), - clEnumValN(DumpInputAlways, "always", "Always dump input"), - clEnumValN(DumpInputFail, "fail", "Dump input on failure"), - clEnumValN(DumpInputNever, "never", "Never dump input"))); - -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; - MarkerStyle() {} - MarkerStyle(char Lead, raw_ostream::Colors Color, - const std::string &Note = "") - : Lead(Lead), Color(Color), Note(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"); - case FileCheckDiag::MatchFoundButWrongLine: - return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); - 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"); - case FileCheckDiag::MatchFuzzy: - return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); - } - 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 annotations printed by -dump-input=always and\n" - << "-dump-input=fail:\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 a pattern of type T from " - << "line L of\n" - << " the check file\n"; - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\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"; - - // 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\n" - << "If you are not seeing color above or in input dumps, try: -color\n"; -} - -/// An annotation for a single input line. -struct InputAnnotation { - /// The check file line (one-origin indexing) where the directive that - /// produced this annotation is located. - unsigned CheckLine; - /// The index of the match result for this check. - unsigned CheckDiagIndex; - /// The label for this annotation. - std::string Label; - /// What input line (one-origin indexing) this annotation marks. This might - /// be different from the starting line of the original diagnostic if this is - /// a non-initial fragment of a diagnostic that has been broken across - /// multiple lines. - unsigned InputLine; - /// The column range (one-origin indexing, open end) in which to 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::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 std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { - // 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; - - // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; - llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; - A.CheckDiagIndex = UINT_MAX; - auto DiagNext = std::next(DiagItr); - if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) - A.CheckDiagIndex = CheckDiagCount++; - else if (CheckDiagCount) { - A.CheckDiagIndex = CheckDiagCount; - CheckDiagCount = 0; - } - if (A.CheckDiagIndex != UINT_MAX) - Label << "'" << A.CheckDiagIndex; - else - A.CheckDiagIndex = 0; - 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.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.CheckLine = A.CheckLine; - B.CheckDiagIndex = A.CheckDiagIndex; - B.Label = A.Label; - 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 void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, - StringRef InputFileText, - std::vector &Annotations, - unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; - - // Sort annotations. - // - // First, sort in the order of input lines to make it easier to find relevant - // annotations while iterating input lines in the implementation below. - // FileCheck diagnostics are not always reported and recorded in the order of - // input lines due to, for example, CHECK-DAG and CHECK-NOT. - // - // Second, for annotations for the same input line, sort in the order of the - // FileCheck directive's line in the check file (where there's at most one - // directive per line) and then by the index of the match result for that - // directive. The rationale of this choice is that, for any input line, this - // sort establishes a total order of annotations that, with respect to match - // results, is consistent across multiple lines, thus making match results - // easier to track from one line to the next when they span multiple lines. - std::sort(Annotations.begin(), Annotations.end(), - [](const InputAnnotation &A, const InputAnnotation &B) { - if (A.InputLine != B.InputLine) - return A.InputLine < B.InputLine; - if (A.CheckLine != B.CheckLine) - return A.CheckLine < B.CheckLine; - // FIXME: Sometimes CHECK-LABEL reports its match twice with - // other diagnostics in between, and then diag index incrementing - // fails to work properly, and then this assert fails. We should - // suppress one of those diagnostics or do a better job of - // computing this index. For now, we just produce a redundant - // CHECK-LABEL annotation. - // assert(A.CheckDiagIndex != B.CheckDiagIndex && - // "expected diagnostic indices to be unique within a " - // " check line"); - return A.CheckDiagIndex < B.CheckDiagIndex; - }); - - // 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. - auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); - for (unsigned Line = 1; - InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; - ++Line) { - const unsigned char *InputFileLine = InputFilePtr; - - // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) - << 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 && WithColor(OS).colorsEnabled()) { - 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(OS); - 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; - } - } - OS << '\n'; - unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; - - // Print any annotations. - while (AnnotationItr != AnnotationEnd && - AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); - // 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; - } - } - - 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"); - DumpInputValue DumpInput = - DumpInputs.empty() - ? DumpInputDefault - : *std::max_element(DumpInputs.begin(), DumpInputs.end()); - if (DumpInput == DumpInputHelp) { - DumpInputAnnotationHelp(outs()); - return 0; - } - if (CheckFilename.empty()) { - errs() << " not specified\n"; - return 2; - } - - FileCheckRequest Req; - for (auto Prefix : CheckPrefixes) - Req.CheckPrefixes.push_back(Prefix); - - for (auto CheckNot : ImplicitCheckNot) - Req.ImplicitCheckNot.push_back(CheckNot); - - bool GlobalDefineError = false; - for (auto 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()) { - errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " - "start with a letter and contain only alphanumeric characters, " - "hyphens and underscores\n"; - 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); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); - - if (FC.readCheckFile(SM, CheckFileText, PrefixRE)) - return 2; - - // Open the file to check and add it to SourceMgr. - ErrorOr> InputFileOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); - 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()); - - if (DumpInput == DumpInputDefault) - DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; - - 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 == "-" ? "" : InputFilename.getValue()) - << "\n" - << "Check file: " << CheckFilename << "\n" - << "\n" - << "-dump-input=help describes the format of the following dump.\n" - << "\n"; - std::vector Annotations; - unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); - } - - return ExitCode; -} diff --git a/utils/FileCheck-9.cpp b/utils/FileCheck-9.cpp deleted file mode 100644 index c882736d4bf..00000000000 --- a/utils/FileCheck-9.cpp +++ /dev/null @@ -1,651 +0,0 @@ -//===- 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::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::opt NoCanonicalizeWhiteSpace( - "strict-whitespace", - cl::desc("Do not treat all horizontal whitespace as equivalent")); - -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::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::desc("Print information helpful in diagnosing internal FileCheck\n" - "issues, or add it to the input dump if enabled. Implies\n" - "-v.\n")); -static const char * DumpInputEnv = "FILECHECK_DUMP_INPUT_ON_FAILURE"; - -static cl::opt DumpInputOnFailure( - "dump-input-on-failure", cl::init(std::getenv(DumpInputEnv)), - cl::desc("Dump original input to stderr before failing.\n" - "The value can be also controlled using\n" - "FILECHECK_DUMP_INPUT_ON_FAILURE environment variable.\n" - "This option is deprecated in favor of -dump-input=fail.\n")); - -enum DumpInputValue { - DumpInputDefault, - DumpInputHelp, - DumpInputNever, - DumpInputFail, - DumpInputAlways -}; - -static cl::opt DumpInput( - "dump-input", cl::init(DumpInputDefault), - cl::desc("Dump input to stderr, adding annotations representing\n" - " currently enabled diagnostics\n"), - cl::value_desc("mode"), - cl::values(clEnumValN(DumpInputHelp, "help", - "Explain dump format and quit"), - clEnumValN(DumpInputNever, "never", "Never dump input"), - clEnumValN(DumpInputFail, "fail", "Dump input on failure"), - clEnumValN(DumpInputAlways, "always", "Always dump input"))); - -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; - MarkerStyle() {} - MarkerStyle(char Lead, raw_ostream::Colors Color, - const std::string &Note = "") - : Lead(Lead), Color(Color), Note(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"); - case FileCheckDiag::MatchFoundButWrongLine: - return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line"); - 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"); - case FileCheckDiag::MatchFuzzy: - return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match"); - } - 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 annotations printed by -dump-input=always and\n" - << "-dump-input=fail:\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 a pattern of type T from " - << "line L of\n" - << " the check file\n"; - OS << " - "; - WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N"; - OS << " labels the Nth match result for a pattern of type T from line " - << "L of\n" - << " the check file\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"; - - // 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\n" - << "If you are not seeing color above or in input dumps, try: -color\n"; -} - -/// An annotation for a single input line. -struct InputAnnotation { - /// The check file line (one-origin indexing) where the directive that - /// produced this annotation is located. - unsigned CheckLine; - /// The index of the match result for this check. - unsigned CheckDiagIndex; - /// The label for this annotation. - std::string Label; - /// What input line (one-origin indexing) this annotation marks. This might - /// be different from the starting line of the original diagnostic if this is - /// a non-initial fragment of a diagnostic that has been broken across - /// multiple lines. - unsigned InputLine; - /// The column range (one-origin indexing, open end) in which to 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::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 std::vector &Diags, - std::vector &Annotations, - unsigned &LabelWidth) { - // 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; - - // Build label, which uniquely identifies this check result. - A.CheckLine = DiagItr->CheckLine; - llvm::raw_string_ostream Label(A.Label); - Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":" - << DiagItr->CheckLine; - A.CheckDiagIndex = UINT_MAX; - auto DiagNext = std::next(DiagItr); - if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy && - DiagItr->CheckLine == DiagNext->CheckLine) - A.CheckDiagIndex = CheckDiagCount++; - else if (CheckDiagCount) { - A.CheckDiagIndex = CheckDiagCount; - CheckDiagCount = 0; - } - if (A.CheckDiagIndex != UINT_MAX) - Label << "'" << A.CheckDiagIndex; - else - A.CheckDiagIndex = 0; - Label.flush(); - LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size()); - - MarkerStyle Marker = GetMarker(DiagItr->MatchTy); - A.Marker = Marker; - A.FoundAndExpectedMatch = - DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected; - - // Compute the mark location, and break annotation into multiple - // annotations if it spans multiple lines. - 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; - A.Marker.Note = ""; - 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) { - Annotations.back().Marker.Note = Marker.Note; - break; - } - InputAnnotation B; - B.CheckLine = A.CheckLine; - B.CheckDiagIndex = A.CheckDiagIndex; - B.Label = A.Label; - B.InputLine = L; - B.Marker = Marker; - B.Marker.Lead = '~'; - B.InputStartCol = 1; - if (L != E) { - B.InputEndCol = UINT_MAX; - B.Marker.Note = ""; - } else - B.InputEndCol = DiagItr->InputEndCol; - B.FoundAndExpectedMatch = A.FoundAndExpectedMatch; - Annotations.push_back(B); - } - } - } -} - -static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req, - StringRef InputFileText, - std::vector &Annotations, - unsigned LabelWidth) { - OS << "Full input was:\n<<<<<<\n"; - - // Sort annotations. - // - // First, sort in the order of input lines to make it easier to find relevant - // annotations while iterating input lines in the implementation below. - // FileCheck diagnostics are not always reported and recorded in the order of - // input lines due to, for example, CHECK-DAG and CHECK-NOT. - // - // Second, for annotations for the same input line, sort in the order of the - // FileCheck directive's line in the check file (where there's at most one - // directive per line) and then by the index of the match result for that - // directive. The rationale of this choice is that, for any input line, this - // sort establishes a total order of annotations that, with respect to match - // results, is consistent across multiple lines, thus making match results - // easier to track from one line to the next when they span multiple lines. - std::sort(Annotations.begin(), Annotations.end(), - [](const InputAnnotation &A, const InputAnnotation &B) { - if (A.InputLine != B.InputLine) - return A.InputLine < B.InputLine; - if (A.CheckLine != B.CheckLine) - return A.CheckLine < B.CheckLine; - // FIXME: Sometimes CHECK-LABEL reports its match twice with - // other diagnostics in between, and then diag index incrementing - // fails to work properly, and then this assert fails. We should - // suppress one of those diagnostics or do a better job of - // computing this index. For now, we just produce a redundant - // CHECK-LABEL annotation. - // assert(A.CheckDiagIndex != B.CheckDiagIndex && - // "expected diagnostic indices to be unique within a " - // " check line"); - return A.CheckDiagIndex < B.CheckDiagIndex; - }); - - // 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. - auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end(); - for (unsigned Line = 1; - InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd; - ++Line) { - const unsigned char *InputFileLine = InputFilePtr; - - // Print right-aligned line number. - WithColor(OS, raw_ostream::BLACK, true) - << 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 && WithColor(OS).colorsEnabled()) { - 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(OS); - 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; - } - } - OS << '\n'; - unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline; - - // Print any annotations. - while (AnnotationItr != AnnotationEnd && - AnnotationItr->InputLine == Line) { - WithColor COS(OS, AnnotationItr->Marker.Color, true); - // 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; - } - } - - 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"); - if (DumpInput == DumpInputHelp) { - DumpInputAnnotationHelp(outs()); - return 0; - } - if (CheckFilename.empty()) { - errs() << " not specified\n"; - return 2; - } - - FileCheckRequest Req; - for (auto Prefix : CheckPrefixes) - Req.CheckPrefixes.push_back(Prefix); - - for (auto CheckNot : ImplicitCheckNot) - Req.ImplicitCheckNot.push_back(CheckNot); - - bool GlobalDefineError = false; - for (auto 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; - - if (VerboseVerbose) - Req.Verbose = true; - - FileCheck FC(Req); - if (!FC.ValidateCheckPrefixes()) { - errs() << "Supplied check-prefix is invalid! Prefixes must be unique and " - "start with a letter and contain only alphanumeric characters, " - "hyphens and underscores\n"; - 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); - - SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer( - CheckFileText, CheckFile.getBufferIdentifier()), - SMLoc()); - - std::vector CheckStrings; - if (FC.ReadCheckFile(SM, CheckFileText, PrefixRE, CheckStrings)) - return 2; - - // Open the file to check and add it to SourceMgr. - ErrorOr> InputFileOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); - 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()); - - if (DumpInput == DumpInputDefault) - DumpInput = DumpInputOnFailure ? DumpInputFail : DumpInputNever; - - std::vector Diags; - int ExitCode = FC.CheckInput(SM, InputFileText, CheckStrings, - DumpInput == DumpInputNever ? nullptr : &Diags) - ? EXIT_SUCCESS - : 1; - if (DumpInput == DumpInputAlways || - (ExitCode == 1 && DumpInput == DumpInputFail)) { - errs() << "\n" - << "Input file: " - << (InputFilename == "-" ? "" : InputFilename.getValue()) - << "\n" - << "Check file: " << CheckFilename << "\n" - << "\n" - << "-dump-input=help describes the format of the following dump.\n" - << "\n"; - std::vector Annotations; - unsigned LabelWidth; - BuildInputAnnotations(Diags, Annotations, LabelWidth); - DumpAnnotatedInput(errs(), Req, InputFileText, Annotations, LabelWidth); - } - - return ExitCode; -}