mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-05 01:20:51 +03:00
Add support for LLVM 11
One major change is the removal of llvm::CallSite, which I've replaced by llvm::CallBase*.
This commit is contained in:
parent
9c42dfd9d0
commit
96b9cde428
33 changed files with 2562 additions and 229 deletions
|
@ -450,7 +450,12 @@ include(HandleLTOPGOBuildOptions)
|
|||
set(LDC_DYNAMIC_COMPILE "AUTO" CACHE STRING "Support dynamic compilation (ON|OFF). Enabled by default.")
|
||||
option(LDC_DYNAMIC_COMPILE_USE_CUSTOM_PASSES "Use custom LDC passes in jit" ON)
|
||||
if(LDC_DYNAMIC_COMPILE STREQUAL "AUTO")
|
||||
if(LDC_LLVM_VER LESS 1100)
|
||||
set(LDC_DYNAMIC_COMPILE ON)
|
||||
else()
|
||||
# FIXME: support LLVM 11+ too
|
||||
set(LDC_DYNAMIC_COMPILE OFF)
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Building LDC with dynamic compilation support: ${LDC_DYNAMIC_COMPILE} (LDC_DYNAMIC_COMPILE=${LDC_DYNAMIC_COMPILE})")
|
||||
if(LDC_DYNAMIC_COMPILE)
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
# We also want an user-specified LLVM_ROOT_DIR to take precedence over the
|
||||
# system default locations such as /usr/local/bin. Executing find_program()
|
||||
# multiples times is the approach recommended in the docs.
|
||||
set(llvm_config_names llvm-config-10.0 llvm-config100 llvm-config-10
|
||||
set(llvm_config_names llvm-config-11.0 llvm-config110 llvm-config-11
|
||||
llvm-config-10.0 llvm-config100 llvm-config-10
|
||||
llvm-config-9.0 llvm-config90 llvm-config-9
|
||||
llvm-config-8.0 llvm-config80 llvm-config-8
|
||||
llvm-config-7.0 llvm-config70 llvm-config-7
|
||||
|
|
|
@ -216,6 +216,7 @@ longdouble_soft ldexpl(longdouble_soft ldval, int exp); // see strtold
|
|||
inline longdouble_soft fabs (longdouble_soft ld) { return fabsl(ld); }
|
||||
inline longdouble_soft sqrt (longdouble_soft ld) { return sqrtl(ld); }
|
||||
|
||||
#if !IN_LLVM
|
||||
#undef LDBL_DIG
|
||||
#undef LDBL_MAX
|
||||
#undef LDBL_MIN
|
||||
|
@ -235,6 +236,7 @@ inline longdouble_soft sqrt (longdouble_soft ld) { return sqrtl(ld); }
|
|||
#define LDBL_MIN_EXP (-16381)
|
||||
#define LDBL_MAX_10_EXP 4932
|
||||
#define LDBL_MIN_10_EXP (-4932)
|
||||
#endif // !IN_LLVM
|
||||
|
||||
extern const longdouble_soft ld_qnan;
|
||||
extern const longdouble_soft ld_inf;
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#include "llvm/Object/MachO.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
#include "llvm/Support/Host.h"
|
||||
#endif
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
|
||||
|
|
|
@ -65,13 +65,22 @@ static bool createSymLink(const char *to, const char *from) {
|
|||
#include <windows.h>
|
||||
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<wchar_t> &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<wchar_t> &Path16);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // LDC_LLVM_VER < 1100
|
||||
} // namespace sys
|
||||
} // namespace llvm
|
||||
// Returns true upon error.
|
||||
namespace {
|
||||
template <typename FType>
|
||||
|
@ -83,11 +92,17 @@ bool createLink(FType f, const char *to, const char *from) {
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
using llvm::sys::windows::widenPath;
|
||||
#else
|
||||
using llvm::sys::path::widenPath;
|
||||
#endif
|
||||
|
||||
llvm::SmallVector<wchar_t, 128> wide_from;
|
||||
llvm::SmallVector<wchar_t, 128> wide_to;
|
||||
if (llvm::sys::path::widenPath(from, wide_from))
|
||||
if (widenPath(from, wide_from))
|
||||
return true;
|
||||
if (llvm::sys::path::widenPath(to, wide_to))
|
||||
if (widenPath(to, wide_to))
|
||||
return true;
|
||||
|
||||
if (!(*f)(wide_from.begin(), wide_to.begin(), NULL))
|
||||
|
|
|
@ -11,7 +11,11 @@
|
|||
|
||||
// Pull in command-line options and helper functions from special LLVM header
|
||||
// shared by multiple LLVM tools.
|
||||
#if LDC_LLVM_VER >= 700
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
#include "llvm/CodeGen/CommandFlags.h"
|
||||
static llvm::codegen::RegisterCodeGenFlags CGF;
|
||||
using namespace llvm;
|
||||
#elif LDC_LLVM_VER >= 700
|
||||
#include "llvm/CodeGen/CommandFlags.inc"
|
||||
#else
|
||||
#include "llvm/CodeGen/CommandFlags.def"
|
||||
|
@ -21,7 +25,7 @@ static cl::opt<bool>
|
|||
DisableRedZone("disable-red-zone", cl::ZeroOrMore,
|
||||
cl::desc("Do not emit code that uses the red zone."));
|
||||
|
||||
#if LDC_LLVM_VER >= 800
|
||||
#if LDC_LLVM_VER >= 800 && LDC_LLVM_VER < 1100
|
||||
// legacy option
|
||||
static cl::opt<bool>
|
||||
disableFPElim("disable-fp-elim", cl::ZeroOrMore, cl::ReallyHidden,
|
||||
|
@ -32,19 +36,41 @@ static cl::opt<bool>
|
|||
// in the opts namespace, including some additional helper functions.
|
||||
namespace opts {
|
||||
|
||||
std::string getArchStr() { return ::MArch; }
|
||||
std::string getArchStr() {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
return codegen::getMArch();
|
||||
#else
|
||||
return ::MArch;
|
||||
#endif
|
||||
}
|
||||
|
||||
Optional<Reloc::Model> getRelocModel() { return ::getRelocModel(); }
|
||||
Optional<Reloc::Model> getRelocModel() {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
return codegen::getExplicitRelocModel();
|
||||
#else
|
||||
return ::getRelocModel();
|
||||
#endif
|
||||
}
|
||||
|
||||
Optional<CodeModel::Model> getCodeModel() { return ::getCodeModel(); }
|
||||
Optional<CodeModel::Model> getCodeModel() {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
return codegen::getExplicitCodeModel();
|
||||
#else
|
||||
return ::getCodeModel();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LDC_LLVM_VER >= 800
|
||||
llvm::Optional<llvm::FramePointer::FP> framePointerUsage() {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
return codegen::getFramePointerUsage();
|
||||
#else
|
||||
if (::FramePointerUsage.getNumOccurrences() > 0)
|
||||
return ::FramePointerUsage.getValue();
|
||||
if (disableFPElim.getNumOccurrences() > 0)
|
||||
return disableFPElim ? llvm::FramePointer::All : llvm::FramePointer::None;
|
||||
return llvm::None;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
cl::boolOrDefault disableFPElim() {
|
||||
|
@ -57,6 +83,10 @@ cl::boolOrDefault disableFPElim() {
|
|||
bool disableRedZone() { return ::DisableRedZone; }
|
||||
|
||||
bool printTargetFeaturesHelp() {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
const auto MCPU = codegen::getMCPU();
|
||||
const auto MAttrs = codegen::getMAttrs();
|
||||
#endif
|
||||
if (MCPU == "help")
|
||||
return true;
|
||||
return std::any_of(MAttrs.begin(), MAttrs.end(),
|
||||
|
@ -64,11 +94,28 @@ bool printTargetFeaturesHelp() {
|
|||
}
|
||||
|
||||
TargetOptions InitTargetOptionsFromCodeGenFlags() {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
return codegen::InitTargetOptionsFromCodeGenFlags();
|
||||
#else
|
||||
return ::InitTargetOptionsFromCodeGenFlags();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getCPUStr() { return ::getCPUStr(); }
|
||||
std::string getFeaturesStr() { return ::getFeaturesStr(); }
|
||||
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
|
||||
}
|
||||
} // namespace opts
|
||||
|
||||
#if LDC_WITH_LLD
|
||||
|
@ -81,30 +128,32 @@ TargetOptions initTargetOptionsFromCodeGenFlags() {
|
|||
#else
|
||||
TargetOptions InitTargetOptionsFromCodeGenFlags() {
|
||||
#endif
|
||||
return ::InitTargetOptionsFromCodeGenFlags();
|
||||
return ::opts::InitTargetOptionsFromCodeGenFlags();
|
||||
}
|
||||
|
||||
#if LDC_LLVM_VER >= 1000
|
||||
Optional<Reloc::Model> getRelocModelFromCMModel() { return ::getRelocModel(); }
|
||||
Optional<Reloc::Model> getRelocModelFromCMModel() {
|
||||
return ::opts::getRelocModel();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LDC_LLVM_VER >= 900
|
||||
Optional<CodeModel::Model> getCodeModelFromCMModel() {
|
||||
return ::getCodeModel();
|
||||
}
|
||||
#else
|
||||
Optional<CodeModel::Model> GetCodeModelFromCMModel() {
|
||||
return ::getCodeModel();
|
||||
#endif
|
||||
return ::opts::getCodeModel();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LDC_LLVM_VER >= 900
|
||||
std::string getCPUStr() { return ::getCPUStr(); }
|
||||
std::string getCPUStr() { return ::opts::getCPUStr(); }
|
||||
#elif LDC_LLVM_VER >= 700
|
||||
std::string GetCPUStr() { return ::getCPUStr(); }
|
||||
std::string GetCPUStr() { return ::opts::getCPUStr(); }
|
||||
#endif
|
||||
|
||||
#if LDC_LLVM_VER >= 900
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
std::vector<std::string> getMAttrs() { return codegen::getMAttrs(); }
|
||||
#elif LDC_LLVM_VER >= 900
|
||||
std::vector<std::string> getMAttrs() { return ::MAttrs; }
|
||||
#elif LDC_LLVM_VER >= 800
|
||||
std::vector<std::string> GetMAttrs() { return ::MAttrs; }
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
#include "gen/logger.h"
|
||||
#include "gen/modules.h"
|
||||
#include "gen/runtime.h"
|
||||
#if LDC_LLVM_VER >= 900
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
#include "llvm/IR/LLVMRemarkStreamer.h"
|
||||
#elif LDC_LLVM_VER >= 900
|
||||
#include "llvm/IR/RemarkStreamer.h"
|
||||
#endif
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
|
@ -52,7 +54,12 @@ createAndSetDiagnosticsOutputFile(IRState &irs, llvm::LLVMContext &ctx,
|
|||
const bool withHotness = opts::isUsingPGOProfile();
|
||||
|
||||
#if LDC_LLVM_VER >= 900
|
||||
auto remarksFileOrError = llvm::setupOptimizationRemarks(
|
||||
auto remarksFileOrError =
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
llvm::setupLLVMOptimizationRemarks(
|
||||
#else
|
||||
llvm::setupOptimizationRemarks(
|
||||
#endif
|
||||
ctx, diagnosticsFilename, "", "", withHotness);
|
||||
if (llvm::Error e = remarksFileOrError.takeError()) {
|
||||
irs.dmodule->error("Could not create file %s: %s",
|
||||
|
|
|
@ -476,7 +476,11 @@ void translateArgs(const llvm::SmallVectorImpl<const char *> &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) {
|
||||
|
|
|
@ -321,7 +321,7 @@ void ArgsBuilder::addSanitizerLinkFlags(const llvm::Triple &triple,
|
|||
|
||||
// When we reach here, we did not find the sanitizer library.
|
||||
// Fallback, requires Clang.
|
||||
args.push_back(fallbackFlag);
|
||||
args.emplace_back(fallbackFlag);
|
||||
}
|
||||
|
||||
// Adds all required link flags for -fsanitize=fuzzer when libFuzzer library is
|
||||
|
|
|
@ -145,7 +145,7 @@ static std::vector<std::string>
|
|||
parseLibNames(llvm::StringRef commaSeparatedList, llvm::StringRef suffix = {}) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::stringstream list(commaSeparatedList);
|
||||
std::stringstream list(commaSeparatedList.str());
|
||||
while (list.good()) {
|
||||
std::string lib;
|
||||
std::getline(list, lib, ',');
|
||||
|
|
16
gen/aa.cpp
16
gen/aa.cpp
|
@ -65,12 +65,10 @@ DLValue *DtoAAIndex(Loc &loc, Type *type, DValue *aa, DValue *key,
|
|||
LLValue *castedAATI = DtoBitCast(rawAATI, funcTy->getParamType(1));
|
||||
LLValue *valsize = DtoConstSize_t(getTypeAllocSize(DtoType(type)));
|
||||
ret = gIR->CreateCallOrInvoke(func, aaval, castedAATI, valsize, pkey,
|
||||
"aa.index")
|
||||
.getInstruction();
|
||||
"aa.index");
|
||||
} else {
|
||||
LLValue *keyti = to_keyti(aa, funcTy->getParamType(1));
|
||||
ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index")
|
||||
.getInstruction();
|
||||
ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index");
|
||||
}
|
||||
|
||||
// cast return value
|
||||
|
@ -134,8 +132,7 @@ DValue *DtoAAIn(Loc &loc, Type *type, DValue *aa, DValue *key) {
|
|||
pkey = DtoBitCast(pkey, getVoidPtrType());
|
||||
|
||||
// call runtime
|
||||
LLValue *ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in")
|
||||
.getInstruction();
|
||||
LLValue *ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in");
|
||||
|
||||
// cast return value
|
||||
LLType *targettype = DtoType(type);
|
||||
|
@ -179,9 +176,9 @@ DValue *DtoAARemove(Loc &loc, DValue *aa, DValue *key) {
|
|||
pkey = DtoBitCast(pkey, funcTy->getParamType(2));
|
||||
|
||||
// call runtime
|
||||
LLCallSite call = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey);
|
||||
LLValue *res = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey);
|
||||
|
||||
return new DImValue(Type::tbool, call.getInstruction());
|
||||
return new DImValue(Type::tbool, res);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -197,8 +194,7 @@ LLValue *DtoAAEquals(Loc &loc, TOK op, DValue *l, DValue *r) {
|
|||
LLValue *abval = DtoBitCast(DtoRVal(r), funcTy->getParamType(2));
|
||||
LLValue *aaTypeInfo = DtoTypeInfoOf(t);
|
||||
LLValue *res =
|
||||
gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes")
|
||||
.getInstruction();
|
||||
gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes");
|
||||
|
||||
const auto predicate = eqTokToICmpPred(op, /* invert = */ true);
|
||||
res = gIR->ir->CreateICmp(predicate, res, DtoConstInt(0));
|
||||
|
|
|
@ -36,7 +36,12 @@ struct LLTypeMemoryLayout {
|
|||
const size_t sizeInBits = getTypeBitSize(type);
|
||||
assert(sizeInBits % 8 == 0);
|
||||
return llvm::VectorType::get(LLIntegerType::get(gIR->context(), 8),
|
||||
sizeInBits / 8);
|
||||
sizeInBits / 8
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
,
|
||||
/*Scalable=*/false
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
if (LLStructType *structType = isaStruct(type)) {
|
||||
|
|
|
@ -198,7 +198,7 @@ static void copySlice(Loc &loc, LLValue *dstarr, LLValue *dstlen, LLValue *srcar
|
|||
const bool checksEnabled =
|
||||
global.params.useAssert == CHECKENABLEon || gIR->emitArrayBoundsChecks();
|
||||
if (checksEnabled && !knownInBounds) {
|
||||
LLValue *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy");
|
||||
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy");
|
||||
gIR->CreateCallOrInvoke(
|
||||
fn, {dstarr, dstlen, srcarr, srclen, DtoConstSize_t(elementSize)}, "",
|
||||
/*isNothrow=*/true);
|
||||
|
@ -293,20 +293,18 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op,
|
|||
}
|
||||
} else if (isConstructing) {
|
||||
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayctor");
|
||||
LLCallSite call = gIR->CreateCallOrInvoke(fn, DtoTypeInfoOf(elemType),
|
||||
gIR->CreateCallOrInvoke(fn, DtoTypeInfoOf(elemType),
|
||||
DtoSlice(rhsPtr, rhsLength),
|
||||
DtoSlice(lhsPtr, lhsLength));
|
||||
call.setCallingConv(llvm::CallingConv::C);
|
||||
} else // assigning
|
||||
{
|
||||
LLValue *tmpSwap = DtoAlloca(elemType, "arrayAssign.tmpSwap");
|
||||
LLFunction *fn = getRuntimeFunction(
|
||||
loc, gIR->module,
|
||||
!canSkipPostblit ? "_d_arrayassign_l" : "_d_arrayassign_r");
|
||||
LLCallSite call = gIR->CreateCallOrInvoke(
|
||||
gIR->CreateCallOrInvoke(
|
||||
fn, DtoTypeInfoOf(elemType), DtoSlice(rhsPtr, rhsLength),
|
||||
DtoSlice(lhsPtr, lhsLength), DtoBitCast(tmpSwap, getVoidPtrType()));
|
||||
call.setCallingConv(llvm::CallingConv::C);
|
||||
}
|
||||
} else {
|
||||
// scalar rhs:
|
||||
|
@ -335,12 +333,11 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op,
|
|||
LLFunction *fn = getRuntimeFunction(loc, gIR->module,
|
||||
isConstructing ? "_d_arraysetctor"
|
||||
: "_d_arraysetassign");
|
||||
LLCallSite call = gIR->CreateCallOrInvoke(
|
||||
gIR->CreateCallOrInvoke(
|
||||
fn, lhsPtr, DtoBitCast(makeLValue(loc, rhs), getVoidPtrType()),
|
||||
gIR->ir->CreateTruncOrBitCast(lhsLength,
|
||||
LLType::getInt32Ty(gIR->context())),
|
||||
DtoTypeInfoOf(stripModifiers(t2)));
|
||||
call.setCallingConv(llvm::CallingConv::C);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -704,8 +701,7 @@ DSliceValue *DtoNewDynArray(Loc &loc, Type *arrayType, DValue *dim,
|
|||
|
||||
// call allocator
|
||||
LLValue *newArray =
|
||||
gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem")
|
||||
.getInstruction();
|
||||
gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem");
|
||||
|
||||
// return a DSliceValue with the well-known length for better optimizability
|
||||
auto ptr =
|
||||
|
@ -774,8 +770,7 @@ DSliceValue *DtoNewMulDimDynArray(Loc &loc, Type *arrayType, DValue **dims,
|
|||
|
||||
// call allocator
|
||||
LLValue *newptr =
|
||||
gIR->CreateCallOrInvoke(fn, arrayTypeInfo, DtoLoad(darray), ".gc_mem")
|
||||
.getInstruction();
|
||||
gIR->CreateCallOrInvoke(fn, arrayTypeInfo, DtoLoad(darray), ".gc_mem");
|
||||
|
||||
IF_LOG Logger::cout() << "final ptr = " << *newptr << '\n';
|
||||
|
||||
|
@ -802,12 +797,10 @@ DSliceValue *DtoResizeDynArray(Loc &loc, Type *arrayType, DValue *array,
|
|||
getRuntimeFunction(loc, gIR->module, zeroInit ? "_d_arraysetlengthT"
|
||||
: "_d_arraysetlengthiT");
|
||||
|
||||
LLValue *newArray =
|
||||
gIR->CreateCallOrInvoke(
|
||||
LLValue *newArray = gIR->CreateCallOrInvoke(
|
||||
fn, DtoTypeInfoOf(arrayType), newdim,
|
||||
DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)),
|
||||
".gc_mem")
|
||||
.getInstruction();
|
||||
".gc_mem");
|
||||
|
||||
return getSlice(arrayType, newArray);
|
||||
}
|
||||
|
@ -853,14 +846,11 @@ DSliceValue *DtoCatAssignArray(Loc &loc, DValue *arr, Expression *exp) {
|
|||
|
||||
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayappendT");
|
||||
// Call _d_arrayappendT(TypeInfo ti, byte[] *px, byte[] y)
|
||||
LLValue *newArray =
|
||||
gIR->CreateCallOrInvoke(
|
||||
LLValue *newArray = gIR->CreateCallOrInvoke(
|
||||
fn, DtoTypeInfoOf(arrayType),
|
||||
DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(1)),
|
||||
DtoAggrPaint(DtoSlice(exp),
|
||||
fn->getFunctionType()->getParamType(2)),
|
||||
".appendedArray")
|
||||
.getInstruction();
|
||||
DtoAggrPaint(DtoSlice(exp), fn->getFunctionType()->getParamType(2)),
|
||||
".appendedArray");
|
||||
|
||||
return getSlice(arrayType, newArray);
|
||||
}
|
||||
|
@ -929,8 +919,7 @@ DSliceValue *DtoCatArrays(Loc &loc, Type *arrayType, Expression *exp1,
|
|||
args.push_back(val);
|
||||
}
|
||||
|
||||
auto newArray =
|
||||
gIR->CreateCallOrInvoke(fn, args, ".appendedArray").getInstruction();
|
||||
auto newArray = gIR->CreateCallOrInvoke(fn, args, ".appendedArray");
|
||||
return getSlice(arrayType, newArray);
|
||||
}
|
||||
|
||||
|
@ -944,13 +933,10 @@ DSliceValue *DtoAppendDChar(Loc &loc, DValue *arr, Expression *exp,
|
|||
LLFunction *fn = getRuntimeFunction(loc, gIR->module, func);
|
||||
|
||||
// Call function (ref string x, dchar c)
|
||||
LLValue *newArray =
|
||||
gIR->CreateCallOrInvoke(
|
||||
fn,
|
||||
DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(0)),
|
||||
LLValue *newArray = gIR->CreateCallOrInvoke(
|
||||
fn, DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(0)),
|
||||
DtoBitCast(valueToAppend, fn->getFunctionType()->getParamType(1)),
|
||||
".appendedArray")
|
||||
.getInstruction();
|
||||
".appendedArray");
|
||||
|
||||
return getSlice(arr->type, newArray);
|
||||
}
|
||||
|
@ -1003,7 +989,7 @@ LLValue *DtoArrayEqCmp_impl(Loc &loc, const char *func, DValue *l,
|
|||
args.push_back(DtoBitCast(tival, fn->getFunctionType()->getParamType(2)));
|
||||
}
|
||||
|
||||
return gIR->CreateCallOrInvoke(fn, args).getInstruction();
|
||||
return gIR->CreateCallOrInvoke(fn, args);
|
||||
}
|
||||
|
||||
/// When `true` is returned, the type can be compared using `memcmp`.
|
||||
|
|
|
@ -103,10 +103,8 @@ DValue *DtoNewClass(Loc &loc, TypeClass *tc, NewExp *newexp) {
|
|||
loc, gIR->module, useEHAlloc ? "_d_newThrowable" : "_d_allocclass");
|
||||
LLConstant *ci = DtoBitCast(getIrAggr(tc->sym)->getClassInfoSymbol(),
|
||||
DtoType(getClassInfoType()));
|
||||
mem = gIR->CreateCallOrInvoke(fn, ci,
|
||||
useEHAlloc ? ".newthrowable_alloc"
|
||||
: ".newclass_gc_alloc")
|
||||
.getInstruction();
|
||||
mem = gIR->CreateCallOrInvoke(
|
||||
fn, ci, useEHAlloc ? ".newthrowable_alloc" : ".newclass_gc_alloc");
|
||||
mem = DtoBitCast(mem, DtoType(tc),
|
||||
useEHAlloc ? ".newthrowable" : ".newclass_gc");
|
||||
doInit = !useEHAlloc;
|
||||
|
@ -375,7 +373,7 @@ DValue *DtoDynamicCastObject(Loc &loc, DValue *val, Type *_to) {
|
|||
assert(funcTy->getParamType(1) == cinfo->getType());
|
||||
|
||||
// call it
|
||||
LLValue *ret = gIR->CreateCallOrInvoke(func, obj, cinfo).getInstruction();
|
||||
LLValue *ret = gIR->CreateCallOrInvoke(func, obj, cinfo);
|
||||
|
||||
// cast return value
|
||||
ret = DtoBitCast(ret, DtoType(_to));
|
||||
|
@ -409,7 +407,7 @@ DValue *DtoDynamicCastInterface(Loc &loc, DValue *val, Type *_to) {
|
|||
cinfo = DtoBitCast(cinfo, funcTy->getParamType(1));
|
||||
|
||||
// call it
|
||||
LLValue *ret = gIR->CreateCallOrInvoke(func, ptr, cinfo).getInstruction();
|
||||
LLValue *ret = gIR->CreateCallOrInvoke(func, ptr, cinfo);
|
||||
|
||||
// cast return value
|
||||
ret = DtoBitCast(ret, DtoType(_to));
|
||||
|
|
|
@ -103,7 +103,8 @@ FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs)
|
|||
: irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(),
|
||||
irs(irs) {}
|
||||
|
||||
llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee,
|
||||
llvm::CallBase *FuncGenState::callOrInvoke(llvm::Value *callee,
|
||||
llvm::FunctionType *calleeType,
|
||||
llvm::ArrayRef<llvm::Value *> args,
|
||||
const char *name, bool isNothrow) {
|
||||
// If this is a direct call, we might be able to use the callee attributes
|
||||
|
@ -123,8 +124,14 @@ llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee,
|
|||
// calls inside a funclet must be annotated with its value
|
||||
llvm::SmallVector<llvm::OperandBundleDef, 2> BundleList;
|
||||
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
llvm::FunctionCallee calleeArg(calleeType, callee);
|
||||
#else
|
||||
auto calleeArg = callee;
|
||||
#endif
|
||||
|
||||
if (doesNotThrow || scopes.empty()) {
|
||||
llvm::CallInst *call = irs.ir->CreateCall(callee, args, BundleList, name);
|
||||
auto call = irs.ir->CreateCall(calleeArg, args, BundleList, name);
|
||||
if (calleeFn) {
|
||||
call->setAttributes(calleeFn->getAttributes());
|
||||
}
|
||||
|
@ -134,8 +141,8 @@ llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee,
|
|||
llvm::BasicBlock *landingPad = scopes.getLandingPad();
|
||||
|
||||
llvm::BasicBlock *postinvoke = irs.insertBB("postinvoke");
|
||||
llvm::InvokeInst *invoke = irs.ir->CreateInvoke(
|
||||
callee, postinvoke, landingPad, args, BundleList, name);
|
||||
auto invoke = irs.ir->CreateInvoke(calleeArg, postinvoke, landingPad, args,
|
||||
BundleList, name);
|
||||
if (calleeFn) {
|
||||
invoke->setAttributes(calleeFn->getAttributes());
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "gen/pgo_ASTbased.h"
|
||||
#include "gen/trycatchfinally.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include <vector>
|
||||
|
||||
class Identifier;
|
||||
|
@ -200,7 +199,8 @@ public:
|
|||
|
||||
/// Emits a call or invoke to the given callee, depending on whether there
|
||||
/// are catches/cleanups active or not.
|
||||
llvm::CallSite callOrInvoke(llvm::Value *callee,
|
||||
llvm::CallBase *callOrInvoke(llvm::Value *callee,
|
||||
llvm::FunctionType *calleeType,
|
||||
llvm::ArrayRef<llvm::Value *> args,
|
||||
const char *name = "", bool isNothrow = false);
|
||||
|
||||
|
|
|
@ -926,7 +926,7 @@ void emulateWeakAnyLinkageForMSVC(LLFunction *func, LINK linkage) {
|
|||
finalMangle = mangleBuffer;
|
||||
}
|
||||
|
||||
std::string finalWeakMangle = finalMangle;
|
||||
std::string finalWeakMangle = finalMangle.str();
|
||||
if (linkage == LINKcpp) {
|
||||
assert(finalMangle.startswith("?"));
|
||||
// prepend `__weak_` to first identifier
|
||||
|
|
|
@ -99,35 +99,42 @@ llvm::BasicBlock *IRState::insertBB(const llvm::Twine &name) {
|
|||
return insertBBAfter(scopebb(), name);
|
||||
}
|
||||
|
||||
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, const char *Name) {
|
||||
return funcGen().callOrInvoke(Callee, {}, Name);
|
||||
}
|
||||
|
||||
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee,
|
||||
llvm::ArrayRef<LLValue *> Args,
|
||||
const char *Name, bool isNothrow) {
|
||||
return funcGen().callOrInvoke(Callee, Args, Name, isNothrow);
|
||||
}
|
||||
|
||||
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee,
|
||||
const char *Name) {
|
||||
return funcGen().callOrInvoke(Callee, {Arg1}, Name);
|
||||
return CreateCallOrInvoke(Callee, {}, Name);
|
||||
}
|
||||
|
||||
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, const char *Name) {
|
||||
llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee,
|
||||
llvm::ArrayRef<LLValue *> Args,
|
||||
const char *Name,
|
||||
bool isNothrow) {
|
||||
return funcGen().callOrInvoke(Callee, Callee->getFunctionType(), Args, Name,
|
||||
isNothrow);
|
||||
}
|
||||
|
||||
llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee,
|
||||
LLValue *Arg1,
|
||||
const char *Name) {
|
||||
return CreateCallOrInvoke(Callee, llvm::ArrayRef<LLValue *>(Arg1), Name);
|
||||
}
|
||||
|
||||
llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee,
|
||||
LLValue *Arg1, LLValue *Arg2,
|
||||
const char *Name) {
|
||||
return CreateCallOrInvoke(Callee, {Arg1, Arg2}, Name);
|
||||
}
|
||||
|
||||
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, LLValue *Arg3,
|
||||
llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee,
|
||||
LLValue *Arg1, LLValue *Arg2,
|
||||
LLValue *Arg3,
|
||||
const char *Name) {
|
||||
return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3}, Name);
|
||||
}
|
||||
|
||||
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, LLValue *Arg3,
|
||||
LLValue *Arg4, const char *Name) {
|
||||
llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee,
|
||||
LLValue *Arg1, LLValue *Arg2,
|
||||
LLValue *Arg3, LLValue *Arg4,
|
||||
const char *Name) {
|
||||
return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3, Arg4}, Name);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "ir/irvar.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/ProfileData/InstrProfReader.h"
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
@ -188,21 +187,22 @@ public:
|
|||
llvm::BasicBlock *insertBB(const llvm::Twine &name);
|
||||
|
||||
// create a call or invoke, depending on the landing pad info
|
||||
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, const char *Name = "");
|
||||
llvm::CallSite CreateCallOrInvoke(LLValue *Callee,
|
||||
llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee,
|
||||
const char *Name = "");
|
||||
llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee,
|
||||
llvm::ArrayRef<LLValue *> Args,
|
||||
const char *Name = "",
|
||||
bool isNothrow = false);
|
||||
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1,
|
||||
const char *Name = "");
|
||||
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, const char *Name = "");
|
||||
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, LLValue *Arg3,
|
||||
const char *Name = "");
|
||||
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, LLValue *Arg3, LLValue *Arg4,
|
||||
const char *Name = "");
|
||||
llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1,
|
||||
LLValue *Arg2, LLValue *Arg3,
|
||||
LLValue *Arg4, const char *Name = "");
|
||||
|
||||
// this holds the array being indexed or sliced so $ will work
|
||||
// might be a better way but it works. problem is I only get a
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
|
||||
#if LDC_LLVM_VER >= 1000
|
||||
// LLVM >= 10 requires C++14 and no longer has llvm::make_unique. Add it back
|
||||
|
@ -46,8 +45,14 @@ using llvm::APInt;
|
|||
using llvm::IRBuilder;
|
||||
|
||||
#if LDC_LLVM_VER >= 1000
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
#define LLAlign llvm::Align
|
||||
#else
|
||||
#define LLAlign llvm::MaybeAlign
|
||||
#endif
|
||||
#define LLMaybeAlign llvm::MaybeAlign
|
||||
#else
|
||||
#define LLAlign
|
||||
#define LLMaybeAlign
|
||||
#endif
|
||||
|
||||
|
@ -75,6 +80,4 @@ using llvm::IRBuilder;
|
|||
#define LLConstantInt llvm::ConstantInt
|
||||
#define LLConstantFP llvm::ConstantFP
|
||||
|
||||
#define LLCallSite llvm::CallSite
|
||||
|
||||
#define LLSmallVector llvm::SmallVector
|
||||
|
|
|
@ -85,7 +85,7 @@ LLValue *DtoNew(Loc &loc, Type *newtype) {
|
|||
LLConstant *ti = DtoTypeInfoOf(newtype);
|
||||
assert(isaPointer(ti));
|
||||
// call runtime allocator
|
||||
LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem").getInstruction();
|
||||
LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem");
|
||||
// cast
|
||||
return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_mem");
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ LLValue *DtoNewStruct(Loc &loc, TypeStruct *newtype) {
|
|||
loc, gIR->module,
|
||||
newtype->isZeroInit(newtype->sym->loc) ? "_d_newitemT" : "_d_newitemiT");
|
||||
LLConstant *ti = DtoTypeInfoOf(newtype);
|
||||
LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct").getInstruction();
|
||||
LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct");
|
||||
return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_struct");
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,9 @@ llvm::AllocaInst *DtoArrayAlloca(Type *type, unsigned arraysize,
|
|||
auto ai = new llvm::AllocaInst(
|
||||
lltype, gIR->module.getDataLayout().getAllocaAddrSpace(),
|
||||
DtoConstUint(arraysize), name, gIR->topallocapoint());
|
||||
ai->setAlignment(LLMaybeAlign(DtoAlignment(type)));
|
||||
if (auto alignment = DtoAlignment(type)) {
|
||||
ai->setAlignment(LLAlign(alignment));
|
||||
}
|
||||
return ai;
|
||||
}
|
||||
|
||||
|
@ -190,7 +192,7 @@ llvm::AllocaInst *DtoRawAlloca(LLType *lltype, size_t alignment,
|
|||
lltype, gIR->module.getDataLayout().getAllocaAddrSpace(), name,
|
||||
gIR->topallocapoint());
|
||||
if (alignment) {
|
||||
ai->setAlignment(LLMaybeAlign(alignment));
|
||||
ai->setAlignment(LLAlign(alignment));
|
||||
}
|
||||
return ai;
|
||||
}
|
||||
|
@ -201,7 +203,7 @@ LLValue *DtoGcMalloc(Loc &loc, LLType *lltype, const char *name) {
|
|||
// parameters
|
||||
LLValue *size = DtoConstSize_t(getTypeAllocSize(lltype));
|
||||
// call runtime allocator
|
||||
LLValue *mem = gIR->CreateCallOrInvoke(fn, size, name).getInstruction();
|
||||
LLValue *mem = gIR->CreateCallOrInvoke(fn, size, name);
|
||||
// cast
|
||||
return DtoBitCast(mem, getPtrToType(lltype), name);
|
||||
}
|
||||
|
@ -1231,7 +1233,12 @@ LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp) {
|
|||
assert(tv->basetype->ty == Tsarray);
|
||||
dinteger_t elemCount =
|
||||
static_cast<TypeSArray *>(tv->basetype)->dim->toInteger();
|
||||
return llvm::ConstantVector::getSplat(elemCount, val);
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
const auto elementCount = llvm::ElementCount(elemCount, false);
|
||||
#else
|
||||
const auto elementCount = elemCount;
|
||||
#endif
|
||||
return llvm::ConstantVector::getSplat(elementCount, val);
|
||||
}
|
||||
|
||||
if (llType->isIntegerTy() && targetLLType->isIntegerTy()) {
|
||||
|
@ -1310,9 +1317,14 @@ static char *DtoOverloadedIntrinsicName(TemplateInstance *ti,
|
|||
if (dtype->isPPC_FP128Ty()) { // special case
|
||||
replacement = "ppcf128";
|
||||
} else if (dtype->isVectorTy()) {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
auto vectorType = llvm::cast<llvm::FixedVectorType>(dtype);
|
||||
#else
|
||||
auto vectorType = llvm::cast<llvm::VectorType>(dtype);
|
||||
#endif
|
||||
llvm::raw_string_ostream stream(replacement);
|
||||
stream << 'v' << dtype->getVectorNumElements() << prefix
|
||||
<< gDataLayout->getTypeSizeInBits(dtype->getVectorElementType());
|
||||
stream << 'v' << vectorType->getNumElements() << prefix
|
||||
<< gDataLayout->getTypeSizeInBits(vectorType->getElementType());
|
||||
stream.flush();
|
||||
} else {
|
||||
replacement = prefix + std::to_string(gDataLayout->getTypeSizeInBits(dtype));
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
|
@ -115,14 +114,14 @@ public:
|
|||
|
||||
// Analyze the current call, filling in some fields. Returns true if
|
||||
// this is an allocation we can stack-allocate.
|
||||
virtual bool analyze(CallSite CS, const Analysis &A) = 0;
|
||||
virtual bool analyze(CallBase *CB, const Analysis &A) = 0;
|
||||
|
||||
// Returns the alloca to replace this call.
|
||||
// It will always be inserted before the call.
|
||||
virtual Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) {
|
||||
virtual Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) {
|
||||
NumGcToStack++;
|
||||
|
||||
auto &BB = CS.getCaller()->getEntryBlock();
|
||||
auto &BB = CB->getCaller()->getEntryBlock();
|
||||
Instruction *Begin = &(*BB.begin());
|
||||
|
||||
// FIXME: set alignment on alloca?
|
||||
|
@ -167,8 +166,8 @@ public:
|
|||
TypeInfoFI(ReturnType::Type returnType, unsigned tiArgNr)
|
||||
: FunctionInfo(returnType), TypeInfoArgNr(tiArgNr) {}
|
||||
|
||||
bool analyze(CallSite CS, const Analysis &A) override {
|
||||
Value *TypeInfo = CS.getArgument(TypeInfoArgNr);
|
||||
bool analyze(CallBase *CB, const Analysis &A) override {
|
||||
Value *TypeInfo = CB->getArgOperand(TypeInfoArgNr);
|
||||
Ty = A.getTypeFor(TypeInfo);
|
||||
if (!Ty) {
|
||||
return false;
|
||||
|
@ -188,12 +187,12 @@ public:
|
|||
: TypeInfoFI(returnType, tiArgNr), ArrSizeArgNr(arrSizeArgNr),
|
||||
Initialized(initialized) {}
|
||||
|
||||
bool analyze(CallSite CS, const Analysis &A) override {
|
||||
if (!TypeInfoFI::analyze(CS, A)) {
|
||||
bool analyze(CallBase *CB, const Analysis &A) override {
|
||||
if (!TypeInfoFI::analyze(CB, A)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
arrSize = CS.getArgument(ArrSizeArgNr);
|
||||
arrSize = CB->getArgOperand(ArrSizeArgNr);
|
||||
|
||||
// Extract the element type from the array type.
|
||||
const StructType *ArrTy = dyn_cast<StructType>(Ty);
|
||||
|
@ -217,15 +216,16 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) override {
|
||||
IRBuilder<> Builder = B;
|
||||
Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) override {
|
||||
IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint());
|
||||
|
||||
// If the allocation is of constant size it's best to put it in the
|
||||
// entry block, so do so if we're not already there.
|
||||
// For dynamically-sized allocations it's best to avoid the overhead
|
||||
// of allocating them if possible, so leave those where they are.
|
||||
// While we're at it, update statistics too.
|
||||
if (isa<Constant>(arrSize)) {
|
||||
BasicBlock &Entry = CS.getCaller()->getEntryBlock();
|
||||
BasicBlock &Entry = CB->getCaller()->getEntryBlock();
|
||||
if (Builder.GetInsertBlock() != &Entry) {
|
||||
Builder.SetInsertPoint(&Entry, Entry.begin());
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ public:
|
|||
}
|
||||
|
||||
if (ReturnType == ReturnType::Array) {
|
||||
Value *arrStruct = llvm::UndefValue::get(CS.getType());
|
||||
Value *arrStruct = llvm::UndefValue::get(CB->getType());
|
||||
arrStruct = Builder.CreateInsertValue(arrStruct, arrSize, 0);
|
||||
Value *memPtr =
|
||||
Builder.CreateBitCast(alloca, PointerType::getUnqual(B.getInt8Ty()));
|
||||
|
@ -265,11 +265,11 @@ public:
|
|||
// FunctionInfo for _d_allocclass
|
||||
class AllocClassFI : public FunctionInfo {
|
||||
public:
|
||||
bool analyze(CallSite CS, const Analysis &A) override {
|
||||
if (CS.arg_size() != 1) {
|
||||
bool analyze(CallBase *CB, const Analysis &A) override {
|
||||
if (CB->arg_size() != 1) {
|
||||
return false;
|
||||
}
|
||||
Value *arg = CS.getArgument(0)->stripPointerCasts();
|
||||
Value *arg = CB->getArgOperand(0)->stripPointerCasts();
|
||||
GlobalVariable *ClassInfo = dyn_cast<GlobalVariable>(arg);
|
||||
if (!ClassInfo) {
|
||||
return false;
|
||||
|
@ -322,12 +322,12 @@ class UntypedMemoryFI : public FunctionInfo {
|
|||
Value *SizeArg;
|
||||
|
||||
public:
|
||||
bool analyze(CallSite CS, const Analysis &A) override {
|
||||
if (CS.arg_size() < SizeArgNr + 1) {
|
||||
bool analyze(CallBase *CB, const Analysis &A) override {
|
||||
if (CB->arg_size() < SizeArgNr + 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SizeArg = CS.getArgument(SizeArgNr);
|
||||
SizeArg = CB->getArgOperand(SizeArgNr);
|
||||
|
||||
// If the user explicitly disabled the limits, don't even check
|
||||
// whether the allocated size fits in 32 bits. This could cause
|
||||
|
@ -341,19 +341,20 @@ public:
|
|||
}
|
||||
|
||||
// Should be i8.
|
||||
Ty = CS.getType()->getContainedType(0);
|
||||
Ty = CB->getType()->getContainedType(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
Value *promote(CallSite CS, IRBuilder<> &B, const Analysis &A) override {
|
||||
IRBuilder<> Builder = B;
|
||||
Value *promote(CallBase *CB, IRBuilder<> &B, const Analysis &A) override {
|
||||
IRBuilder<> Builder(B.GetInsertBlock(), B.GetInsertPoint());
|
||||
|
||||
// If the allocation is of constant size it's best to put it in the
|
||||
// entry block, so do so if we're not already there.
|
||||
// For dynamically-sized allocations it's best to avoid the overhead
|
||||
// of allocating them if possible, so leave those where they are.
|
||||
// While we're at it, update statistics too.
|
||||
if (isa<Constant>(SizeArg)) {
|
||||
BasicBlock &Entry = CS.getCaller()->getEntryBlock();
|
||||
BasicBlock &Entry = CB->getCaller()->getEntryBlock();
|
||||
if (Builder.GetInsertBlock() != &Entry) {
|
||||
Builder.SetInsertPoint(&Entry, Entry.begin());
|
||||
}
|
||||
|
@ -367,7 +368,7 @@ public:
|
|||
AllocaInst *alloca =
|
||||
Builder.CreateAlloca(Ty, count, ".nongc_mem"); // FIXME: align?
|
||||
|
||||
return Builder.CreateBitCast(alloca, CS.getType());
|
||||
return Builder.CreateBitCast(alloca, CB->getType());
|
||||
}
|
||||
|
||||
explicit UntypedMemoryFI(unsigned sizeArgNr)
|
||||
|
@ -430,26 +431,24 @@ GarbageCollect2Stack::GarbageCollect2Stack()
|
|||
KnownFunctions["_d_allocmemory"] = &AllocMemory;
|
||||
}
|
||||
|
||||
static void RemoveCall(CallSite CS, const Analysis &A) {
|
||||
static void RemoveCall(CallBase *CB, const Analysis &A) {
|
||||
// For an invoke instruction, we insert a branch to the normal target BB
|
||||
// immediately before it. Ideally, we would find a way to not invalidate
|
||||
// the dominator tree here.
|
||||
if (CS.isInvoke()) {
|
||||
InvokeInst *Invoke = cast<InvokeInst>(CS.getInstruction());
|
||||
|
||||
if (auto Invoke = dyn_cast<InvokeInst>(CB)) {
|
||||
BranchInst::Create(Invoke->getNormalDest(), Invoke);
|
||||
Invoke->getUnwindDest()->removePredecessor(CS->getParent());
|
||||
Invoke->getUnwindDest()->removePredecessor(CB->getParent());
|
||||
}
|
||||
|
||||
// Remove the runtime call.
|
||||
if (A.CGNode) {
|
||||
#if LDC_LLVM_VER >= 900
|
||||
A.CGNode->removeCallEdgeFor(*cast<CallBase>(CS.getInstruction()));
|
||||
A.CGNode->removeCallEdgeFor(*CB);
|
||||
#else
|
||||
A.CGNode->removeCallEdgeFor(CS);
|
||||
A.CGNode->removeCallEdgeFor(CB);
|
||||
#endif
|
||||
}
|
||||
CS->eraseFromParent();
|
||||
CB->eraseFromParent();
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -482,14 +481,13 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) {
|
|||
auto originalI = I;
|
||||
|
||||
// Ignore non-calls.
|
||||
Instruction *Inst = &(*(I++));
|
||||
CallSite CS(Inst);
|
||||
if (!CS.getInstruction()) {
|
||||
auto CB = dyn_cast<CallBase>(&(*(I++)));
|
||||
if (!CB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore indirect calls and calls to non-external functions.
|
||||
Function *Callee = CS.getCalledFunction();
|
||||
Function *Callee = CB->getCalledFunction();
|
||||
if (Callee == nullptr || !Callee->isDeclaration() ||
|
||||
!Callee->hasExternalLinkage()) {
|
||||
continue;
|
||||
|
@ -503,16 +501,16 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) {
|
|||
|
||||
FunctionInfo *info = OMI->getValue();
|
||||
|
||||
if (Inst->use_empty()) {
|
||||
if (CB->use_empty()) {
|
||||
Changed = true;
|
||||
NumDeleted++;
|
||||
RemoveCall(CS, A);
|
||||
RemoveCall(CB, A);
|
||||
continue;
|
||||
}
|
||||
|
||||
LLVM_DEBUG(errs() << "GarbageCollect2Stack inspecting: " << *Inst);
|
||||
LLVM_DEBUG(errs() << "GarbageCollect2Stack inspecting: " << *CB);
|
||||
|
||||
if (!info->analyze(CS, A)) {
|
||||
if (!info->analyze(CB, A)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -522,7 +520,7 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) {
|
|||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!isSafeToStackAllocate(originalI, Inst, DT, RemoveTailCallInsts)) {
|
||||
if (!isSafeToStackAllocate(originalI, CB, DT, RemoveTailCallInsts)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -537,18 +535,18 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) {
|
|||
}
|
||||
|
||||
IRBuilder<> Builder(&BB, originalI);
|
||||
Value *newVal = info->promote(CS, Builder, A);
|
||||
Value *newVal = info->promote(CB, Builder, A);
|
||||
|
||||
LLVM_DEBUG(errs() << "Promoted to: " << *newVal);
|
||||
|
||||
// Make sure the type is the same as it was before, and replace all
|
||||
// uses of the runtime call with the alloca.
|
||||
if (newVal->getType() != Inst->getType()) {
|
||||
newVal = Builder.CreateBitCast(newVal, Inst->getType());
|
||||
if (newVal->getType() != CB->getType()) {
|
||||
newVal = Builder.CreateBitCast(newVal, CB->getType());
|
||||
}
|
||||
Inst->replaceAllUsesWith(newVal);
|
||||
CB->replaceAllUsesWith(newVal);
|
||||
|
||||
RemoveCall(CS, A);
|
||||
RemoveCall(CB, A);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -818,11 +816,11 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V,
|
|||
switch (I->getOpcode()) {
|
||||
case Instruction::Call:
|
||||
case Instruction::Invoke: {
|
||||
CallSite CS(I);
|
||||
auto CB = llvm::cast<CallBase>(I);
|
||||
// Not captured if the callee is readonly, doesn't return a copy through
|
||||
// its return value and doesn't unwind (a readonly function can leak bits
|
||||
// by throwing an exception or not depending on the input value).
|
||||
if (CS.onlyReadsMemory() && CS.doesNotThrow() &&
|
||||
if (CB->onlyReadsMemory() && CB->doesNotThrow() &&
|
||||
I->getType() == llvm::Type::getVoidTy(I->getContext())) {
|
||||
break;
|
||||
}
|
||||
|
@ -834,18 +832,17 @@ bool isSafeToStackAllocate(BasicBlock::iterator Alloc, Value *V,
|
|||
// that loading a value from a pointer does not cause the pointer to be
|
||||
// captured, even though the loaded value might be the pointer itself
|
||||
// (think of self-referential objects).
|
||||
CallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
|
||||
for (CallSite::arg_iterator A = B; A != E; ++A) {
|
||||
auto B = CB->arg_begin(), E = CB->arg_end();
|
||||
for (auto A = B; A != E; ++A) {
|
||||
if (A->get() == V) {
|
||||
if (!CS.paramHasAttr(A - B, llvm::Attribute::AttrKind::NoCapture)) {
|
||||
if (!CB->paramHasAttr(A - B, llvm::Attribute::AttrKind::NoCapture)) {
|
||||
// The parameter is not marked 'nocapture' - captured.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CS.isCall()) {
|
||||
CallInst *CI = cast<CallInst>(I);
|
||||
if (CI->isTailCall()) {
|
||||
RemoveTailCallInsts.push_back(CI);
|
||||
if (auto call = dyn_cast<CallInst>(CB)) {
|
||||
if (call->isTailCall()) {
|
||||
RemoveTailCallInsts.push_back(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -405,7 +405,9 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
|
|||
|
||||
llvm::StoreInst *ret = p->ir->CreateStore(val, ptr);
|
||||
ret->setAtomic(llvm::AtomicOrdering(atomicOrdering));
|
||||
ret->setAlignment(LLMaybeAlign(getTypeAllocSize(val->getType())));
|
||||
if (auto alignment = getTypeAllocSize(val->getType())) {
|
||||
ret->setAlignment(LLAlign(alignment));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -435,7 +437,9 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
|
|||
}
|
||||
|
||||
llvm::LoadInst *load = p->ir->CreateLoad(ptr);
|
||||
load->setAlignment(LLMaybeAlign(getTypeAllocSize(load->getType())));
|
||||
if (auto alignment = getTypeAllocSize(load->getType())) {
|
||||
load->setAlignment(LLAlign(alignment));
|
||||
}
|
||||
load->setAtomic(llvm::AtomicOrdering(atomicOrdering));
|
||||
llvm::Value *val = load;
|
||||
if (val->getType() != pointeeType) {
|
||||
|
@ -868,21 +872,21 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval,
|
|||
}
|
||||
|
||||
// call the function
|
||||
LLCallSite call = gIR->CreateCallOrInvoke(callable, args, "", tf->isnothrow);
|
||||
llvm::CallBase *call = gIR->funcGen().callOrInvoke(callable, callableTy, args,
|
||||
"", tf->isnothrow);
|
||||
|
||||
// PGO: Insert instrumentation or attach profile metadata at indirect call
|
||||
// sites.
|
||||
if (!call.getCalledFunction()) {
|
||||
if (!call->getCalledFunction()) {
|
||||
auto &PGO = gIR->funcGen().pgo;
|
||||
PGO.emitIndirectCallPGO(call.getInstruction(), callable);
|
||||
PGO.emitIndirectCallPGO(call, callable);
|
||||
}
|
||||
|
||||
// get return value
|
||||
const int sretArgIndex =
|
||||
(irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(tf) ? 1
|
||||
: 0);
|
||||
LLValue *retllval =
|
||||
(irFty.arg_sret ? args[sretArgIndex] : call.getInstruction());
|
||||
LLValue *retllval = (irFty.arg_sret ? args[sretArgIndex] : call);
|
||||
bool retValIsLVal =
|
||||
(tf->isref && returnTy != Tvoid) || (irFty.arg_sret != nullptr);
|
||||
|
||||
|
@ -1002,16 +1006,16 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval,
|
|||
gIR->context(),
|
||||
static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
|
||||
} else {
|
||||
call.setCallingConv(callconv);
|
||||
call->setCallingConv(callconv);
|
||||
}
|
||||
} else {
|
||||
call.setCallingConv(callconv);
|
||||
call->setCallingConv(callconv);
|
||||
}
|
||||
// merge in function attributes set in callOrInvoke
|
||||
attrlist = attrlist.addAttributes(
|
||||
gIR->context(), LLAttributeList::FunctionIndex,
|
||||
llvm::AttrBuilder(call.getAttributes(), LLAttributeList::FunctionIndex));
|
||||
call.setAttributes(attrlist);
|
||||
llvm::AttrBuilder(call->getAttributes(), LLAttributeList::FunctionIndex));
|
||||
call->setAttributes(attrlist);
|
||||
|
||||
// Special case for struct constructor calls: For temporaries, using the
|
||||
// this pointer value returned from the constructor instead of the alloca
|
||||
|
|
|
@ -664,8 +664,13 @@ public:
|
|||
// cast.
|
||||
// FIXME: Check DMD source to understand why two different ASTs are
|
||||
// constructed.
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
const auto elementCount = llvm::ElementCount(elemCount, false);
|
||||
#else
|
||||
const auto elementCount = elemCount;
|
||||
#endif
|
||||
result = llvm::ConstantVector::getSplat(
|
||||
elemCount, toConstElem(e->e1->optimize(WANTvalue)));
|
||||
elementCount, toConstElem(e->e1->optimize(WANTvalue)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
gen/toir.cpp
11
gen/toir.cpp
|
@ -2428,8 +2428,7 @@ public:
|
|||
LLValue *valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2));
|
||||
|
||||
LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, keysArray,
|
||||
valuesArray, "aa")
|
||||
.getInstruction();
|
||||
valuesArray, "aa");
|
||||
if (basetype->ty != Taarray) {
|
||||
LLValue *tmp = DtoAlloca(e->type, "aaliteral");
|
||||
DtoStore(aa, DtoGEP(tmp, 0u, 0));
|
||||
|
@ -2623,7 +2622,13 @@ public:
|
|||
DValue *val = toElem(e->e1);
|
||||
LLValue *llElement = getCastElement(val);
|
||||
if (auto llConstant = isaConstant(llElement)) {
|
||||
auto vectorConstant = llvm::ConstantVector::getSplat(N, llConstant);
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
const auto elementCount = llvm::ElementCount(N, false);
|
||||
#else
|
||||
const auto elementCount = N;
|
||||
#endif
|
||||
auto vectorConstant =
|
||||
llvm::ConstantVector::getSplat(elementCount, llConstant);
|
||||
DtoStore(vectorConstant, dstMem);
|
||||
} else {
|
||||
for (unsigned int i = 0; i < N; ++i) {
|
||||
|
|
|
@ -452,7 +452,9 @@ LLValue *DtoLoad(LLValue *src, const char *name) {
|
|||
// the type.
|
||||
LLValue *DtoAlignedLoad(LLValue *src, const char *name) {
|
||||
llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name);
|
||||
ld->setAlignment(LLMaybeAlign(getABITypeAlign(ld->getType())));
|
||||
if (auto alignment = getABITypeAlign(ld->getType())) {
|
||||
ld->setAlignment(LLAlign(alignment));
|
||||
}
|
||||
return ld;
|
||||
}
|
||||
|
||||
|
@ -489,7 +491,9 @@ void DtoAlignedStore(LLValue *src, LLValue *dst) {
|
|||
assert(src->getType() != llvm::Type::getInt1Ty(gIR->context()) &&
|
||||
"Should store bools as i8 instead of i1.");
|
||||
llvm::StoreInst *st = gIR->ir->CreateStore(src, dst);
|
||||
st->setAlignment(LLMaybeAlign(getABITypeAlign(src->getType())));
|
||||
if (auto alignment = getABITypeAlign(src->getType())) {
|
||||
st->setAlignment(LLAlign(alignment));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -365,9 +365,9 @@ llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock,
|
|||
// We need a branch selector if we are here...
|
||||
if (!branchSelector) {
|
||||
// ... and have not created one yet, so do so now.
|
||||
llvm::Type *branchSelectorType = llvm::Type::getInt32Ty(irs.context());
|
||||
branchSelector = new llvm::AllocaInst(
|
||||
llvm::Type::getInt32Ty(irs.context()),
|
||||
irs.module.getDataLayout().getAllocaAddrSpace(),
|
||||
branchSelectorType, irs.module.getDataLayout().getAllocaAddrSpace(),
|
||||
llvm::Twine("branchsel.") + beginBlock()->getName(),
|
||||
irs.topallocapoint());
|
||||
|
||||
|
@ -380,7 +380,11 @@ llvm::BasicBlock *CleanupScope::run(IRState &irs, llvm::BasicBlock *sourceBlock,
|
|||
// And convert the BranchInst to the existing branch target to a
|
||||
// SelectInst so we can append the other cases to it.
|
||||
endBlock()->getTerminator()->eraseFromParent();
|
||||
llvm::Value *sel = new llvm::LoadInst(branchSelector, "", endBlock());
|
||||
llvm::Value *sel = new llvm::LoadInst(
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
branchSelectorType,
|
||||
#endif
|
||||
branchSelector, "", endBlock());
|
||||
llvm::SwitchInst::Create(
|
||||
sel, exitTargets[0].branchTarget,
|
||||
1, // Expected number of branches, only for pre-allocating.
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
|
||||
#if LDC_LLVM_VER < 1100
|
||||
namespace llvm {
|
||||
// Auto-generate:
|
||||
// Attribute::AttrKind getAttrKindFromName(StringRef AttrName) { ... }
|
||||
|
@ -26,6 +27,7 @@ namespace llvm {
|
|||
#include "llvm/IR/Attributes.inc"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -205,7 +207,11 @@ void applyAttrLLVMAttr(StructLiteralExp *sle, llvm::AttrBuilder &attrs) {
|
|||
llvm::StringRef key = getStringElem(sle, 0);
|
||||
llvm::StringRef value = getStringElem(sle, 1);
|
||||
if (value.empty()) {
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
const auto kind = llvm::Attribute::getAttrKindFromName(key);
|
||||
#else
|
||||
const auto kind = llvm::getAttrKindFromName(key);
|
||||
#endif
|
||||
if (kind != llvm::Attribute::None) {
|
||||
attrs.addAttribute(kind);
|
||||
} else {
|
||||
|
|
|
@ -220,5 +220,10 @@ llvm::Type *IrTypeVector::vector2llvm(Type *dt) {
|
|||
TypeSArray *tsa = static_cast<TypeSArray *>(tv->basetype);
|
||||
uint64_t dim = static_cast<uint64_t>(tsa->dim->toUInteger());
|
||||
LLType *elemType = DtoMemType(tsa->next);
|
||||
return llvm::VectorType::get(elemType, dim);
|
||||
return llvm::VectorType::get(elemType, dim
|
||||
#if LDC_LLVM_VER >= 1100
|
||||
,
|
||||
/*Scalable=*/false
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ config.available_features.add("llvm%d" % config.llvm_version)
|
|||
plusoneable_llvmversion = config.llvm_version // 10 + config.llvm_version%10
|
||||
for version in range(60, plusoneable_llvmversion+1):
|
||||
config.available_features.add("atleast_llvm%d0%d" % (version//10, version%10))
|
||||
for version in range(plusoneable_llvmversion, 101):
|
||||
for version in range(plusoneable_llvmversion, 111):
|
||||
config.available_features.add("atmost_llvm%d0%d" % (version//10, version%10))
|
||||
|
||||
# Define OS as available feature (Windows, Darwin, Linux)
|
||||
|
|
1344
tools/ldc-profdata/llvm-profdata-11.0.cpp
Normal file
1344
tools/ldc-profdata/llvm-profdata-11.0.cpp
Normal file
File diff suppressed because it is too large
Load diff
859
utils/FileCheck-11.0.cpp
Normal file
859
utils/FileCheck-11.0.cpp
Normal file
|
@ -0,0 +1,859 @@
|
|||
//===- FileCheck.cpp - Check that File's Contents match what is expected --===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// FileCheck does a line-by line check of a file that validates whether it
|
||||
// contains the expected content. This is useful for regression tests etc.
|
||||
//
|
||||
// This program exits with an exit status of 2 on error, exit status of 0 if
|
||||
// the file matched the expected contents, and exit status of 1 if it did not
|
||||
// contain the expected contents.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/InitLLVM.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/WithColor.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/FileCheck.h"
|
||||
#include <cmath>
|
||||
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> 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::init(false), cl::ZeroOrMore,
|
||||
cl::desc("Print directive pattern matches, or add them to the input dump\n"
|
||||
"if enabled.\n"));
|
||||
|
||||
static cl::opt<bool> VerboseVerbose(
|
||||
"vv", cl::init(false), cl::ZeroOrMore,
|
||||
cl::desc("Print information helpful in diagnosing internal FileCheck\n"
|
||||
"issues, or add it to the input dump if enabled. Implies\n"
|
||||
"-v.\n"));
|
||||
|
||||
// The order of DumpInputValue members affects their precedence, as documented
|
||||
// for -dump-input below.
|
||||
enum DumpInputValue {
|
||||
DumpInputNever,
|
||||
DumpInputFail,
|
||||
DumpInputAlways,
|
||||
DumpInputHelp
|
||||
};
|
||||
|
||||
static cl::list<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::MatchNoneAndExcluded:
|
||||
return MarkerStyle('X', raw_ostream::GREEN);
|
||||
case FileCheckDiag::MatchNoneButExpected:
|
||||
return MarkerStyle('X', raw_ostream::RED, "error: no match found",
|
||||
/*FiltersAsError=*/true);
|
||||
case FileCheckDiag::MatchFuzzy:
|
||||
return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match",
|
||||
/*FiltersAsError=*/true);
|
||||
}
|
||||
llvm_unreachable_internal("unexpected match type");
|
||||
}
|
||||
|
||||
static void DumpInputAnnotationHelp(raw_ostream &OS) {
|
||||
OS << "The following description was requested by -dump-input=help to\n"
|
||||
<< "explain the input dump printed by FileCheck.\n"
|
||||
<< "\n"
|
||||
<< "Related command-line options:\n"
|
||||
<< "\n"
|
||||
<< " - -dump-input=<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";
|
||||
|
||||
// Labels for annotation lines.
|
||||
OS << " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
|
||||
OS << " labels the only match result for either (1) a pattern of type T"
|
||||
<< " from\n"
|
||||
<< " line L of the check file if L is an integer or (2) the"
|
||||
<< " I-th implicit\n"
|
||||
<< " pattern if L is \"imp\" followed by an integer "
|
||||
<< "I (index origin one)\n";
|
||||
OS << " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
|
||||
OS << " labels the Nth match result for such a pattern\n";
|
||||
|
||||
// Markers on annotation lines.
|
||||
OS << " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "^~~";
|
||||
OS << " marks good match (reported if -v)\n"
|
||||
<< " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "!~~";
|
||||
OS << " marks bad match, such as:\n"
|
||||
<< " - CHECK-NEXT on same line as previous match (error)\n"
|
||||
<< " - CHECK-NOT found (error)\n"
|
||||
<< " - CHECK-DAG overlapping match (discarded, reported if "
|
||||
<< "-vv)\n"
|
||||
<< " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
|
||||
OS << " marks search range when no match is found, such as:\n"
|
||||
<< " - CHECK-NEXT not found (error)\n"
|
||||
<< " - CHECK-NOT not found (success, reported if -vv)\n"
|
||||
<< " - CHECK-DAG not found after discarded matches (error)\n"
|
||||
<< " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
|
||||
OS << " marks fuzzy match when no match is found\n";
|
||||
|
||||
// Elided lines.
|
||||
OS << " - ";
|
||||
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "...";
|
||||
OS << " indicates elided input lines and annotations, as specified by\n"
|
||||
<< " -dump-input-filter and -dump-input-context\n";
|
||||
|
||||
// Colors.
|
||||
OS << " - colors ";
|
||||
WithColor(OS, raw_ostream::GREEN, true) << "success";
|
||||
OS << ", ";
|
||||
WithColor(OS, raw_ostream::RED, true) << "error";
|
||||
OS << ", ";
|
||||
WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
|
||||
OS << ", ";
|
||||
WithColor(OS, raw_ostream::CYAN, true, false) << "discarded match";
|
||||
OS << ", ";
|
||||
WithColor(OS, raw_ostream::CYAN, true, true) << "unmatched input";
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
/// An annotation for a single input line.
|
||||
struct InputAnnotation {
|
||||
/// The index of the match result across all checks
|
||||
unsigned DiagIndex;
|
||||
/// The label for this annotation.
|
||||
std::string Label;
|
||||
/// Is this the initial fragment of a diagnostic that has been broken across
|
||||
/// multiple lines?
|
||||
bool IsFirstLine;
|
||||
/// What input line (one-origin indexing) this annotation marks. This might
|
||||
/// be different from the starting line of the original diagnostic if
|
||||
/// !IsFirstLine.
|
||||
unsigned InputLine;
|
||||
/// The column range (one-origin indexing, open end) in which to mark the
|
||||
/// input line. If InputEndCol is UINT_MAX, treat it as the last column
|
||||
/// before the newline.
|
||||
unsigned InputStartCol, InputEndCol;
|
||||
/// The marker to use.
|
||||
MarkerStyle Marker;
|
||||
/// Whether this annotation represents a good match for an expected pattern.
|
||||
bool FoundAndExpectedMatch;
|
||||
};
|
||||
|
||||
/// Get an abbreviation for the check type.
|
||||
std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
|
||||
switch (Ty) {
|
||||
case Check::CheckPlain:
|
||||
if (Ty.getCount() > 1)
|
||||
return "count";
|
||||
return "check";
|
||||
case Check::CheckNext:
|
||||
return "next";
|
||||
case Check::CheckSame:
|
||||
return "same";
|
||||
case Check::CheckNot:
|
||||
return "not";
|
||||
case Check::CheckDAG:
|
||||
return "dag";
|
||||
case Check::CheckLabel:
|
||||
return "label";
|
||||
case Check::CheckEmpty:
|
||||
return "empty";
|
||||
case Check::CheckComment:
|
||||
return "com";
|
||||
case Check::CheckEOF:
|
||||
return "eof";
|
||||
case Check::CheckBadNot:
|
||||
return "bad-not";
|
||||
case Check::CheckBadCount:
|
||||
return "bad-count";
|
||||
case Check::CheckNone:
|
||||
llvm_unreachable("invalid FileCheckType");
|
||||
}
|
||||
llvm_unreachable("unknown FileCheckType");
|
||||
}
|
||||
|
||||
static void
|
||||
BuildInputAnnotations(const SourceMgr &SM, unsigned CheckFileBufferID,
|
||||
const std::pair<unsigned, unsigned> &ImpPatBufferIDRange,
|
||||
const std::vector<FileCheckDiag> &Diags,
|
||||
std::vector<InputAnnotation> &Annotations,
|
||||
unsigned &LabelWidth) {
|
||||
// How many diagnostics have we seen so far?
|
||||
unsigned DiagCount = 0;
|
||||
// How many diagnostics has the current check seen so far?
|
||||
unsigned CheckDiagCount = 0;
|
||||
// What's the widest label?
|
||||
LabelWidth = 0;
|
||||
for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
|
||||
++DiagItr) {
|
||||
InputAnnotation A;
|
||||
A.DiagIndex = DiagCount++;
|
||||
|
||||
// Build label, which uniquely identifies this check result.
|
||||
unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
|
||||
auto CheckLineAndCol =
|
||||
SM.getLineAndColumn(DiagItr->CheckLoc, CheckBufferID);
|
||||
llvm::raw_string_ostream Label(A.Label);
|
||||
Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":";
|
||||
if (CheckBufferID == CheckFileBufferID)
|
||||
Label << CheckLineAndCol.first;
|
||||
else if (ImpPatBufferIDRange.first <= CheckBufferID &&
|
||||
CheckBufferID < ImpPatBufferIDRange.second)
|
||||
Label << "imp" << (CheckBufferID - ImpPatBufferIDRange.first + 1);
|
||||
else
|
||||
llvm_unreachable("expected diagnostic's check location to be either in "
|
||||
"the check file or for an implicit pattern");
|
||||
unsigned CheckDiagIndex = UINT_MAX;
|
||||
auto DiagNext = std::next(DiagItr);
|
||||
if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy &&
|
||||
DiagItr->CheckLoc == DiagNext->CheckLoc)
|
||||
CheckDiagIndex = CheckDiagCount++;
|
||||
else if (CheckDiagCount) {
|
||||
CheckDiagIndex = CheckDiagCount;
|
||||
CheckDiagCount = 0;
|
||||
}
|
||||
if (CheckDiagIndex != UINT_MAX)
|
||||
Label << "'" << CheckDiagIndex;
|
||||
Label.flush();
|
||||
LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
|
||||
|
||||
A.Marker = GetMarker(DiagItr->MatchTy);
|
||||
A.FoundAndExpectedMatch =
|
||||
DiagItr->MatchTy == FileCheckDiag::MatchFoundAndExpected;
|
||||
|
||||
// Compute the mark location, and break annotation into multiple
|
||||
// annotations if it spans multiple lines.
|
||||
A.IsFirstLine = true;
|
||||
A.InputLine = DiagItr->InputStartLine;
|
||||
A.InputStartCol = DiagItr->InputStartCol;
|
||||
if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
|
||||
// Sometimes ranges are empty in order to indicate a specific point, but
|
||||
// that would mean nothing would be marked, so adjust the range to
|
||||
// include the following character.
|
||||
A.InputEndCol =
|
||||
std::max(DiagItr->InputStartCol + 1, DiagItr->InputEndCol);
|
||||
Annotations.push_back(A);
|
||||
} else {
|
||||
assert(DiagItr->InputStartLine < DiagItr->InputEndLine &&
|
||||
"expected input range not to be inverted");
|
||||
A.InputEndCol = UINT_MAX;
|
||||
Annotations.push_back(A);
|
||||
for (unsigned L = DiagItr->InputStartLine + 1, E = DiagItr->InputEndLine;
|
||||
L <= E; ++L) {
|
||||
// If a range ends before the first column on a line, then it has no
|
||||
// characters on that line, so there's nothing to render.
|
||||
if (DiagItr->InputEndCol == 1 && L == E)
|
||||
break;
|
||||
InputAnnotation B;
|
||||
B.DiagIndex = A.DiagIndex;
|
||||
B.Label = A.Label;
|
||||
B.IsFirstLine = false;
|
||||
B.InputLine = L;
|
||||
B.Marker = A.Marker;
|
||||
B.Marker.Lead = '~';
|
||||
B.Marker.Note = "";
|
||||
B.InputStartCol = 1;
|
||||
if (L != E)
|
||||
B.InputEndCol = UINT_MAX;
|
||||
else
|
||||
B.InputEndCol = DiagItr->InputEndCol;
|
||||
B.FoundAndExpectedMatch = A.FoundAndExpectedMatch;
|
||||
Annotations.push_back(B);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned FindInputLineInFilter(
|
||||
DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
|
||||
const std::vector<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.
|
||||
std::sort(Annotations.begin(), Annotations.end(),
|
||||
[](const InputAnnotation &A, const InputAnnotation &B) {
|
||||
// 1. Sort annotations in the order of the input lines.
|
||||
//
|
||||
// This makes it easier to find relevant annotations while
|
||||
// iterating input lines in the implementation below. FileCheck
|
||||
// does not always produce diagnostics in the order of input
|
||||
// lines due to, for example, CHECK-DAG and CHECK-NOT.
|
||||
if (A.InputLine != B.InputLine)
|
||||
return A.InputLine < B.InputLine;
|
||||
// 2. Sort annotations in the temporal order FileCheck produced
|
||||
// their associated diagnostics.
|
||||
//
|
||||
// This sort offers several benefits:
|
||||
//
|
||||
// A. On a single input line, the order of annotations reflects
|
||||
// the FileCheck logic for processing directives/patterns.
|
||||
// This can be helpful in understanding cases in which the
|
||||
// order of the associated directives/patterns in the check
|
||||
// file or on the command line either (i) does not match the
|
||||
// temporal order in which FileCheck looks for matches for the
|
||||
// directives/patterns (due to, for example, CHECK-LABEL,
|
||||
// CHECK-NOT, or `--implicit-check-not`) or (ii) does match
|
||||
// that order but does not match the order of those
|
||||
// diagnostics along an input line (due to, for example,
|
||||
// CHECK-DAG).
|
||||
//
|
||||
// On the other hand, because our presentation format presents
|
||||
// input lines in order, there's no clear way to offer the
|
||||
// same benefit across input lines. For consistency, it might
|
||||
// then seem worthwhile to have annotations on a single line
|
||||
// also sorted in input order (that is, by input column).
|
||||
// However, in practice, this appears to be more confusing
|
||||
// than helpful. Perhaps it's intuitive to expect annotations
|
||||
// to be listed in the temporal order in which they were
|
||||
// produced except in cases the presentation format obviously
|
||||
// and inherently cannot support it (that is, across input
|
||||
// lines).
|
||||
//
|
||||
// B. When diagnostics' annotations are split among multiple
|
||||
// input lines, the user must track them from one input line
|
||||
// to the next. One property of the sort chosen here is that
|
||||
// it facilitates the user in this regard by ensuring the
|
||||
// following: when comparing any two input lines, a
|
||||
// diagnostic's annotations are sorted in the same position
|
||||
// relative to all other diagnostics' annotations.
|
||||
return A.DiagIndex < B.DiagIndex;
|
||||
});
|
||||
|
||||
// Compute the width of the label column.
|
||||
const unsigned char *InputFilePtr = InputFileText.bytes_begin(),
|
||||
*InputFileEnd = InputFileText.bytes_end();
|
||||
unsigned LineCount = InputFileText.count('\n');
|
||||
if (InputFileEnd[-1] != '\n')
|
||||
++LineCount;
|
||||
unsigned LineNoWidth = std::log10(LineCount) + 1;
|
||||
// +3 below adds spaces (1) to the left of the (right-aligned) line numbers
|
||||
// on input lines and (2) to the right of the (left-aligned) labels on
|
||||
// annotation lines so that input lines and annotation lines are more
|
||||
// visually distinct. For example, the spaces on the annotation lines ensure
|
||||
// that input line numbers and check directive line numbers never align
|
||||
// horizontally. Those line numbers might not even be for the same file.
|
||||
// One space would be enough to achieve that, but more makes it even easier
|
||||
// to see.
|
||||
LabelWidth = std::max(LabelWidth, LineNoWidth) + 3;
|
||||
|
||||
// Print annotated input lines.
|
||||
unsigned PrevLineInFilter = 0; // 0 means none so far
|
||||
unsigned NextLineInFilter = 0; // 0 means uncomputed, UINT_MAX means none
|
||||
std::string ElidedLines;
|
||||
raw_string_ostream ElidedLinesOS(ElidedLines);
|
||||
ColorMode TheColorMode =
|
||||
WithColor(OS).colorsEnabled() ? ColorMode::Enable : ColorMode::Disable;
|
||||
if (TheColorMode == ColorMode::Enable)
|
||||
ElidedLinesOS.enable_colors(true);
|
||||
auto AnnotationItr = Annotations.begin(), AnnotationEnd = Annotations.end();
|
||||
for (unsigned Line = 1;
|
||||
InputFilePtr != InputFileEnd || AnnotationItr != AnnotationEnd;
|
||||
++Line) {
|
||||
const unsigned char *InputFileLine = InputFilePtr;
|
||||
|
||||
// Compute the previous and next line included by the filter.
|
||||
if (NextLineInFilter < Line)
|
||||
NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
|
||||
AnnotationItr, AnnotationEnd);
|
||||
assert(NextLineInFilter && "expected NextLineInFilter to be computed");
|
||||
if (NextLineInFilter == Line)
|
||||
PrevLineInFilter = Line;
|
||||
|
||||
// Elide this input line and its annotations if it's not within the
|
||||
// context specified by -dump-input-context of an input line included by
|
||||
// -dump-input-filter. However, in case the resulting ellipsis would occupy
|
||||
// more lines than the input lines and annotations it elides, buffer the
|
||||
// elided lines and annotations so we can print them instead.
|
||||
raw_ostream *LineOS = &OS;
|
||||
if ((!PrevLineInFilter || PrevLineInFilter + DumpInputContext < Line) &&
|
||||
(NextLineInFilter == UINT_MAX ||
|
||||
Line + DumpInputContext < NextLineInFilter))
|
||||
LineOS = &ElidedLinesOS;
|
||||
else {
|
||||
LineOS = &OS;
|
||||
DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
|
||||
}
|
||||
|
||||
// Print right-aligned line number.
|
||||
WithColor(*LineOS, raw_ostream::BLACK, /*Bold=*/true, /*BF=*/false,
|
||||
TheColorMode)
|
||||
<< format_decimal(Line, LabelWidth) << ": ";
|
||||
|
||||
// For the case where -v and colors are enabled, find the annotations for
|
||||
// good matches for expected patterns in order to highlight everything
|
||||
// else in the line. There are no such annotations if -v is disabled.
|
||||
std::vector<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;
|
||||
else
|
||||
COS << *InputFilePtr;
|
||||
++InputFilePtr;
|
||||
}
|
||||
}
|
||||
*LineOS << '\n';
|
||||
unsigned InputLineWidth = InputFilePtr - InputFileLine - Newline;
|
||||
|
||||
// Print any annotations.
|
||||
while (AnnotationItr != AnnotationEnd &&
|
||||
AnnotationItr->InputLine == Line) {
|
||||
WithColor COS(*LineOS, AnnotationItr->Marker.Color, /*Bold=*/true,
|
||||
/*BG=*/false, TheColorMode);
|
||||
// The two spaces below are where the ": " appears on input lines.
|
||||
COS << left_justify(AnnotationItr->Label, LabelWidth) << " ";
|
||||
unsigned Col;
|
||||
for (Col = 1; Col < AnnotationItr->InputStartCol; ++Col)
|
||||
COS << ' ';
|
||||
COS << AnnotationItr->Marker.Lead;
|
||||
// If InputEndCol=UINT_MAX, stop at InputLineWidth.
|
||||
for (++Col; Col < AnnotationItr->InputEndCol && Col <= InputLineWidth;
|
||||
++Col)
|
||||
COS << '~';
|
||||
const std::string &Note = AnnotationItr->Marker.Note;
|
||||
if (!Note.empty()) {
|
||||
// Put the note at the end of the input line. If we were to instead
|
||||
// put the note right after the marker, subsequent annotations for the
|
||||
// same input line might appear to mark this note instead of the input
|
||||
// line.
|
||||
for (; Col <= InputLineWidth; ++Col)
|
||||
COS << ' ';
|
||||
COS << ' ' << Note;
|
||||
}
|
||||
COS << '\n';
|
||||
++AnnotationItr;
|
||||
}
|
||||
}
|
||||
DumpEllipsisOrElidedLines(OS, ElidedLinesOS.str(), LabelWidth);
|
||||
|
||||
OS << ">>>>>>\n";
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Enable use of ANSI color codes because FileCheck is using them to
|
||||
// highlight text.
|
||||
llvm::sys::Process::UseANSIEscapeCodes(true);
|
||||
|
||||
InitLLVM X(argc, argv);
|
||||
cl::ParseCommandLineOptions(argc, argv, /*Overview*/ "", /*Errs*/ nullptr,
|
||||
"FILECHECK_OPTS");
|
||||
|
||||
// Select -dump-input* values. The -help documentation specifies the default
|
||||
// value and which value to choose if an option is specified multiple times.
|
||||
// In the latter case, the general rule of thumb is to choose the value that
|
||||
// provides the most information.
|
||||
DumpInputValue DumpInput =
|
||||
DumpInputs.empty()
|
||||
? DumpInputFail
|
||||
: *std::max_element(DumpInputs.begin(), DumpInputs.end());
|
||||
DumpInputFilterValue DumpInputFilter;
|
||||
if (DumpInputFilters.empty())
|
||||
DumpInputFilter = DumpInput == DumpInputAlways ? DumpInputFilterAll
|
||||
: DumpInputFilterError;
|
||||
else
|
||||
DumpInputFilter =
|
||||
*std::max_element(DumpInputFilters.begin(), DumpInputFilters.end());
|
||||
unsigned DumpInputContext = DumpInputContexts.empty()
|
||||
? 5
|
||||
: *std::max_element(DumpInputContexts.begin(),
|
||||
DumpInputContexts.end());
|
||||
|
||||
if (DumpInput == DumpInputHelp) {
|
||||
DumpInputAnnotationHelp(outs());
|
||||
return 0;
|
||||
}
|
||||
if (CheckFilename.empty()) {
|
||||
errs() << "<check-file> not specified\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
FileCheckRequest Req;
|
||||
for (StringRef Prefix : CheckPrefixes)
|
||||
Req.CheckPrefixes.push_back(Prefix);
|
||||
|
||||
for (StringRef Prefix : CommentPrefixes)
|
||||
Req.CommentPrefixes.push_back(Prefix);
|
||||
|
||||
for (StringRef CheckNot : ImplicitCheckNot)
|
||||
Req.ImplicitCheckNot.push_back(CheckNot);
|
||||
|
||||
bool GlobalDefineError = false;
|
||||
for (StringRef G : GlobalDefines) {
|
||||
size_t EqIdx = G.find('=');
|
||||
if (EqIdx == std::string::npos) {
|
||||
errs() << "Missing equal sign in command-line definition '-D" << G
|
||||
<< "'\n";
|
||||
GlobalDefineError = true;
|
||||
continue;
|
||||
}
|
||||
if (EqIdx == 0) {
|
||||
errs() << "Missing variable name in command-line definition '-D" << G
|
||||
<< "'\n";
|
||||
GlobalDefineError = true;
|
||||
continue;
|
||||
}
|
||||
Req.GlobalDefines.push_back(G);
|
||||
}
|
||||
if (GlobalDefineError)
|
||||
return 2;
|
||||
|
||||
Req.AllowEmptyInput = AllowEmptyInput;
|
||||
Req.EnableVarScope = EnableVarScope;
|
||||
Req.AllowDeprecatedDagOverlap = AllowDeprecatedDagOverlap;
|
||||
Req.Verbose = Verbose;
|
||||
Req.VerboseVerbose = VerboseVerbose;
|
||||
Req.NoCanonicalizeWhiteSpace = NoCanonicalizeWhiteSpace;
|
||||
Req.MatchFullLines = MatchFullLines;
|
||||
Req.IgnoreCase = IgnoreCase;
|
||||
|
||||
if (VerboseVerbose)
|
||||
Req.Verbose = true;
|
||||
|
||||
FileCheck FC(Req);
|
||||
if (!FC.ValidateCheckPrefixes())
|
||||
return 2;
|
||||
|
||||
Regex PrefixRE = FC.buildCheckPrefixRegex();
|
||||
std::string REError;
|
||||
if (!PrefixRE.isValid(REError)) {
|
||||
errs() << "Unable to combine check-prefix strings into a prefix regular "
|
||||
"expression! This is likely a bug in FileCheck's verification of "
|
||||
"the check-prefix strings. Regular expression parsing failed "
|
||||
"with the following error: "
|
||||
<< REError << "\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
SourceMgr SM;
|
||||
|
||||
// Read the expected strings from the check file.
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> CheckFileOrErr =
|
||||
MemoryBuffer::getFileOrSTDIN(CheckFilename);
|
||||
if (std::error_code EC = CheckFileOrErr.getError()) {
|
||||
errs() << "Could not open check file '" << CheckFilename
|
||||
<< "': " << EC.message() << '\n';
|
||||
return 2;
|
||||
}
|
||||
MemoryBuffer &CheckFile = *CheckFileOrErr.get();
|
||||
|
||||
SmallString<4096> CheckFileBuffer;
|
||||
StringRef CheckFileText = FC.CanonicalizeFile(CheckFile, CheckFileBuffer);
|
||||
|
||||
unsigned CheckFileBufferID =
|
||||
SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(
|
||||
CheckFileText, CheckFile.getBufferIdentifier()),
|
||||
SMLoc());
|
||||
|
||||
std::pair<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);
|
||||
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;
|
||||
}
|
|
@ -68,16 +68,17 @@ string dtype(Record* rec, bool readOnlyMem)
|
|||
return "";
|
||||
}
|
||||
|
||||
string attributes(ListInit* propertyList)
|
||||
StringRef attributes(ListInit* propertyList)
|
||||
{
|
||||
string prop =
|
||||
propertyList->size()
|
||||
? propertyList->getElementAsRecord(0)->getName() : "";
|
||||
const auto prop = propertyList->size()
|
||||
? propertyList->getElementAsRecord(0)->getName()
|
||||
: "";
|
||||
|
||||
return
|
||||
prop == "IntrNoMem" ? " pure @safe" :
|
||||
prop == "IntrReadArgMem" ? " pure" :
|
||||
prop == "IntrReadWriteArgMem" ? " pure" : "";
|
||||
if (prop == "IntrNoMem")
|
||||
return " pure @safe";
|
||||
if (prop == "IntrReadArgMem" || prop == "IntrReadWriteArgMem")
|
||||
return " pure";
|
||||
return "";
|
||||
}
|
||||
|
||||
void processRecord(raw_ostream& os, Record& rec, string arch)
|
||||
|
@ -85,8 +86,8 @@ void processRecord(raw_ostream& os, Record& rec, string arch)
|
|||
if(!rec.getValue("GCCBuiltinName"))
|
||||
return;
|
||||
|
||||
string builtinName = rec.getValueAsString("GCCBuiltinName");
|
||||
string name = rec.getName();
|
||||
const StringRef builtinName = rec.getValueAsString("GCCBuiltinName");
|
||||
string name = rec.getName().str();
|
||||
|
||||
if(name.substr(0, 4) != "int_" || name.find(arch) == string::npos)
|
||||
return;
|
||||
|
@ -96,9 +97,8 @@ void processRecord(raw_ostream& os, Record& rec, string arch)
|
|||
name = string("llvm.") + name;
|
||||
|
||||
ListInit* propsList = rec.getValueAsListInit("IntrProperties");
|
||||
string prop =
|
||||
propsList->size()
|
||||
? propsList->getElementAsRecord(0)->getName() : "";
|
||||
const StringRef prop =
|
||||
propsList->size() ? propsList->getElementAsRecord(0)->getName() : "";
|
||||
|
||||
bool readOnlyMem = prop == "IntrReadArgMem" || prop == "IntrReadMem";
|
||||
|
||||
|
@ -127,8 +127,8 @@ void processRecord(raw_ostream& os, Record& rec, string arch)
|
|||
else
|
||||
return;
|
||||
|
||||
os << "pragma(LDC_intrinsic, \"" + name + "\")\n ";
|
||||
os << ret + " " + builtinName + "(";
|
||||
os << "pragma(LDC_intrinsic, \"" << name << "\")\n ";
|
||||
os << ret << " " << builtinName << "(";
|
||||
|
||||
if(params.size())
|
||||
os << params[0];
|
||||
|
@ -136,7 +136,7 @@ void processRecord(raw_ostream& os, Record& rec, string arch)
|
|||
for(size_t i = 1; i < params.size(); i++)
|
||||
os << ", " << params[i];
|
||||
|
||||
os << ")" + attributes(propsList) + ";\n\n";
|
||||
os << ")" << attributes(propsList) << ";\n\n";
|
||||
}
|
||||
|
||||
std::string arch;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue