Add support for LLVM 17. (#4533)

This commit is contained in:
Johan Engelen 2023-12-03 19:07:31 +01:00 committed by GitHub
parent fb637c408f
commit 91d306894e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 4309 additions and 73 deletions

View file

@ -15,7 +15,11 @@ jobs:
fail-fast: false
matrix:
include:
- job_name: Ubuntu 20.04, LLVM 16, latest LDC beta
- job_name: Ubuntu 20.04, LDC-LLVM 17, latest LDC beta
os: ubuntu-20.04
host_dc: ldc-beta
llvm_version: 17.0.6
- job_name: Ubuntu 20.04, LDC-LLVM 16, latest LDC beta
os: ubuntu-20.04
host_dc: ldc-beta
llvm_version: 16.0.6 # LDC-LLVM
@ -90,16 +94,25 @@ jobs:
version='${{ matrix.llvm_version }}'
if [[ '${{ runner.os }}' == macOS ]]; then
suffix='x86_64-apple-darwin'
elif [[ "$version" =~ ^1[7-9]\. ]]; then
suffix='x86_64-linux-gnu-ubuntu-22.04' # LLVM 17+
elif [[ "$version" =~ ^1[3-9]\. ]]; then
suffix='x86_64-linux-gnu-ubuntu-18.04' # LLVM 13.0.1+
else
suffix='x86_64-linux-gnu-ubuntu-16.04'
fi
url="https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/clang+llvm-$version-$suffix.tar.xz"
# FIXME: weird crashes with official v16.0.0 archive; use LDC-LLVM instead
if [[ "$version" =~ ^16\. ]]; then
url="https://github.com/ldc-developers/llvm-project/releases/download/ldc-v$version/llvm-$version-linux-x86_64.tar.xz"
fi
# FIXME: Use LDC-LLVM v17.0.6 RC package because it still works on Ubuntu 20 (official release doesn't), so
# we can still use older gdb and postpone fxixing issues with newer gdb
if [[ "$version" =~ ^17\. ]]; then
url="https://github.com/ldc-developers/llvm-project/releases/download/CI/llvm-be7fee94-linux-x86_64.tar.xz"
fi
curl -fL --retry 3 --max-time 300 -o llvm.tar.xz "$url"
mkdir llvm
tar -xf llvm.tar.xz --strip 1 -C llvm

View file

@ -802,7 +802,13 @@ function(copy_compilerrt_lib llvm_lib_name ldc_lib_name fixup_dylib)
set(ldc_lib_path ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/${ldc_lib_name})
copy_and_rename_file(${llvm_lib_path} ${ldc_lib_path})
if (fixup_dylib)
execute_process(COMMAND install_name_tool -id @rpath/${ldc_lib_name} ${ldc_lib_path})
execute_process(COMMAND install_name_tool -id @rpath/${ldc_lib_name} ${ldc_lib_path} ERROR_VARIABLE INSTALL_NAME_TOOL_STDERR)
if(${INSTALL_NAME_TOOL_STDERR} MATCHES "warning: changes being made to the file will invalidate the code signature")
# Eat the warning, it's ok.
else()
message(WARNING "install_name_tool stderr: ${INSTALL_NAME_TOOL_STDERR}")
endif()
execute_process(COMMAND codesign --force -s - ${ldc_lib_path})
endif()
install(FILES ${ldc_lib_path} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
else()

View file

@ -32,7 +32,8 @@
# We also want an user-specified LLVM_ROOT_DIR to take precedence over the
# system default locations such as /usr/local/bin. Executing find_program()
# multiples times is the approach recommended in the docs.
set(llvm_config_names llvm-config-16.0 llvm-config160 llvm-config-16
set(llvm_config_names llvm-config-17.0 llvm-config170 llvm-config-17
llvm-config-16.0 llvm-config160 llvm-config-16
llvm-config-15.0 llvm-config150 llvm-config-15
llvm-config-14.0 llvm-config140 llvm-config-14
llvm-config-13.0 llvm-config130 llvm-config-13
@ -48,10 +49,12 @@ if(APPLE)
# extra fallbacks for MacPorts & Homebrew
find_program(LLVM_CONFIG
NAMES ${llvm_config_names}
PATHS /opt/local/libexec/llvm-16/bin /opt/local/libexec/llvm-15/bin
PATHS /opt/local/libexec/llvm-17/bin
/opt/local/libexec/llvm-16/bin /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/bin
/usr/local/opt/llvm@17/bin
/usr/local/opt/llvm@16/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

View file

@ -17,7 +17,11 @@
#include "compiler.h"
#if IN_LLVM
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
enum OUTPUTFLAG
{

View file

@ -14,13 +14,18 @@
#include "driver/timetrace.h"
#include "driver/tool.h"
#include "gen/logger.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Host.h"
#else
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#endif
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"

View file

@ -10,7 +10,11 @@
#include "driver/cl_options-llvm.h"
#if LDC_WITH_LLD
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
#endif
// Pull in command-line options and helper functions from special LLVM header

View file

@ -9,7 +9,14 @@
#pragma once
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Optional.h"
#else
#include <optional>
namespace llvm {
template <typename T> using Optional = std::optional<T>;
}
#endif
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetOptions.h"

View file

@ -723,7 +723,9 @@ cl::opt<bool> dynamicCompileTlsWorkaround(
cl::Hidden);
#endif
#if LDC_LLVM_VER >= 1400
#if LDC_LLVM_VER >= 1700
bool enableOpaqueIRPointers = true; // typed pointers are no longer supported from LLVM 17
#elif LDC_LLVM_VER >= 1400
bool enableOpaqueIRPointers = false;
#endif

View file

@ -18,7 +18,11 @@
#include "dmd/errors.h"
#include "dmd/globals.h"
#include "gen/to_string.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
namespace {
namespace cl = llvm::cl;

View file

@ -27,6 +27,13 @@
#if LDC_WITH_LLD
#include "lld/Common/Driver.h"
#if LDC_LLVM_VER >= 1700
LLD_HAS_DRIVER(coff)
LLD_HAS_DRIVER(elf)
LLD_HAS_DRIVER(mingw)
LLD_HAS_DRIVER(macho)
LLD_HAS_DRIVER(wasm)
#endif
#endif
//////////////////////////////////////////////////////////////////////////////

View file

@ -22,6 +22,9 @@
#if LDC_WITH_LLD
#include "lld/Common/Driver.h"
#if LDC_LLVM_VER >= 1700
LLD_HAS_DRIVER(coff)
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
@ -36,7 +39,10 @@ void addMscrtLibs(bool useInternalToolchain, std::vector<std::string> &args) {
// We need the vcruntime lib for druntime's exception handling (ldc.eh_msvc).
// Pick one of the 4 variants matching the selected main UCRT lib.
#if LDC_LLVM_VER >= 1300
#if LDC_LLVM_VER >= 1700
#define contains_lower contains_insensitive
#define endswith_lower ends_with_insensitive
#elif LDC_LLVM_VER >= 1300
#define contains_lower contains_insensitive
#define endswith_lower endswith_insensitive
#endif

View file

@ -57,7 +57,11 @@
#include "llvm/LinkAllIR.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Support/FileSystem.h"
#if LDC_LLVM_VER >= 1700
#include "llvm/TargetParser/Host.h"
#else
#include "llvm/Support/Host.h"
#endif
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/StringSaver.h"
@ -565,7 +569,11 @@ void parseCommandLine(Strings &sourceFiles) {
}
#endif
#if LDC_LLVM_VER >= 1500
#if LDC_LLVM_VER >= 1700
if (!opts::enableOpaqueIRPointers)
error(Loc(),
"LLVM version 17 or above only supports --opaque-pointers=true");
#elif LDC_LLVM_VER >= 1500
getGlobalContext().setOpaquePointers(opts::enableOpaqueIRPointers);
#elif LDC_LLVM_VER >= 1400
if (opts::enableOpaqueIRPointers)
@ -597,9 +605,11 @@ void initializePasses() {
initializeGlobalISel(Registry);
initializeTarget(Registry);
#if LDC_LLVM_VER < 1700
// Initialize passes not included above
initializeRewriteSymbolsLegacyPassPass(Registry);
initializeSjLjEHPreparePass(Registry);
#endif
}
/// Register the MIPS ABI.
@ -1023,8 +1033,11 @@ void registerPredefinedVersions() {
VersionCondition::addPredefinedGlobalIdent("LDC_ThreadSanitizer");
}
#if LDC_LLVM_VER >= 1400
// A version identifier for whether opaque pointers are enabled or not. (needed e.g. for intrinsic mangling)
// Set a version identifier for whether opaque pointers are enabled or not. (needed e.g. for intrinsic mangling)
#if LDC_LLVM_VER >= 1700
// Since LLVM 17, IR pointers are always opaque.
VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_OpaquePointers");
#elif LDC_LLVM_VER >= 1400
if (!getGlobalContext().supportsTypedPointers()) {
VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_OpaquePointers");
}

View file

@ -21,14 +21,27 @@
#include "gen/logger.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetParser.h"
#if LDC_LLVM_VER >= 1400
#include "llvm/Support/AArch64TargetParser.h"
#include "llvm/Support/ARMTargetParser.h"
#endif
#else
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/ARMTargetParser.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/TargetParser.h"
#include "llvm/TargetParser/Triple.h"
#endif
#include "llvm/IR/Module.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/Support/CommandLine.h"
#if LDC_LLVM_VER >= 1400
#include "llvm/MC/TargetRegistry.h"
#else
#include "llvm/Support/TargetRegistry.h"
@ -36,10 +49,6 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#if LDC_LLVM_VER >= 1400
#include "llvm/Support/AArch64TargetParser.h"
#include "llvm/Support/ARMTargetParser.h"
#endif
#include "gen/optimizer.h"
@ -630,7 +639,10 @@ createTargetMachine(const std::string targetTriple, const std::string arch,
// LLVM fork. LLVM 7+ enables regular emutls by default; prevent that.
if (triple.getEnvironment() == llvm::Triple::Android) {
targetOptions.EmulatedTLS = false;
#if LDC_LLVM_VER < 1700
// Removed in this commit: https://github.com/llvm/llvm-project/commit/0d333bf0e3aa37e2e6ae211e3aa80631c3e01b85
targetOptions.ExplicitEmulatedTLS = true;
#endif
}
const std::string finalFeaturesString =

View file

@ -14,7 +14,14 @@
#pragma once
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Optional.h"
#else
#include <optional>
namespace llvm {
template <typename T> using Optional = std::optional<T>;
}
#endif
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CodeGen.h"

View file

@ -47,10 +47,19 @@
using CodeGenFileType = llvm::CodeGenFileType;
#if LDC_LLVM_VER < 1700
static llvm::cl::opt<bool>
NoIntegratedAssembler("no-integrated-as", llvm::cl::ZeroOrMore,
llvm::cl::Hidden,
llvm::cl::desc("Disable integrated assembler"));
#else
namespace llvm {
namespace codegen {
bool getDisableIntegratedAS();
}
}
#define NoIntegratedAssembler llvm::codegen::getDisableIntegratedAS()
#endif
namespace {

View file

@ -17,7 +17,14 @@
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/tollvm.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Optional.h"
#else
#include <optional>
namespace llvm {
template <typename T> using Optional = std::optional<T>;
}
#endif
class Dsymbol;
class Type;

View file

@ -157,7 +157,12 @@ DRValue *DLValue::getRVal() {
////////////////////////////////////////////////////////////////////////////////
DSpecialRefValue::DSpecialRefValue(Type *t, LLValue *v) : DLValue(v, t) {
#if LDC_LLVM_VER >= 1700 // LLVM >= 17 uses opaque pointers, type check boils
// down to pointer check only.
assert(v->getType()->isPointerTy());
#else
assert(v->getType() == DtoPtrToType(t)->getPointerTo());
#endif
}
DRValue *DSpecialRefValue::getRVal() {

View file

@ -882,7 +882,12 @@ void defineParameters(IrFuncTy &irFty, VarDeclarations &parameters) {
if (irparam->arg->byref) {
// The argument is an appropriate lvalue passed by reference.
// Use the passed pointer as parameter storage.
#if LDC_LLVM_VER >= 1700 // LLVM >= 17 uses opaque pointers, type check boils
// down to pointer check only.
assert(irparam->value->getType()->isPointerTy());
#else
assert(irparam->value->getType() == DtoPtrToType(paramType));
#endif
} else {
// Let the ABI transform the parameter back to an lvalue.
irparam->value =

View file

@ -50,6 +50,9 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#if LDC_LLVM_VER >= 1700
#include "llvm/Support/VirtualFileSystem.h"
#endif
#if _AIX || __sun
#include <alloca.h>
@ -354,7 +357,12 @@ void loadInstrProfileData(IRState *irs) {
global.params.datafileInstrProf);
auto readerOrErr =
llvm::IndexedInstrProfReader::create(global.params.datafileInstrProf);
llvm::IndexedInstrProfReader::create(global.params.datafileInstrProf
#if LDC_LLVM_VER >= 1700
,
*llvm::vfs::getRealFileSystem()
#endif
);
if (auto E = readerOrErr.takeError()) {
handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) {
error(irs->dmodule->loc, "Could not read profile file '%s': %s",

View file

@ -20,7 +20,11 @@
#include "driver/cl_options_sanitizers.h"
#include "driver/plugins.h"
#include "driver/targetmachine.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
@ -34,7 +38,9 @@
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/IPO.h"
#if LDC_LLVM_VER < 1700
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
@ -544,23 +550,32 @@ static void addGarbageCollect2StackPass(ModulePassManager &mpm,
static llvm::Optional<PGOOptions> getPGOOptions() {
//FIXME: Do we have these anywhere?
bool debugInfoForProfiling=false;
bool pseudoProbeForProfiling=false;
// FIXME: Do we have these anywhere?
bool debugInfoForProfiling = false;
bool pseudoProbeForProfiling = false;
if (opts::isInstrumentingForIRBasedPGO()) {
return PGOOptions(global.params.datafileInstrProf, "", "",
PGOOptions::PGOAction::IRInstr,
PGOOptions::CSPGOAction::NoCSAction,
return PGOOptions(
global.params.datafileInstrProf, "", "",
#if LDC_LLVM_VER >= 1700
"" /*MemoryProfileUsePath*/, llvm::vfs::getRealFileSystem(),
#endif
PGOOptions::PGOAction::IRInstr, PGOOptions::CSPGOAction::NoCSAction,
debugInfoForProfiling, pseudoProbeForProfiling);
} else if (opts::isUsingIRBasedPGOProfile()) {
return PGOOptions(global.params.datafileInstrProf, "", "",
PGOOptions::PGOAction::IRUse,
PGOOptions::CSPGOAction::NoCSAction,
return PGOOptions(
global.params.datafileInstrProf, "", "",
#if LDC_LLVM_VER >= 1700
"" /*MemoryProfileUsePath*/, llvm::vfs::getRealFileSystem(),
#endif
PGOOptions::PGOAction::IRUse, PGOOptions::CSPGOAction::NoCSAction,
debugInfoForProfiling, pseudoProbeForProfiling);
} else if (opts::isUsingSampleBasedPGOProfile()) {
return PGOOptions(global.params.datafileInstrProf, "", "",
PGOOptions::PGOAction::SampleUse,
PGOOptions::CSPGOAction::NoCSAction,
return PGOOptions(
global.params.datafileInstrProf, "", "",
#if LDC_LLVM_VER >= 1700
"" /*MemoryProfileUsePath*/, llvm::vfs::getRealFileSystem(),
#endif
PGOOptions::PGOAction::SampleUse, PGOOptions::CSPGOAction::NoCSAction,
debugInfoForProfiling, pseudoProbeForProfiling);
}
#if LDC_LLVM_VER < 1600
@ -637,7 +652,11 @@ void runOptimizationPasses(llvm::Module *M) {
StandardInstrumentations si(M->getContext(), debugLogging, /*VerifyEach=*/false, ppo);
#endif
#if LDC_LLVM_VER < 1700
si.registerCallbacks(pic, &fam);
#else
si.registerCallbacks(pic, &mam);
#endif
PassBuilder pb(gTargetMachine, getPipelineTuningOptions(optLevelVal, sizeLevelVal),
getPGOOptions(), &pic);

View file

@ -276,7 +276,11 @@ bool DLLImportRelocation::run(Module &m) {
Impl impl(m);
bool hasChanged = false;
#if LDC_LLVM_VER >= 1700
for (GlobalVariable &global : m.globals()) {
#else
for (GlobalVariable &global : m.getGlobalList()) {
#endif
// TODO: thread-local globals would need to be initialized in a separate TLS
// ctor
if (!global.hasInitializer() || global.isThreadLocal())

View file

@ -920,7 +920,11 @@ void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader,
auto EC = RecordExpected.takeError();
if (EC) {
#if LDC_LLVM_VER >= 1700
auto IPE = std::get<0>(llvm::InstrProfError::take(std::move(EC)));
#else
auto IPE = llvm::InstrProfError::take(std::move(EC));
#endif
if (IPE == llvm::instrprof_error::unknown_function) {
IF_LOG Logger::println("No profile data for function: %s",
FuncName.c_str());

View file

@ -965,11 +965,13 @@ public:
result = new DLValue(e->type, DtoBitCast(V, DtoPtrToType(e->type)));
}
static llvm::PointerType * getWithSamePointeeType(llvm::PointerType *p, unsigned as) {
#if LDC_LLVM_VER >= 1300
return llvm::PointerType::getWithSamePointeeType(p, as);
static llvm::PointerType * getWithSamePointeeType(llvm::PointerType *p, unsigned addressSpace) {
#if LDC_LLVM_VER >= 1700
return llvm::PointerType::get(p->getContext(), addressSpace);
#elif LDC_LLVM_VER >= 1300
return llvm::PointerType::getWithSamePointeeType(p, addressSpace);
#else
return p->getPointerElementType()->getPointerTo(as);
return p->getPointerElementType()->getPointerTo(addressSpace);
#endif
}

View file

@ -590,7 +590,9 @@ LLType *stripAddrSpaces(LLType *t)
if (!pt)
return t;
#if LDC_LLVM_VER >= 1400
#if LDC_LLVM_VER >= 1700
return getVoidPtrType();
#elif LDC_LLVM_VER >= 1400
if (pt->isOpaque())
return getVoidPtrType();
else {

View file

@ -449,8 +449,13 @@ bool parseCallingConvention(llvm::StringRef name,
.Case("swifttailcc", llvm::CallingConv::SwiftTail)
#endif
.Case("x86_intrcc", llvm::CallingConv::X86_INTR)
#if LDC_LLVM_VER >= 1700
.Case("hhvmcc", llvm::CallingConv::DUMMY_HHVM)
.Case("hhvm_ccc", llvm::CallingConv::DUMMY_HHVM_C)
#else
.Case("hhvmcc", llvm::CallingConv::HHVM)
.Case("hhvm_ccc", llvm::CallingConv::HHVM_C)
#endif
.Case("cxx_fast_tlscc", llvm::CallingConv::CXX_FAST_TLS)
.Case("amdgpu_vs", llvm::CallingConv::AMDGPU_VS)
#if LDC_LLVM_VER >= 1200

View file

@ -26,6 +26,8 @@ else version (LDC_LLVM_1300) enum LLVM_version = 1300;
else version (LDC_LLVM_1400) enum LLVM_version = 1400;
else version (LDC_LLVM_1500) enum LLVM_version = 1500;
else version (LDC_LLVM_1600) enum LLVM_version = 1600;
else version (LDC_LLVM_1700) enum LLVM_version = 1700;
else version (LDC_LLVM_1800) enum LLVM_version = 1800;
else static assert(false, "LDC LLVM version not supported");
enum LLVM_atleast(int major) = (LLVM_version >= major * 100);

View file

@ -17,8 +17,15 @@
#include "param_slice.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Optional.h"
#else
#include <optional>
namespace llvm {
template <typename T> using Optional = std::optional<T>;
}
#endif
namespace llvm {
class Constant;

View file

@ -12,7 +12,11 @@
#include <algorithm>
#include <unordered_map>
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCCodeEmitter.h"

View file

@ -19,7 +19,11 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Triple.h"
#else
#include "llvm/TargetParser/Triple.h"
#endif
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"

View file

@ -15,7 +15,14 @@
#pragma once
#if LDC_LLVM_VER < 1700
#include "llvm/ADT/Optional.h"
#else
#include <optional>
namespace llvm {
template <typename T> using Optional = std::optional<T>;
}
#endif
#include "llvm/ADT/STLExtras.h"
namespace llvm {

View file

@ -8,12 +8,9 @@
import ldc.dcompute;
import ldc.opencl;
// CHECK-DAG: %opencl.image1d_ro_t = type opaque
// CHECK-DAG: %opencl.image1d_wo_t = type opaque
// CHECK-DAG: %"ldc.dcompute.Pointer!(AddrSpace.Global, image1d_ro_t).Pointer" = type { %opencl.image1d_ro_t addrspace(1)* }
// CHECK-DAG: %"ldc.dcompute.Pointer!(AddrSpace.Global, image1d_wo_t).Pointer" = type { %opencl.image1d_wo_t addrspace(1)* }
// CHECK-DAG: %"ldc.dcompute.Pointer!(AddrSpace.Shared, sampler_t).Pointer" = type { %opencl.sampler_t addrspace(2)* }
// CHECK-DAG: %opencl.sampler_t = type opaque
// CHECK-DAG: %"ldc.dcompute.Pointer!(AddrSpace.Global, image1d_ro_t).Pointer" = type { {{%opencl.image1d_ro_t addrspace\(1\)\*|ptr addrspace\(1\)}} }
// CHECK-DAG: %"ldc.dcompute.Pointer!(AddrSpace.Global, image1d_wo_t).Pointer" = type { {{%opencl.image1d_wo_t addrspace\(1\)\*|ptr addrspace\(1\)}} }
// CHECK-DAG: %"ldc.dcompute.Pointer!(AddrSpace.Shared, sampler_t).Pointer" = type { {{%opencl.sampler_t addrspace\(2\)\*|ptr addrspace\(2\)}} }
pragma(mangle,"__translate_sampler_initializer")
Sampler makeSampler(int);
@ -26,16 +23,16 @@ pragma(mangle,"_Z12write_imagef11ocl_image1d_woiDv4_f")
@kernel void img(GlobalPointer!image1d_ro_t src, GlobalPointer!image1d_wo_t dst)
{
// CHECK: %{{[0-9+]}} = call spir_func %opencl.sampler_t addrspace(2)* @__translate_sampler_initializer(i32 0) {{.*}}
// CHECK: %{{[0-9+]}} = call spir_func <4 x float> @_Z11read_imagef11ocl_image1d_ro11ocl_sampleri(%opencl.image1d_ro_t addrspace(1)* %.DComputePointerRewrite_arg, %opencl.sampler_t addrspace(2)* %.DComputePointerRewrite_arg1, i32 0) {{.*}}
// CHECK: %{{[0-9+]}} = call spir_func {{%opencl.sampler_t addrspace\(2\)\*|ptr addrspace\(2\)}} @__translate_sampler_initializer(i32 0) {{.*}}
// CHECK: %{{[0-9+]}} = call spir_func <4 x float> @_Z11read_imagef11ocl_image1d_ro11ocl_sampleri({{%opencl.image1d_ro_t addrspace\(1\)\*|ptr addrspace\(1\)}} %.DComputePointerRewrite_arg, {{%opencl.sampler_t addrspace\(2\)\*|ptr addrspace\(2\)}} %.DComputePointerRewrite_arg1, i32 0) {{.*}}
auto x = src.read(makeSampler(0), 0);
// CHECK: call spir_func void @_Z12write_imagef11ocl_image1d_woiDv4_f(%opencl.image1d_wo_t addrspace(1)* %.DComputePointerRewrite_arg2, i32 0, <4 x float> %{{[0-9+]}})
// CHECK: call spir_func void @_Z12write_imagef11ocl_image1d_woiDv4_f({{%opencl.image1d_wo_t addrspace\(1\)\*|ptr addrspace\(1\)}} %.DComputePointerRewrite_arg2, i32 0, <4 x float> %{{[0-9+]}})
dst.write(0,x);
}
@kernel void img2(GlobalPointer!image1d_ro_t src, Sampler samp)
{
// CHECK: %{{[0-9+]}} = call spir_func <4 x float> @_Z11read_imagef11ocl_image1d_ro11ocl_sampleri(%opencl.image1d_ro_t addrspace(1)* %.DComputePointerRewrite_arg, %opencl.sampler_t addrspace(2)* %.DComputePointerRewrite_arg1, i32 0) {{.*}}
// CHECK: %{{[0-9+]}} = call spir_func <4 x float> @_Z11read_imagef11ocl_image1d_ro11ocl_sampleri({{%opencl.image1d_ro_t addrspace\(1\)\*|ptr addrspace\(1\)}} %.DComputePointerRewrite_arg, {{%opencl.sampler_t addrspace\(2\)\*|ptr addrspace\(2\)}} %.DComputePointerRewrite_arg1, i32 0) {{.*}}
auto x = src.read(samp, 0);
}

View file

@ -9,19 +9,19 @@ void foo_array_foo() {
// CHECK: alloca [400 x i8]
// CHECK: alloca [800 x i8]
{
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 400
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 400
byte[400] arr = void;
// CHECK: call void @opaque
opaque(&arr[0]);
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 400
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 400
}
{
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 800
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 800
byte[800] arr = void;
// CHECK: call void @opaque
opaque(&arr[0]);
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 800
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 800
}
// CHECK-LABEL: ret void
@ -34,16 +34,16 @@ void foo_forloop_foo() {
// This call should appear before lifetime start of while-loop variable.
opaque(&i);
for (byte[13] d; d[0] < 2; d[0]++) {
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 13
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 13
// Lifetime should start before initializing the variable
// CHECK: call void @llvm.memset.p0i8.i{{.*}}13
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 44
// CHECK: call void @llvm.memset.p0{{(i8)?}}.i{{.*}}13
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 44
byte[44] arr = void;
// CHECK: call void @opaque
opaque(&arr[0]);
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 44
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 44
// CHECK: endfor:
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 13
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 13
}
// CHECK-LABEL: ret void
@ -56,15 +56,15 @@ void foo_whileloop_foo() {
// This call should appear before lifetime start of while-loop variable.
opaque(&i);
while (ulong d = 131) {
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 8
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 8
// Lifetime should start before initializing the variable
// CHECK: store i64 131
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 33
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 33
byte[33] arr = void;
// CHECK: call void @opaque
opaque(&arr[0]);
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 33
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 8
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 33
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 8
}
// CHECK-LABEL: ret void
@ -76,25 +76,25 @@ void foo_if_foo() {
// CHECK: call void @opaque
// This call should appear before lifetime start of if-statement condition variable.
opaque(&i);
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 8
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 8
// Lifetime should start before initializing the variable
// CHECK: store i64 565
if (ulong d = 565) {
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 72
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 72
byte[72] arr = void;
// CHECK: call void @opaque
opaque(&arr[0]);
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 72
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 72
} else {
// d is out of scope here.
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 51
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 51
byte[51] arr = void;
// CHECK: call void @opaque
opaque(&arr[0]);
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 51
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 51
}
// CHECK: endif:
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 8
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 8
// CHECK-LABEL: ret void
}
@ -111,12 +111,12 @@ void opaque_S(S* i);
// CHECK-LABEL: define void @foo_struct_foo()
void foo_struct_foo() {
{
// CHECK: call void @llvm.lifetime.start.p0i8(i64 immarg 123
// CHECK: call void @llvm.lifetime.start.p0{{(i8)?}}(i64 immarg 123
S s;
// CHECK: invoke void @opaque_S
opaque_S(&s);
}
// CHECK: call void @llvm.lifetime.end.p0i8(i64 immarg 123
// CHECK: call void @llvm.lifetime.end.p0{{(i8)?}}(i64 immarg 123
// CHECK-NEXT: ret void
}

View file

@ -5,4 +5,4 @@ extern (C) void main()
assert(false);
}
// CHECK: declare void @__assert_func(i8*, i32, i8*, i8*)
// CHECK: declare void @__assert_func({{i8\*|ptr}}, i32, {{i8\*|ptr}}, {{i8\*|ptr}})

View file

@ -10,9 +10,12 @@
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#if LLVM_VERSION < 1500 // legacy pass manager
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
using namespace llvm;

File diff suppressed because it is too large Load diff

895
utils/FileCheck-17.cpp Normal file
View file

@ -0,0 +1,895 @@
//===- 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/FileCheck/FileCheck.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <cmath>
#include <map>
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<std::string>
CheckFilename(cl::Positional, cl::desc("<check-file>"), cl::Optional);
static cl::opt<std::string>
InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
cl::init("-"), cl::value_desc("filename"));
static cl::list<std::string> CheckPrefixes(
"check-prefix",
cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
static cl::alias CheckPrefixesAlias(
"check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
cl::NotHidden,
cl::desc(
"Alias for -check-prefix permitting multiple comma separated values"));
static cl::list<std::string> CommentPrefixes(
"comment-prefixes", cl::CommaSeparated, cl::Hidden,
cl::desc("Comma-separated list of comment prefixes to use from check file\n"
"(defaults to 'COM,RUN'). Please avoid using this feature in\n"
"LLVM's LIT-based test suites, which should be easier to\n"
"maintain if they all follow a consistent comment style. This\n"
"feature is meant for non-LIT test suites using FileCheck."));
static cl::opt<bool> NoCanonicalizeWhiteSpace(
"strict-whitespace",
cl::desc("Do not treat all horizontal whitespace as equivalent"));
static cl::opt<bool> IgnoreCase(
"ignore-case",
cl::desc("Use case-insensitive matching"));
static cl::list<std::string> 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<std::string>
GlobalDefines("D", cl::AlwaysPrefix,
cl::desc("Define a variable to be used in capture patterns."),
cl::value_desc("VAR=VALUE"));
static cl::opt<bool> 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<bool> AllowUnusedPrefixes(
"allow-unused-prefixes",
cl::desc("Allow prefixes to be specified but not appear in the test."));
static cl::opt<bool> 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<bool> 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<bool> 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<bool> Verbose(
"v",
cl::desc("Print directive pattern matches, or add them to the input dump\n"
"if enabled.\n"));
static cl::opt<bool> VerboseVerbose(
"vv",
cl::desc("Print information helpful in diagnosing internal FileCheck\n"
"issues, or add it to the input dump if enabled. Implies\n"
"-v.\n"));
// The order of DumpInputValue members affects their precedence, as documented
// for -dump-input below.
enum DumpInputValue {
DumpInputNever,
DumpInputFail,
DumpInputAlways,
DumpInputHelp
};
static cl::list<DumpInputValue> 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 <value> that appears earliest\n"
"in the list below has precedence. The default is 'fail'.\n"),
cl::value_desc("mode"),
cl::values(clEnumValN(DumpInputHelp, "help", "Explain input dump and quit"),
clEnumValN(DumpInputAlways, "always", "Always dump input"),
clEnumValN(DumpInputFail, "fail", "Dump input on failure"),
clEnumValN(DumpInputNever, "never", "Never dump input")));
// The order of DumpInputFilterValue members affects their precedence, as
// documented for -dump-input-filter below.
enum DumpInputFilterValue {
DumpInputFilterError,
DumpInputFilterAnnotation,
DumpInputFilterAnnotationFull,
DumpInputFilterAll
};
static cl::list<DumpInputFilterValue> DumpInputFilters(
"dump-input-filter",
cl::desc("In the dump requested by -dump-input, print only input lines of\n"
"kind <value> plus any context specified by -dump-input-context.\n"
"When there are multiple occurrences of this option, the <value>\n"
"that appears earliest in the list below has precedence. The\n"
"default is 'error' when -dump-input=fail, and it's 'all' when\n"
"-dump-input=always.\n"),
cl::values(clEnumValN(DumpInputFilterAll, "all", "All input lines"),
clEnumValN(DumpInputFilterAnnotationFull, "annotation-full",
"Input lines with annotations"),
clEnumValN(DumpInputFilterAnnotation, "annotation",
"Input lines with starting points of annotations"),
clEnumValN(DumpInputFilterError, "error",
"Input lines with starting points of error "
"annotations")));
static cl::list<unsigned> DumpInputContexts(
"dump-input-context", cl::value_desc("N"),
cl::desc("In the dump requested by -dump-input, print <N> input lines\n"
"before and <N> input lines after any lines specified by\n"
"-dump-input-filter. When there are multiple occurrences of\n"
"this option, the largest specified <N> has precedence. The\n"
"default is 5.\n"));
typedef cl::list<std::string>::const_iterator prefix_iterator;
static void DumpCommandLine(int argc, char **argv) {
errs() << "FileCheck command line: ";
for (int I = 0; I < argc; I++)
errs() << " " << argv[I];
errs() << "\n";
}
struct MarkerStyle {
/// The starting char (before tildes) for marking the line.
char Lead;
/// What color to use for this annotation.
raw_ostream::Colors Color;
/// A note to follow the marker, or empty string if none.
std::string Note;
/// Does this marker indicate inclusion by -dump-input-filter=error?
bool FiltersAsError;
MarkerStyle() {}
MarkerStyle(char Lead, raw_ostream::Colors Color,
const std::string &Note = "", bool FiltersAsError = false)
: Lead(Lead), Color(Color), Note(Note), FiltersAsError(FiltersAsError) {
assert((!FiltersAsError || !Note.empty()) &&
"expected error diagnostic to have note");
}
};
static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
switch (MatchTy) {
case FileCheckDiag::MatchFoundAndExpected:
return MarkerStyle('^', raw_ostream::GREEN);
case FileCheckDiag::MatchFoundButExcluded:
return MarkerStyle('!', raw_ostream::RED, "error: no match expected",
/*FiltersAsError=*/true);
case FileCheckDiag::MatchFoundButWrongLine:
return MarkerStyle('!', raw_ostream::RED, "error: match on wrong line",
/*FiltersAsError=*/true);
case FileCheckDiag::MatchFoundButDiscarded:
return MarkerStyle('!', raw_ostream::CYAN,
"discard: overlaps earlier match");
case FileCheckDiag::MatchFoundErrorNote:
// Note should always be overridden within the FileCheckDiag.
return MarkerStyle('!', raw_ostream::RED,
"error: unknown error after match",
/*FiltersAsError=*/true);
case FileCheckDiag::MatchNoneAndExcluded:
return MarkerStyle('X', raw_ostream::GREEN);
case FileCheckDiag::MatchNoneButExpected:
return MarkerStyle('X', raw_ostream::RED, "error: no match found",
/*FiltersAsError=*/true);
case FileCheckDiag::MatchNoneForInvalidPattern:
return MarkerStyle('X', raw_ostream::RED,
"error: match failed for invalid pattern",
/*FiltersAsError=*/true);
case FileCheckDiag::MatchFuzzy:
return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
/*FiltersAsError=*/true);
}
llvm_unreachable_internal("unexpected match type");
}
static void DumpInputAnnotationHelp(raw_ostream &OS) {
OS << "The following description was requested by -dump-input=help to\n"
<< "explain the input dump printed by FileCheck.\n"
<< "\n"
<< "Related command-line options:\n"
<< "\n"
<< " - -dump-input=<value> enables or disables the input dump\n"
<< " - -dump-input-filter=<value> filters the input lines\n"
<< " - -dump-input-context=<N> adjusts the context of filtered lines\n"
<< " - -v and -vv add more annotations\n"
<< " - -color forces colors to be enabled both in the dump and below\n"
<< " - -help documents the above options in more detail\n"
<< "\n"
<< "These options can also be set via FILECHECK_OPTS. For example, for\n"
<< "maximum debugging output on failures:\n"
<< "\n"
<< " $ FILECHECK_OPTS='-dump-input-filter=all -vv -color' ninja check\n"
<< "\n"
<< "Input dump annotation format:\n"
<< "\n";
// Labels for input lines.
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "L:";
OS << " labels line number L of the input file\n"
<< " An extra space is added after each input line to represent"
<< " the\n"
<< " newline character\n";
// Labels for annotation lines.
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
OS << " labels the only match result for either (1) a pattern of type T"
<< " from\n"
<< " line L of the check file if L is an integer or (2) the"
<< " I-th implicit\n"
<< " pattern if L is \"imp\" followed by an integer "
<< "I (index origin one)\n";
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
OS << " labels the Nth match result for such a pattern\n";
// Markers on annotation lines.
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
OS << " marks good match (reported if -v)\n"
<< " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
OS << " marks bad match, such as:\n"
<< " - CHECK-NEXT on same line as previous match (error)\n"
<< " - CHECK-NOT found (error)\n"
<< " - CHECK-DAG overlapping match (discarded, reported if "
<< "-vv)\n"
<< " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
OS << " marks search range when no match is found, such as:\n"
<< " - CHECK-NEXT not found (error)\n"
<< " - CHECK-NOT not found (success, reported if -vv)\n"
<< " - CHECK-DAG not found after discarded matches (error)\n"
<< " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
OS << " marks fuzzy match when no match is found\n";
// Elided lines.
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
OS << " indicates elided input lines and annotations, as specified by\n"
<< " -dump-input-filter and -dump-input-context\n";
// Colors.
OS << " - colors ";
WithColor(OS, raw_ostream::GREEN, true) << "success";
OS << ", ";
WithColor(OS, raw_ostream::RED, true) << "error";
OS << ", ";
WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
OS << ", ";
WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
OS << ", ";
WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
OS << "\n";
}
/// An annotation for a single input line.
struct InputAnnotation {
/// The index of the match result across all checks
unsigned DiagIndex;
/// The label for this annotation.
std::string Label;
/// Is this the initial fragment of a diagnostic that has been broken across
/// multiple lines?
bool IsFirstLine;
/// What input line (one-origin indexing) this annotation marks. This might
/// be different from the starting line of the original diagnostic if
/// !IsFirstLine.
unsigned InputLine;
/// The column range (one-origin indexing, open end) in which to mark the
/// input line. If InputEndCol is UINT_MAX, treat it as the last column
/// before the newline.
unsigned InputStartCol, InputEndCol;
/// The marker to use.
MarkerStyle Marker;
/// Whether this annotation represents a good match for an expected pattern.
bool FoundAndExpectedMatch;
};
/// Get an abbreviation for the check type.
static std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
switch (Ty) {
case Check::CheckPlain:
if (Ty.getCount() > 1)
return "count";
return "check";
case Check::CheckNext:
return "next";
case Check::CheckSame:
return "same";
case Check::CheckNot:
return "not";
case Check::CheckDAG:
return "dag";
case Check::CheckLabel:
return "label";
case Check::CheckEmpty:
return "empty";
case Check::CheckComment:
return "com";
case Check::CheckEOF:
return "eof";
case Check::CheckBadNot:
return "bad-not";
case Check::CheckBadCount:
return "bad-count";
case Check::CheckMisspelled:
return "misspelled";
case Check::CheckNone:
llvm_unreachable("invalid FileCheckType");
}
llvm_unreachable("unknown FileCheckType");
}
static void
BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
const std::vector<FileCheckDiag> &Diags,
std::vector<InputAnnotation> &Annotations,
unsigned &LabelWidth) {
struct CompareSMLoc {
bool operator()(const SMLoc &LHS, const SMLoc &RHS) const {
return LHS.getPointer() < RHS.getPointer();
}
};
// How many diagnostics does each pattern have?
std::map<SMLoc, unsigned, CompareSMLoc> DiagCountPerPattern;
for (auto Diag : Diags)
++DiagCountPerPattern[Diag.CheckLoc];
// How many diagnostics have we seen so far per pattern?
std::map<SMLoc, unsigned, CompareSMLoc> DiagIndexPerPattern;
// How many total diagnostics have we seen so far?
unsigned DiagIndex = 0;
// What's the widest label?
LabelWidth = 0;
for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
++DiagItr) {
InputAnnotation A;
A.DiagIndex = DiagIndex++;
// Build label, which uniquely identifies this check result.
unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
auto CheckLineAndCol =
SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID);
llvm::raw_string_ostream Label(A.Label);
Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":";
if (CheckBufferID == CheckFileBufferID)
Label << CheckLineAndCol.first;
else if (ImpPatBufferIDRange.first <= CheckBufferID &&
CheckBufferID < ImpPatBufferIDRange.second)
Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
else
llvm_unreachable("expected diagnostic's check location to be either in "
"the check file or for an implicit pattern");
if (DiagCountPerPattern[DiagItr->CheckLoc] > 1)
Label << "'" << DiagIndexPerPattern[DiagItr->CheckLoc]++;
LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
A.Marker = GetMarker(DiagItr->MatchTy);
if (!DiagItr->Note.empty()) {
A.Marker.Note = DiagItr->Note;
// It's less confusing if notes that don't actually have ranges don't have
// markers. For example, a marker for 'with "VAR" equal to "5"' would
// seem to indicate where "VAR" matches, but the location we actually have
// for the marker simply points to the start of the match/search range for
// the full pattern of which the substitution is potentially just one
// component.
if (DiagItr->InputStartLine == DiagItr->InputEndLine &&
DiagItr->InputStartCol == DiagItr->InputEndCol)
A.Marker.Lead = ' ';
}
if (DiagItr->MatchTy == FileCheckDiag::MatchFoundErrorNote) {
assert(!DiagItr->Note.empty() &&
"expected custom note for MatchFoundErrorNote");
A.Marker.Note = "error: " + A.Marker.Note;
}
A.FoundAndExpectedMatch =
DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
// Compute the mark location, and break annotation into multiple
// annotations if it spans multiple lines.
A.IsFirstLine = true;
A.InputLine = DiagItr->InputStartLine;
A.InputStartCol = DiagItr->InputStartCol;
if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
// Sometimes ranges are empty in order to indicate a specific point, but
// that would mean nothing would be marked, so adjust the range to
// include the following character.
A.InputEndCol =
std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
Annotations.push_back(A);
} else {
assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
"expected input range not to be inverted");
A.InputEndCol = UINT_MAX;
Annotations.push_back(A);
for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
L <= E; ++L) {
// If a range ends before the first column on a line, then it has no
// characters on that line, so there's nothing to render.
if (DiagItr->InputEndCol == 1 && L == E)
break;
InputAnnotation B;
B.DiagIndex = A.DiagIndex;
B.Label = A.Label;
B.IsFirstLine = false;
B.InputLine = L;
B.Marker = A.Marker;
B.Marker.Lead = '~';
B.Marker.Note = "";
B.InputStartCol = 1;
if (L != E)
B.InputEndCol = UINT_MAX;
else
B.InputEndCol = DiagItr->InputEndCol;
B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
Annotations.push_back(B);
}
}
}
}
static unsigned FindInputLineInFilter(
DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
const std::vector<InputAnnotation>::iterator &AnnotationBeg,
const std::vector<InputAnnotation>::iterator &AnnotationEnd) {
if (DumpInputFilter == DumpInputFilterAll)
return CurInputLine;
for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
++AnnotationItr) {
switch (DumpInputFilter) {
case DumpInputFilterAll:
llvm_unreachable("unexpected DumpInputFilterAll");
break;
case DumpInputFilterAnnotationFull:
return AnnotationItr->InputLine;
case DumpInputFilterAnnotation:
if (AnnotationItr->IsFirstLine)
return AnnotationItr->InputLine;
break;
case DumpInputFilterError:
if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
return AnnotationItr->InputLine;
break;
}
}
return UINT_MAX;
}
/// To OS, print a vertical ellipsis (right-justified at LabelWidth) if it would
/// occupy less lines than ElidedLines, but print ElidedLines otherwise. Either
/// way, clear ElidedLines. Thus, if ElidedLines is empty, do nothing.
static void DumpEllipsisOrElidedLines(raw_ostream &OS, std::string &ElidedLines,
unsigned LabelWidth) {
if (ElidedLines.empty())
return;
unsigned EllipsisLines = 3;
if (EllipsisLines < StringRef(ElidedLines).count('\n')) {
for (unsigned i = 0; i < EllipsisLines; ++i) {
WithColor(OS, raw_ostream::BLACK, /*Bold=*/true)
<< right_justify(".", LabelWidth);
OS << '\n';
}
} else
OS << ElidedLines;
ElidedLines.clear();
}
static void DumpAnnotatedInput(raw_ostream &OS, const FileCheckRequest &Req,
DumpInputFilterValue DumpInputFilter,
unsigned DumpInputContext,
StringRef InputFileText,
std::vector<InputAnnotation> &Annotations,
unsigned LabelWidth) {
OS << "Input was:\n<<<<<<\n";
// Sort annotations.
llvm::sort(Annotations,
[](const InputAnnotation &A, const InputAnnotation &B) {
// 1. Sort annotations in the order of the input lines.
//
// This makes it easier to find relevant annotations while
// iterating input lines in the implementation below. FileCheck
// does not always produce diagnostics in the order of input
// lines due to, for example, CHECK-DAG and CHECK-NOT.
if (A.InputLine != B.InputLine)
return A.InputLine < B.InputLine;
// 2. Sort annotations in the temporal order FileCheck produced
// their associated diagnostics.
//
// This sort offers several benefits:
//
// A. On a single input line, the order of annotations reflects
// the FileCheck logic for processing directives/patterns.
// This can be helpful in understanding cases in which the
// order of the associated directives/patterns in the check
// file or on the command line either (i) does not match the
// temporal order in which FileCheck looks for matches for the
// directives/patterns (due to, for example, CHECK-LABEL,
// CHECK-NOT, or `--implicit-check-not`) or (ii) does match
// that order but does not match the order of those
// diagnostics along an input line (due to, for example,
// CHECK-DAG).
//
// On the other hand, because our presentation format presents
// input lines in order, there's no clear way to offer the
// same benefit across input lines. For consistency, it might
// then seem worthwhile to have annotations on a single line
// also sorted in input order (that is, by input column).
// However, in practice, this appears to be more confusing
// than helpful. Perhaps it's intuitive to expect annotations
// to be listed in the temporal order in which they were
// produced except in cases the presentation format obviously
// and inherently cannot support it (that is, across input
// lines).
//
// B. When diagnostics' annotations are split among multiple
// input lines, the user must track them from one input line
// to the next. One property of the sort chosen here is that
// it facilitates the user in this regard by ensuring the
// following: when comparing any two input lines, a
// diagnostic's annotations are sorted in the same position
// relative to all other diagnostics' annotations.
return A.DiagIndex < B.DiagIndex;
});
// Compute the width of the label column.
const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
*InputFileEnd = InputFileText.bytes_end();
unsigned LineCount = InputFileText.count('\n');
if (InputFileEnd[-1] != '\n')
++LineCount;
unsigned LineNoWidth = std::log10(LineCount) + 1;
// +3 below adds spaces (1) to the left of the (right-aligned) line numbers
// on input lines and (2) to the right of the (left-aligned) labels on
// annotation lines so that input lines and annotation lines are more
// visually distinct. For example, the spaces on the annotation lines ensure
// that input line numbers and check directive line numbers never align
// horizontally. Those line numbers might not even be for the same file.
// One space would be enough to achieve that, but more makes it even easier
// to see.
LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
// Print annotated input lines.
unsigned PrevLineInFilter = 0; // 0 means none so far
unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
std::string ElidedLines;
raw_string_ostream ElidedLinesOS(ElidedLines);
ColorMode TheColorMode =
WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
if (TheColorMode == ColorMode::Enable)
ElidedLinesOS.enable_colors(true);
auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
for (unsigned Line = 1;
InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
++Line) {
const unsigned char *InputFileLine = InputFilePtr;
// Compute the previous and next line included by the filter.
if (NextLineInFilter < Line)
NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
AnnotationItr, AnnotationEnd);
assert(NextLineInFilter && "expected NextLineInFilter to be computed");
if (NextLineInFilter == Line)
PrevLineInFilter = Line;
// Elide this input line and its annotations if it's not within the
// context specified by -dump-input-context of an input line included by
// -dump-input-filter. However, in case the resulting ellipsis would occupy
// more lines than the input lines and annotations it elides, buffer the
// elided lines and annotations so we can print them instead.
raw_ostream *LineOS;
if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
(NextLineInFilter == UINT_MAX ||
Line + DumpInputContext < NextLineInFilter))
LineOS = &ElidedLinesOS;
else {
LineOS = &OS;
DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
}
// Print right-aligned line number.
WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false,
TheColorMode)
<< format_decimal(Line, LabelWidth) << ": ";
// For the case where -v and colors are enabled, find the annotations for
// good matches for expected patterns in order to highlight everything
// else in the line. There are no such annotations if -v is disabled.
std::vector<InputAnnotation> FoundAndExpectedMatches;
if (Req.Verbose && TheColorMode == ColorMode::Enable) {
for (auto I = AnnotationItr; I != AnnotationEnd && I->InputLine == Line;
++I) {
if (I->FoundAndExpectedMatch)
FoundAndExpectedMatches.push_back(*I);
}
}
// Print numbered line with highlighting where there are no matches for
// expected patterns.
bool Newline = false;
{
WithColor COS(*LineOS, raw_ostream::SAVEDCOLOR, /*Bold=*/false,
/*BG=*/false, TheColorMode);
bool InMatch = false;
if (Req.Verbose)
COS.changeColor(raw_ostream::CYAN, true, true);
for (unsigned Col = 1; InputFilePtr != InputFileEnd && !Newline; ++Col) {
bool WasInMatch = InMatch;
InMatch = false;
for (auto M : FoundAndExpectedMatches) {
if (M.InputStartCol <= Col && Col < M.InputEndCol) {
InMatch = true;
break;
}
}
if (!WasInMatch && InMatch)
COS.resetColor();
else if (WasInMatch && !InMatch)
COS.changeColor(raw_ostream::CYAN, true, true);
if (*InputFilePtr == '\n') {
Newline = true;
COS << ' ';
} else
COS << *InputFilePtr;
++InputFilePtr;
}
}
*LineOS << '\n';
unsigned InputLineWidth = InputFilePtr - InputFileLine;
// Print any annotations.
while (AnnotationItr != AnnotationEnd &&
AnnotationItr->InputLine == Line) {
WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
/*BG=*/false, TheColorMode);
// The two spaces below are where the ": " appears on input lines.
COS << left_justify(AnnotationItr->Label, LabelWidth) << " ";
unsigned Col;
for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
COS << ' ';
COS << AnnotationItr->Marker.Lead;
// If InputEndCol=UINT_MAX, stop at InputLineWidth.
for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
++Col)
COS << '~';
const std::string &Note = AnnotationItr->Marker.Note;
if (!Note.empty()) {
// Put the note at the end of the input line. If we were to instead
// put the note right after the marker, subsequent annotations for the
// same input line might appear to mark this note instead of the input
// line.
for (; Col <= InputLineWidth; ++Col)
COS << ' ';
COS << ' ' << Note;
}
COS << '\n';
++AnnotationItr;
}
}
DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
OS << ">>>>>>\n";
}
int main(int argc, char **argv) {
// Enable use of ANSI color codes because FileCheck is using them to
// highlight text.
llvm::sys::Process::UseANSIEscapeCodes(true);
InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
"FILECHECK_OPTS");
// Select -dump-input* values. The -help documentation specifies the default
// value and which value to choose if an option is specified multiple times.
// In the latter case, the general rule of thumb is to choose the value that
// provides the most information.
DumpInputValue DumpInput =
DumpInputs.empty()
? DumpInputFail
: *std::max_element(DumpInputs.begin(), DumpInputs.end());
DumpInputFilterValue DumpInputFilter;
if (DumpInputFilters.empty())
DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
: DumpInputFilterError;
else
DumpInputFilter =
*std::max_element(DumpInputFilters.begin(), DumpInputFilters.end());
unsigned DumpInputContext = DumpInputContexts.empty()
? 5
: *std::max_element(DumpInputContexts.begin(),
DumpInputContexts.end());
if (DumpInput == DumpInputHelp) {
DumpInputAnnotationHelp(outs());
return 0;
}
if (CheckFilename.empty()) {
errs() << "<check-file> not specified\n";
return 2;
}
FileCheckRequest Req;
append_range(Req.CheckPrefixes, CheckPrefixes);
append_range(Req.CommentPrefixes, CommentPrefixes);
append_range(Req.ImplicitCheckNot, ImplicitCheckNot);
bool GlobalDefineError = false;
for (StringRef G : GlobalDefines) {
size_t EqIdx = G.find('=');
if (EqIdx == std::string::npos) {
errs() << "Missing equal sign in command-line definition '-D" << G
<< "'\n";
GlobalDefineError = true;
continue;
}
if (EqIdx == 0) {
errs() << "Missing variable name in command-line definition '-D" << G
<< "'\n";
GlobalDefineError = true;
continue;
}
Req.GlobalDefines.push_back(G);
}
if (GlobalDefineError)
return 2;
Req.AllowEmptyInput = AllowEmptyInput;
Req.AllowUnusedPrefixes = AllowUnusedPrefixes;
Req.EnableVarScope = EnableVarScope;
Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
Req.Verbose = Verbose;
Req.VerboseVerbose = VerboseVerbose;
Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
Req.MatchFullLines = MatchFullLines;
Req.IgnoreCase = IgnoreCase;
if (VerboseVerbose)
Req.Verbose = true;
FileCheck FC(Req);
if (!FC.ValidateCheckPrefixes())
return 2;
Regex PrefixRE = FC.buildCheckPrefixRegex();
std::string REError;
if (!PrefixRE.isValid(REError)) {
errs() << "Unable to combine check-prefix strings into a prefix regular "
"expression! This is likely a bug in FileCheck's verification of "
"the check-prefix strings. Regular expression parsing failed "
"with the following error: "
<< REError << "\n";
return 2;
}
SourceMgr SM;
// Read the expected strings from the check file.
ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
MemoryBuffer::getFileOrSTDIN(CheckFilename, /*IsText=*/true);
if (std::error_code EC = CheckFileOrErr.getError()) {
errs() << "Could not open check file '" << CheckFilename
<< "': " << EC.message() << '\n';
return 2;
}
MemoryBuffer &CheckFile = *CheckFileOrErr.get();
SmallString<4096> CheckFileBuffer;
StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
unsigned CheckFileBufferID =
SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
CheckFileText, CheckFile.getBufferIdentifier()),
SMLoc());
std::pair<unsigned, unsigned> ImpPatBufferIDRange;
if (FC.readCheckFile(SM, CheckFileText, PrefixRE, &ImpPatBufferIDRange))
return 2;
// Open the file to check and add it to SourceMgr.
ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr =
MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
if (InputFilename == "-")
InputFilename = "<stdin>"; // Overwrite for improved diagnostic messages
if (std::error_code EC = InputFileOrErr.getError()) {
errs() << "Could not open input file '" << InputFilename
<< "': " << EC.message() << '\n';
return 2;
}
MemoryBuffer &InputFile = *InputFileOrErr.get();
if (InputFile.getBufferSize() == 0 && !AllowEmptyInput) {
errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
DumpCommandLine(argc, argv);
return 2;
}
SmallString<4096> InputFileBuffer;
StringRef InputFileText = FC.CanonicalizeFile(InputFile, InputFileBuffer);
SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
InputFileText, InputFile.getBufferIdentifier()),
SMLoc());
std::vector<FileCheckDiag> Diags;
int ExitCode = FC.checkInput(SM, InputFileText,
DumpInput == DumpInputNever ? nullptr : &Diags)
? EXIT_SUCCESS
: 1;
if (DumpInput == DumpInputAlways ||
(ExitCode == 1 && DumpInput == DumpInputFail)) {
errs() << "\n"
<< "Input file: " << InputFilename << "\n"
<< "Check file: " << CheckFilename << "\n"
<< "\n"
<< "-dump-input=help explains the following input dump.\n"
<< "\n";
std::vector<InputAnnotation> Annotations;
unsigned LabelWidth;
BuildInputAnnotations(SM, CheckFileBufferID, ImpPatBufferIDRange, Diags,
Annotations, LabelWidth);
DumpAnnotatedInput(errs(), Req, DumpInputFilter, DumpInputContext,
InputFileText, Annotations, LabelWidth);
}
return ExitCode;
}