diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
| commit | 044eb2f6afba375a914ac9d8024f8f5142bb912e (patch) | |
| tree | 1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /include/llvm/Support | |
| parent | eb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff) | |
Notes
Diffstat (limited to 'include/llvm/Support')
54 files changed, 2968 insertions, 1914 deletions
diff --git a/include/llvm/Support/AArch64TargetParser.def b/include/llvm/Support/AArch64TargetParser.def index 09f9602a24d9..30c7924ea5f1 100644 --- a/include/llvm/Support/AArch64TargetParser.def +++ b/include/llvm/Support/AArch64TargetParser.def @@ -16,19 +16,25 @@ #ifndef AARCH64_ARCH #define AARCH64_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, ARCH_BASE_EXT) #endif -AARCH64_ARCH("invalid", AK_INVALID, nullptr, nullptr, +AARCH64_ARCH("invalid", INVALID, "", "", ARMBuildAttrs::CPUArch::v8_A, FK_NONE, AArch64::AEK_NONE) -AARCH64_ARCH("armv8-a", AK_ARMV8A, "8-A", "v8", ARMBuildAttrs::CPUArch::v8_A, +AARCH64_ARCH("armv8-a", ARMV8A, "8-A", "v8", ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, (AArch64::AEK_CRYPTO | AArch64::AEK_FP | AArch64::AEK_SIMD)) -AARCH64_ARCH("armv8.1-a", AK_ARMV8_1A, "8.1-A", "v8.1a", +AARCH64_ARCH("armv8.1-a", ARMV8_1A, "8.1-A", "v8.1a", ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, (AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | - AArch64::AEK_SIMD | AArch64::AEK_LSE)) -AARCH64_ARCH("armv8.2-a", AK_ARMV8_2A, "8.2-A", "v8.2a", + AArch64::AEK_SIMD | AArch64::AEK_LSE | AArch64::AEK_RDM)) +AARCH64_ARCH("armv8.2-a", ARMV8_2A, "8.2-A", "v8.2a", ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, (AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | - AArch64::AEK_SIMD | AArch64::AEK_RAS | AArch64::AEK_LSE)) + AArch64::AEK_SIMD | AArch64::AEK_RAS | AArch64::AEK_LSE | + AArch64::AEK_RDM)) +AARCH64_ARCH("armv8.3-a", ARMV8_3A, "8.3-A", "v8.3a", + ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, + (AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | + AArch64::AEK_SIMD | AArch64::AEK_RAS | AArch64::AEK_LSE | + AArch64::AEK_RDM | AArch64::AEK_RCPC)) #undef AARCH64_ARCH #ifndef AARCH64_ARCH_EXT_NAME @@ -39,50 +45,59 @@ AARCH64_ARCH_EXT_NAME("invalid", AArch64::AEK_INVALID, nullptr, nullptr) AARCH64_ARCH_EXT_NAME("none", AArch64::AEK_NONE, nullptr, nullptr) AARCH64_ARCH_EXT_NAME("crc", AArch64::AEK_CRC, "+crc", "-crc") AARCH64_ARCH_EXT_NAME("lse", AArch64::AEK_LSE, "+lse", "-lse") +AARCH64_ARCH_EXT_NAME("rdm", AArch64::AEK_RDM, "+rdm", "-rdm") AARCH64_ARCH_EXT_NAME("crypto", AArch64::AEK_CRYPTO, "+crypto","-crypto") +AARCH64_ARCH_EXT_NAME("dotprod", AArch64::AEK_DOTPROD, "+dotprod","-dotprod") AARCH64_ARCH_EXT_NAME("fp", AArch64::AEK_FP, "+fp-armv8", "-fp-armv8") AARCH64_ARCH_EXT_NAME("simd", AArch64::AEK_SIMD, "+neon", "-neon") AARCH64_ARCH_EXT_NAME("fp16", AArch64::AEK_FP16, "+fullfp16", "-fullfp16") AARCH64_ARCH_EXT_NAME("profile", AArch64::AEK_PROFILE, "+spe", "-spe") AARCH64_ARCH_EXT_NAME("ras", AArch64::AEK_RAS, "+ras", "-ras") AARCH64_ARCH_EXT_NAME("sve", AArch64::AEK_SVE, "+sve", "-sve") +AARCH64_ARCH_EXT_NAME("rcpc", AArch64::AEK_RCPC, "+rcpc", "-rcpc") #undef AARCH64_ARCH_EXT_NAME #ifndef AARCH64_CPU_NAME #define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) #endif -AARCH64_CPU_NAME("cortex-a35", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("cortex-a35", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("cortex-a53", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, true, +AARCH64_CPU_NAME("cortex-a53", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, true, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("cortex-a57", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("cortex-a55", ARMV8_2A, FK_CRYPTO_NEON_FP_ARMV8, false, + (AArch64::AEK_FP16 | AArch64::AEK_DOTPROD | AArch64::AEK_RCPC)) +AARCH64_CPU_NAME("cortex-a57", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("cortex-a72", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("cortex-a72", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("cortex-a73", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("cortex-a73", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("cyclone", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("cortex-a75", ARMV8_2A, FK_CRYPTO_NEON_FP_ARMV8, false, + (AArch64::AEK_FP16 | AArch64::AEK_DOTPROD | AArch64::AEK_RCPC)) +AARCH64_CPU_NAME("cyclone", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_NONE)) -AARCH64_CPU_NAME("exynos-m1", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, - (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("exynos-m2", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("exynos-m1", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("exynos-m3", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("exynos-m2", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("falkor", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("exynos-m3", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("kryo", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("falkor", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, + (AArch64::AEK_CRC | AArch64::AEK_RDM)) +AARCH64_CPU_NAME("saphira", ARMV8_3A, FK_CRYPTO_NEON_FP_ARMV8, false, + (AArch64::AEK_PROFILE)) +AARCH64_CPU_NAME("kryo", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) -AARCH64_CPU_NAME("thunderx2t99", AK_ARMV8_1A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("thunderx2t99", ARMV8_1A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_NONE)) -AARCH64_CPU_NAME("thunderx", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("thunderx", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC | AArch64::AEK_PROFILE)) -AARCH64_CPU_NAME("thunderxt88", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("thunderxt88", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC | AArch64::AEK_PROFILE)) -AARCH64_CPU_NAME("thunderxt81", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("thunderxt81", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC | AArch64::AEK_PROFILE)) -AARCH64_CPU_NAME("thunderxt83", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, +AARCH64_CPU_NAME("thunderxt83", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC | AArch64::AEK_PROFILE)) // Invalid CPU -AARCH64_CPU_NAME("invalid", AK_INVALID, FK_INVALID, true, AArch64::AEK_INVALID) +AARCH64_CPU_NAME("invalid", INVALID, FK_INVALID, true, AArch64::AEK_INVALID) #undef AARCH64_CPU_NAME diff --git a/include/llvm/Support/AMDGPUKernelDescriptor.h b/include/llvm/Support/AMDGPUKernelDescriptor.h new file mode 100644 index 000000000000..ce2c0c1c959e --- /dev/null +++ b/include/llvm/Support/AMDGPUKernelDescriptor.h @@ -0,0 +1,139 @@ +//===--- AMDGPUKernelDescriptor.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// \brief AMDGPU kernel descriptor definitions. For more information, visit +/// https://llvm.org/docs/AMDGPUUsage.html#kernel-descriptor-for-gfx6-gfx9 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_AMDGPUKERNELDESCRIPTOR_H +#define LLVM_SUPPORT_AMDGPUKERNELDESCRIPTOR_H + +#include <cstdint> + +// Creates enumeration entries used for packing bits into integers. Enumeration +// entries include bit shift amount, bit width, and bit mask. +#define AMDGPU_BITS_ENUM_ENTRY(name, shift, width) \ + name ## _SHIFT = (shift), \ + name ## _WIDTH = (width), \ + name = (((1 << (width)) - 1) << (shift)) \ + +// Gets bits for specified bit mask from specified source. +#define AMDGPU_BITS_GET(src, mask) \ + ((src & mask) >> mask ## _SHIFT) \ + +// Sets bits for specified bit mask in specified destination. +#define AMDGPU_BITS_SET(dst, mask, val) \ + dst &= (~(1 << mask ## _SHIFT) & ~mask); \ + dst |= (((val) << mask ## _SHIFT) & mask) \ + +namespace llvm { +namespace AMDGPU { +namespace HSAKD { + +/// \brief Floating point rounding modes. +enum : uint8_t { + AMDGPU_FLOAT_ROUND_MODE_NEAR_EVEN = 0, + AMDGPU_FLOAT_ROUND_MODE_PLUS_INFINITY = 1, + AMDGPU_FLOAT_ROUND_MODE_MINUS_INFINITY = 2, + AMDGPU_FLOAT_ROUND_MODE_ZERO = 3, +}; + +/// \brief Floating point denorm modes. +enum : uint8_t { + AMDGPU_FLOAT_DENORM_MODE_FLUSH_SRC_DST = 0, + AMDGPU_FLOAT_DENORM_MODE_FLUSH_DST = 1, + AMDGPU_FLOAT_DENORM_MODE_FLUSH_SRC = 2, + AMDGPU_FLOAT_DENORM_MODE_FLUSH_NONE = 3, +}; + +/// \brief System VGPR workitem IDs. +enum : uint8_t { + AMDGPU_SYSTEM_VGPR_WORKITEM_ID_X = 0, + AMDGPU_SYSTEM_VGPR_WORKITEM_ID_X_Y = 1, + AMDGPU_SYSTEM_VGPR_WORKITEM_ID_X_Y_Z = 2, + AMDGPU_SYSTEM_VGPR_WORKITEM_ID_UNDEFINED = 3, +}; + +/// \brief Compute program resource register one layout. +enum ComputePgmRsrc1 { + AMDGPU_BITS_ENUM_ENTRY(GRANULATED_WORKITEM_VGPR_COUNT, 0, 6), + AMDGPU_BITS_ENUM_ENTRY(GRANULATED_WAVEFRONT_SGPR_COUNT, 6, 4), + AMDGPU_BITS_ENUM_ENTRY(PRIORITY, 10, 2), + AMDGPU_BITS_ENUM_ENTRY(FLOAT_ROUND_MODE_32, 12, 2), + AMDGPU_BITS_ENUM_ENTRY(FLOAT_ROUND_MODE_16_64, 14, 2), + AMDGPU_BITS_ENUM_ENTRY(FLOAT_DENORM_MODE_32, 16, 2), + AMDGPU_BITS_ENUM_ENTRY(FLOAT_DENORM_MODE_16_64, 18, 2), + AMDGPU_BITS_ENUM_ENTRY(PRIV, 20, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_DX10_CLAMP, 21, 1), + AMDGPU_BITS_ENUM_ENTRY(DEBUG_MODE, 22, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_IEEE_MODE, 23, 1), + AMDGPU_BITS_ENUM_ENTRY(BULKY, 24, 1), + AMDGPU_BITS_ENUM_ENTRY(CDBG_USER, 25, 1), + AMDGPU_BITS_ENUM_ENTRY(FP16_OVFL, 26, 1), + AMDGPU_BITS_ENUM_ENTRY(RESERVED0, 27, 5), +}; + +/// \brief Compute program resource register two layout. +enum ComputePgmRsrc2 { + AMDGPU_BITS_ENUM_ENTRY(ENABLE_SGPR_PRIVATE_SEGMENT_WAVE_OFFSET, 0, 1), + AMDGPU_BITS_ENUM_ENTRY(USER_SGPR_COUNT, 1, 5), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_TRAP_HANDLER, 6, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_SGPR_WORKGROUP_ID_X, 7, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_SGPR_WORKGROUP_ID_Y, 8, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_SGPR_WORKGROUP_ID_Z, 9, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_SGPR_WORKGROUP_INFO, 10, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_VGPR_WORKITEM_ID, 11, 2), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_ADDRESS_WATCH, 13, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_MEMORY, 14, 1), + AMDGPU_BITS_ENUM_ENTRY(GRANULATED_LDS_SIZE, 15, 9), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_IEEE_754_FP_INVALID_OPERATION, 24, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_FP_DENORMAL_SOURCE, 25, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_IEEE_754_FP_DIVISION_BY_ZERO, 26, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_IEEE_754_FP_OVERFLOW, 27, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_IEEE_754_FP_UNDERFLOW, 28, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_IEEE_754_FP_INEXACT, 29, 1), + AMDGPU_BITS_ENUM_ENTRY(ENABLE_EXCEPTION_INT_DIVIDE_BY_ZERO, 30, 1), + AMDGPU_BITS_ENUM_ENTRY(RESERVED1, 31, 1), +}; + +/// \brief Kernel descriptor layout. This layout should be kept backwards +/// compatible as it is consumed by the command processor. +struct KernelDescriptor final { + uint32_t GroupSegmentFixedSize; + uint32_t PrivateSegmentFixedSize; + uint32_t MaxFlatWorkGroupSize; + uint64_t IsDynamicCallStack : 1; + uint64_t IsXNACKEnabled : 1; + uint64_t Reserved0 : 30; + int64_t KernelCodeEntryByteOffset; + uint64_t Reserved1[3]; + uint32_t ComputePgmRsrc1; + uint32_t ComputePgmRsrc2; + uint64_t EnableSGPRPrivateSegmentBuffer : 1; + uint64_t EnableSGPRDispatchPtr : 1; + uint64_t EnableSGPRQueuePtr : 1; + uint64_t EnableSGPRKernargSegmentPtr : 1; + uint64_t EnableSGPRDispatchID : 1; + uint64_t EnableSGPRFlatScratchInit : 1; + uint64_t EnableSGPRPrivateSegmentSize : 1; + uint64_t EnableSGPRGridWorkgroupCountX : 1; + uint64_t EnableSGPRGridWorkgroupCountY : 1; + uint64_t EnableSGPRGridWorkgroupCountZ : 1; + uint64_t Reserved2 : 54; + + KernelDescriptor() = default; +}; + +} // end namespace HSAKD +} // end namespace AMDGPU +} // end namespace llvm + +#endif // LLVM_SUPPORT_AMDGPUKERNELDESCRIPTOR_H diff --git a/include/llvm/Support/AMDGPUCodeObjectMetadata.h b/include/llvm/Support/AMDGPUMetadata.h index d274c5ee9184..00039a75c51d 100644 --- a/include/llvm/Support/AMDGPUCodeObjectMetadata.h +++ b/include/llvm/Support/AMDGPUMetadata.h @@ -1,4 +1,4 @@ -//===--- AMDGPUCodeObjectMetadata.h -----------------------------*- C++ -*-===// +//===--- AMDGPUMetadata.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -8,14 +8,13 @@ //===----------------------------------------------------------------------===// // /// \file -/// \brief AMDGPU Code Object Metadata definitions and in-memory -/// representations. +/// \brief AMDGPU metadata definitions and in-memory representations. /// // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_AMDGPUCODEOBJECTMETADATA_H -#define LLVM_SUPPORT_AMDGPUCODEOBJECTMETADATA_H +#ifndef LLVM_SUPPORT_AMDGPUMETADATA_H +#define LLVM_SUPPORT_AMDGPUMETADATA_H #include <cstdint> #include <string> @@ -26,21 +25,19 @@ namespace llvm { namespace AMDGPU { //===----------------------------------------------------------------------===// -// Code Object Metadata. +// HSA metadata. //===----------------------------------------------------------------------===// -namespace CodeObject { +namespace HSAMD { -/// \brief Code object metadata major version. -constexpr uint32_t MetadataVersionMajor = 1; -/// \brief Code object metadata minor version. -constexpr uint32_t MetadataVersionMinor = 0; +/// \brief HSA metadata major version. +constexpr uint32_t VersionMajor = 1; +/// \brief HSA metadata minor version. +constexpr uint32_t VersionMinor = 0; -/// \brief Code object metadata beginning assembler directive. -constexpr char MetadataAssemblerDirectiveBegin[] = - ".amdgpu_code_object_metadata"; -/// \brief Code object metadata ending assembler directive. -constexpr char MetadataAssemblerDirectiveEnd[] = - ".end_amdgpu_code_object_metadata"; +/// \brief HSA metadata beginning assembler directive. +constexpr char AssemblerDirectiveBegin[] = ".amd_amdgpu_hsa_metadata"; +/// \brief HSA metadata ending assembler directive. +constexpr char AssemblerDirectiveEnd[] = ".end_amd_amdgpu_hsa_metadata"; /// \brief Access qualifiers. enum class AccessQualifier : uint8_t { @@ -115,6 +112,8 @@ constexpr char ReqdWorkGroupSize[] = "ReqdWorkGroupSize"; constexpr char WorkGroupSizeHint[] = "WorkGroupSizeHint"; /// \brief Key for Kernel::Attr::Metadata::mVecTypeHint. constexpr char VecTypeHint[] = "VecTypeHint"; +/// \brief Key for Kernel::Attr::Metadata::mRuntimeHandle. +constexpr char RuntimeHandle[] = "RuntimeHandle"; } // end namespace Key /// \brief In-memory representation of kernel attributes metadata. @@ -125,20 +124,22 @@ struct Metadata final { std::vector<uint32_t> mWorkGroupSizeHint = std::vector<uint32_t>(); /// \brief 'vec_type_hint' attribute. Optional. std::string mVecTypeHint = std::string(); + /// \brief External symbol created by runtime to store the kernel address + /// for enqueued blocks. + std::string mRuntimeHandle = std::string(); /// \brief Default constructor. Metadata() = default; /// \returns True if kernel attributes metadata is empty, false otherwise. bool empty() const { - return mReqdWorkGroupSize.empty() && - mWorkGroupSizeHint.empty() && - mVecTypeHint.empty(); + return !notEmpty(); } /// \returns True if kernel attributes metadata is not empty, false otherwise. bool notEmpty() const { - return !empty(); + return !mReqdWorkGroupSize.empty() || !mWorkGroupSizeHint.empty() || + !mVecTypeHint.empty() || !mRuntimeHandle.empty(); } }; @@ -150,6 +151,10 @@ struct Metadata final { namespace Arg { namespace Key { +/// \brief Key for Kernel::Arg::Metadata::mName. +constexpr char Name[] = "Name"; +/// \brief Key for Kernel::Arg::Metadata::mTypeName. +constexpr char TypeName[] = "TypeName"; /// \brief Key for Kernel::Arg::Metadata::mSize. constexpr char Size[] = "Size"; /// \brief Key for Kernel::Arg::Metadata::mAlign. @@ -160,26 +165,28 @@ constexpr char ValueKind[] = "ValueKind"; constexpr char ValueType[] = "ValueType"; /// \brief Key for Kernel::Arg::Metadata::mPointeeAlign. constexpr char PointeeAlign[] = "PointeeAlign"; -/// \brief Key for Kernel::Arg::Metadata::mAccQual. -constexpr char AccQual[] = "AccQual"; /// \brief Key for Kernel::Arg::Metadata::mAddrSpaceQual. constexpr char AddrSpaceQual[] = "AddrSpaceQual"; +/// \brief Key for Kernel::Arg::Metadata::mAccQual. +constexpr char AccQual[] = "AccQual"; +/// \brief Key for Kernel::Arg::Metadata::mActualAccQual. +constexpr char ActualAccQual[] = "ActualAccQual"; /// \brief Key for Kernel::Arg::Metadata::mIsConst. constexpr char IsConst[] = "IsConst"; -/// \brief Key for Kernel::Arg::Metadata::mIsPipe. -constexpr char IsPipe[] = "IsPipe"; /// \brief Key for Kernel::Arg::Metadata::mIsRestrict. constexpr char IsRestrict[] = "IsRestrict"; /// \brief Key for Kernel::Arg::Metadata::mIsVolatile. constexpr char IsVolatile[] = "IsVolatile"; -/// \brief Key for Kernel::Arg::Metadata::mName. -constexpr char Name[] = "Name"; -/// \brief Key for Kernel::Arg::Metadata::mTypeName. -constexpr char TypeName[] = "TypeName"; +/// \brief Key for Kernel::Arg::Metadata::mIsPipe. +constexpr char IsPipe[] = "IsPipe"; } // end namespace Key /// \brief In-memory representation of kernel argument metadata. struct Metadata final { + /// \brief Name. Optional. + std::string mName = std::string(); + /// \brief Type name. Optional. + std::string mTypeName = std::string(); /// \brief Size in bytes. Required. uint32_t mSize = 0; /// \brief Alignment in bytes. Required. @@ -190,22 +197,20 @@ struct Metadata final { ValueType mValueType = ValueType::Unknown; /// \brief Pointee alignment in bytes. Optional. uint32_t mPointeeAlign = 0; - /// \brief Access qualifier. Optional. - AccessQualifier mAccQual = AccessQualifier::Unknown; /// \brief Address space qualifier. Optional. AddressSpaceQualifier mAddrSpaceQual = AddressSpaceQualifier::Unknown; + /// \brief Access qualifier. Optional. + AccessQualifier mAccQual = AccessQualifier::Unknown; + /// \brief Actual access qualifier. Optional. + AccessQualifier mActualAccQual = AccessQualifier::Unknown; /// \brief True if 'const' qualifier is specified. Optional. bool mIsConst = false; - /// \brief True if 'pipe' qualifier is specified. Optional. - bool mIsPipe = false; /// \brief True if 'restrict' qualifier is specified. Optional. bool mIsRestrict = false; /// \brief True if 'volatile' qualifier is specified. Optional. bool mIsVolatile = false; - /// \brief Name. Optional. - std::string mName = std::string(); - /// \brief Type name. Optional. - std::string mTypeName = std::string(); + /// \brief True if 'pipe' qualifier is specified. Optional. + bool mIsPipe = false; /// \brief Default constructor. Metadata() = default; @@ -221,51 +226,63 @@ namespace CodeProps { namespace Key { /// \brief Key for Kernel::CodeProps::Metadata::mKernargSegmentSize. constexpr char KernargSegmentSize[] = "KernargSegmentSize"; -/// \brief Key for Kernel::CodeProps::Metadata::mWorkgroupGroupSegmentSize. -constexpr char WorkgroupGroupSegmentSize[] = "WorkgroupGroupSegmentSize"; -/// \brief Key for Kernel::CodeProps::Metadata::mWorkitemPrivateSegmentSize. -constexpr char WorkitemPrivateSegmentSize[] = "WorkitemPrivateSegmentSize"; -/// \brief Key for Kernel::CodeProps::Metadata::mWavefrontNumSGPRs. -constexpr char WavefrontNumSGPRs[] = "WavefrontNumSGPRs"; -/// \brief Key for Kernel::CodeProps::Metadata::mWorkitemNumVGPRs. -constexpr char WorkitemNumVGPRs[] = "WorkitemNumVGPRs"; +/// \brief Key for Kernel::CodeProps::Metadata::mGroupSegmentFixedSize. +constexpr char GroupSegmentFixedSize[] = "GroupSegmentFixedSize"; +/// \brief Key for Kernel::CodeProps::Metadata::mPrivateSegmentFixedSize. +constexpr char PrivateSegmentFixedSize[] = "PrivateSegmentFixedSize"; /// \brief Key for Kernel::CodeProps::Metadata::mKernargSegmentAlign. constexpr char KernargSegmentAlign[] = "KernargSegmentAlign"; -/// \brief Key for Kernel::CodeProps::Metadata::mGroupSegmentAlign. -constexpr char GroupSegmentAlign[] = "GroupSegmentAlign"; -/// \brief Key for Kernel::CodeProps::Metadata::mPrivateSegmentAlign. -constexpr char PrivateSegmentAlign[] = "PrivateSegmentAlign"; /// \brief Key for Kernel::CodeProps::Metadata::mWavefrontSize. constexpr char WavefrontSize[] = "WavefrontSize"; +/// \brief Key for Kernel::CodeProps::Metadata::mNumSGPRs. +constexpr char NumSGPRs[] = "NumSGPRs"; +/// \brief Key for Kernel::CodeProps::Metadata::mNumVGPRs. +constexpr char NumVGPRs[] = "NumVGPRs"; +/// \brief Key for Kernel::CodeProps::Metadata::mMaxFlatWorkGroupSize. +constexpr char MaxFlatWorkGroupSize[] = "MaxFlatWorkGroupSize"; +/// \brief Key for Kernel::CodeProps::Metadata::mIsDynamicCallStack. +constexpr char IsDynamicCallStack[] = "IsDynamicCallStack"; +/// \brief Key for Kernel::CodeProps::Metadata::mIsXNACKEnabled. +constexpr char IsXNACKEnabled[] = "IsXNACKEnabled"; +/// \brief Key for Kernel::CodeProps::Metadata::mNumSpilledSGPRs. +constexpr char NumSpilledSGPRs[] = "NumSpilledSGPRs"; +/// \brief Key for Kernel::CodeProps::Metadata::mNumSpilledVGPRs. +constexpr char NumSpilledVGPRs[] = "NumSpilledVGPRs"; } // end namespace Key /// \brief In-memory representation of kernel code properties metadata. struct Metadata final { /// \brief Size in bytes of the kernarg segment memory. Kernarg segment memory - /// holds the values of the arguments to the kernel. Optional. + /// holds the values of the arguments to the kernel. Required. uint64_t mKernargSegmentSize = 0; /// \brief Size in bytes of the group segment memory required by a workgroup. /// This value does not include any dynamically allocated group segment memory - /// that may be added when the kernel is dispatched. Optional. - uint32_t mWorkgroupGroupSegmentSize = 0; + /// that may be added when the kernel is dispatched. Required. + uint32_t mGroupSegmentFixedSize = 0; /// \brief Size in bytes of the private segment memory required by a workitem. - /// Private segment memory includes arg, spill and private segments. Optional. - uint32_t mWorkitemPrivateSegmentSize = 0; + /// Private segment memory includes arg, spill and private segments. Required. + uint32_t mPrivateSegmentFixedSize = 0; + /// \brief Maximum byte alignment of variables used by the kernel in the + /// kernarg memory segment. Required. + uint32_t mKernargSegmentAlign = 0; + /// \brief Wavefront size. Required. + uint32_t mWavefrontSize = 0; /// \brief Total number of SGPRs used by a wavefront. Optional. - uint16_t mWavefrontNumSGPRs = 0; + uint16_t mNumSGPRs = 0; /// \brief Total number of VGPRs used by a workitem. Optional. - uint16_t mWorkitemNumVGPRs = 0; - /// \brief Maximum byte alignment of variables used by the kernel in the - /// kernarg memory segment. Expressed as a power of two. Optional. - uint8_t mKernargSegmentAlign = 0; - /// \brief Maximum byte alignment of variables used by the kernel in the - /// group memory segment. Expressed as a power of two. Optional. - uint8_t mGroupSegmentAlign = 0; - /// \brief Maximum byte alignment of variables used by the kernel in the - /// private memory segment. Expressed as a power of two. Optional. - uint8_t mPrivateSegmentAlign = 0; - /// \brief Wavefront size. Expressed as a power of two. Optional. - uint8_t mWavefrontSize = 0; + uint16_t mNumVGPRs = 0; + /// \brief Maximum flat work-group size supported by the kernel. Optional. + uint32_t mMaxFlatWorkGroupSize = 0; + /// \brief True if the generated machine code is using a dynamically sized + /// call stack. Optional. + bool mIsDynamicCallStack = false; + /// \brief True if the generated machine code is capable of supporting XNACK. + /// Optional. + bool mIsXNACKEnabled = false; + /// \brief Number of SGPRs spilled by a wavefront. Optional. + uint16_t mNumSpilledSGPRs = 0; + /// \brief Number of VGPRs spilled by a workitem. Optional. + uint16_t mNumSpilledVGPRs = 0; /// \brief Default constructor. Metadata() = default; @@ -279,10 +296,7 @@ struct Metadata final { /// \returns True if kernel code properties metadata is not empty, false /// otherwise. bool notEmpty() const { - return mKernargSegmentSize || mWorkgroupGroupSegmentSize || - mWorkitemPrivateSegmentSize || mWavefrontNumSGPRs || - mWorkitemNumVGPRs || mKernargSegmentAlign || mGroupSegmentAlign || - mPrivateSegmentAlign || mWavefrontSize; + return true; } }; @@ -348,6 +362,8 @@ struct Metadata final { namespace Key { /// \brief Key for Kernel::Metadata::mName. constexpr char Name[] = "Name"; +/// \brief Key for Kernel::Metadata::mSymbolName. +constexpr char SymbolName[] = "SymbolName"; /// \brief Key for Kernel::Metadata::mLanguage. constexpr char Language[] = "Language"; /// \brief Key for Kernel::Metadata::mLanguageVersion. @@ -364,8 +380,10 @@ constexpr char DebugProps[] = "DebugProps"; /// \brief In-memory representation of kernel metadata. struct Metadata final { - /// \brief Name. Required. + /// \brief Kernel source name. Required. std::string mName = std::string(); + /// \brief Kernel descriptor name. Required. + std::string mSymbolName = std::string(); /// \brief Language. Optional. std::string mLanguage = std::string(); /// \brief Language version. Optional. @@ -386,37 +404,78 @@ struct Metadata final { } // end namespace Kernel namespace Key { -/// \brief Key for CodeObject::Metadata::mVersion. +/// \brief Key for HSA::Metadata::mVersion. constexpr char Version[] = "Version"; -/// \brief Key for CodeObject::Metadata::mPrintf. +/// \brief Key for HSA::Metadata::mPrintf. constexpr char Printf[] = "Printf"; -/// \brief Key for CodeObject::Metadata::mKernels. +/// \brief Key for HSA::Metadata::mKernels. constexpr char Kernels[] = "Kernels"; } // end namespace Key -/// \brief In-memory representation of code object metadata. +/// \brief In-memory representation of HSA metadata. struct Metadata final { - /// \brief Code object metadata version. Required. + /// \brief HSA metadata version. Required. std::vector<uint32_t> mVersion = std::vector<uint32_t>(); /// \brief Printf metadata. Optional. std::vector<std::string> mPrintf = std::vector<std::string>(); - /// \brief Kernels metadata. Optional. + /// \brief Kernels metadata. Required. std::vector<Kernel::Metadata> mKernels = std::vector<Kernel::Metadata>(); /// \brief Default constructor. Metadata() = default; +}; + +/// \brief Converts \p String to \p HSAMetadata. +std::error_code fromString(std::string String, Metadata &HSAMetadata); - /// \brief Converts \p YamlString to \p CodeObjectMetadata. - static std::error_code fromYamlString(std::string YamlString, - Metadata &CodeObjectMetadata); +/// \brief Converts \p HSAMetadata to \p String. +std::error_code toString(Metadata HSAMetadata, std::string &String); - /// \brief Converts \p CodeObjectMetadata to \p YamlString. - static std::error_code toYamlString(Metadata CodeObjectMetadata, - std::string &YamlString); +} // end namespace HSAMD + +//===----------------------------------------------------------------------===// +// PAL metadata. +//===----------------------------------------------------------------------===// +namespace PALMD { + +/// \brief PAL metadata assembler directive. +constexpr char AssemblerDirective[] = ".amd_amdgpu_pal_metadata"; + +/// \brief PAL metadata keys. +enum Key : uint32_t { + LS_NUM_USED_VGPRS = 0x10000021, + HS_NUM_USED_VGPRS = 0x10000022, + ES_NUM_USED_VGPRS = 0x10000023, + GS_NUM_USED_VGPRS = 0x10000024, + VS_NUM_USED_VGPRS = 0x10000025, + PS_NUM_USED_VGPRS = 0x10000026, + CS_NUM_USED_VGPRS = 0x10000027, + + LS_NUM_USED_SGPRS = 0x10000028, + HS_NUM_USED_SGPRS = 0x10000029, + ES_NUM_USED_SGPRS = 0x1000002a, + GS_NUM_USED_SGPRS = 0x1000002b, + VS_NUM_USED_SGPRS = 0x1000002c, + PS_NUM_USED_SGPRS = 0x1000002d, + CS_NUM_USED_SGPRS = 0x1000002e, + + LS_SCRATCH_SIZE = 0x10000044, + HS_SCRATCH_SIZE = 0x10000045, + ES_SCRATCH_SIZE = 0x10000046, + GS_SCRATCH_SIZE = 0x10000047, + VS_SCRATCH_SIZE = 0x10000048, + PS_SCRATCH_SIZE = 0x10000049, + CS_SCRATCH_SIZE = 0x1000004a }; -} // end namespace CodeObject +/// \brief PAL metadata represented as a vector. +typedef std::vector<uint32_t> Metadata; + +/// \brief Converts \p PALMetadata to \p String. +std::error_code toString(const Metadata &PALMetadata, std::string &String); + +} // end namespace PALMD } // end namespace AMDGPU } // end namespace llvm -#endif // LLVM_SUPPORT_AMDGPUCODEOBJECTMETADATA_H +#endif // LLVM_SUPPORT_AMDGPUMETADATA_H diff --git a/include/llvm/Support/ARMTargetParser.def b/include/llvm/Support/ARMTargetParser.def index 65cb2715a6a5..6c8eff1a8f84 100644 --- a/include/llvm/Support/ARMTargetParser.def +++ b/include/llvm/Support/ARMTargetParser.def @@ -16,105 +16,109 @@ #ifndef ARM_FPU #define ARM_FPU(NAME, KIND, VERSION, NEON_SUPPORT, RESTRICTION) #endif -ARM_FPU("invalid", FK_INVALID, FV_NONE, NS_None, FR_None) -ARM_FPU("none", FK_NONE, FV_NONE, NS_None, FR_None) -ARM_FPU("vfp", FK_VFP, FV_VFPV2, NS_None, FR_None) -ARM_FPU("vfpv2", FK_VFPV2, FV_VFPV2, NS_None, FR_None) -ARM_FPU("vfpv3", FK_VFPV3, FV_VFPV3, NS_None, FR_None) -ARM_FPU("vfpv3-fp16", FK_VFPV3_FP16, FV_VFPV3_FP16, NS_None, FR_None) -ARM_FPU("vfpv3-d16", FK_VFPV3_D16, FV_VFPV3, NS_None, FR_D16) -ARM_FPU("vfpv3-d16-fp16", FK_VFPV3_D16_FP16, FV_VFPV3_FP16, NS_None, FR_D16) -ARM_FPU("vfpv3xd", FK_VFPV3XD, FV_VFPV3, NS_None, FR_SP_D16) -ARM_FPU("vfpv3xd-fp16", FK_VFPV3XD_FP16, FV_VFPV3_FP16, NS_None, FR_SP_D16) -ARM_FPU("vfpv4", FK_VFPV4, FV_VFPV4, NS_None, FR_None) -ARM_FPU("vfpv4-d16", FK_VFPV4_D16, FV_VFPV4, NS_None, FR_D16) -ARM_FPU("fpv4-sp-d16", FK_FPV4_SP_D16, FV_VFPV4, NS_None, FR_SP_D16) -ARM_FPU("fpv5-d16", FK_FPV5_D16, FV_VFPV5, NS_None, FR_D16) -ARM_FPU("fpv5-sp-d16", FK_FPV5_SP_D16, FV_VFPV5, NS_None, FR_SP_D16) -ARM_FPU("fp-armv8", FK_FP_ARMV8, FV_VFPV5, NS_None, FR_None) -ARM_FPU("neon", FK_NEON, FV_VFPV3, NS_Neon, FR_None) -ARM_FPU("neon-fp16", FK_NEON_FP16, FV_VFPV3_FP16, NS_Neon, FR_None) -ARM_FPU("neon-vfpv4", FK_NEON_VFPV4, FV_VFPV4, NS_Neon, FR_None) -ARM_FPU("neon-fp-armv8", FK_NEON_FP_ARMV8, FV_VFPV5, NS_Neon, FR_None) -ARM_FPU("crypto-neon-fp-armv8", FK_CRYPTO_NEON_FP_ARMV8, FV_VFPV5, NS_Crypto, - FR_None) -ARM_FPU("softvfp", FK_SOFTVFP, FV_NONE, NS_None, FR_None) +ARM_FPU("invalid", FK_INVALID, FPUVersion::NONE, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("none", FK_NONE, FPUVersion::NONE, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("vfp", FK_VFP, FPUVersion::VFPV2, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("vfpv2", FK_VFPV2, FPUVersion::VFPV2, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("vfpv3", FK_VFPV3, FPUVersion::VFPV3, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("vfpv3-fp16", FK_VFPV3_FP16, FPUVersion::VFPV3_FP16, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("vfpv3-d16", FK_VFPV3_D16, FPUVersion::VFPV3, NeonSupportLevel::None, FPURestriction::D16) +ARM_FPU("vfpv3-d16-fp16", FK_VFPV3_D16_FP16, FPUVersion::VFPV3_FP16, NeonSupportLevel::None, FPURestriction::D16) +ARM_FPU("vfpv3xd", FK_VFPV3XD, FPUVersion::VFPV3, NeonSupportLevel::None, FPURestriction::SP_D16) +ARM_FPU("vfpv3xd-fp16", FK_VFPV3XD_FP16, FPUVersion::VFPV3_FP16, NeonSupportLevel::None, FPURestriction::SP_D16) +ARM_FPU("vfpv4", FK_VFPV4, FPUVersion::VFPV4, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("vfpv4-d16", FK_VFPV4_D16, FPUVersion::VFPV4, NeonSupportLevel::None, FPURestriction::D16) +ARM_FPU("fpv4-sp-d16", FK_FPV4_SP_D16, FPUVersion::VFPV4, NeonSupportLevel::None, FPURestriction::SP_D16) +ARM_FPU("fpv5-d16", FK_FPV5_D16, FPUVersion::VFPV5, NeonSupportLevel::None, FPURestriction::D16) +ARM_FPU("fpv5-sp-d16", FK_FPV5_SP_D16, FPUVersion::VFPV5, NeonSupportLevel::None, FPURestriction::SP_D16) +ARM_FPU("fp-armv8", FK_FP_ARMV8, FPUVersion::VFPV5, NeonSupportLevel::None, FPURestriction::None) +ARM_FPU("neon", FK_NEON, FPUVersion::VFPV3, NeonSupportLevel::Neon, FPURestriction::None) +ARM_FPU("neon-fp16", FK_NEON_FP16, FPUVersion::VFPV3_FP16, NeonSupportLevel::Neon, FPURestriction::None) +ARM_FPU("neon-vfpv4", FK_NEON_VFPV4, FPUVersion::VFPV4, NeonSupportLevel::Neon, FPURestriction::None) +ARM_FPU("neon-fp-armv8", FK_NEON_FP_ARMV8, FPUVersion::VFPV5, NeonSupportLevel::Neon, FPURestriction::None) +ARM_FPU("crypto-neon-fp-armv8", FK_CRYPTO_NEON_FP_ARMV8, FPUVersion::VFPV5, NeonSupportLevel::Crypto, + FPURestriction::None) +ARM_FPU("softvfp", FK_SOFTVFP, FPUVersion::NONE, NeonSupportLevel::None, FPURestriction::None) #undef ARM_FPU #ifndef ARM_ARCH #define ARM_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, ARCH_BASE_EXT) #endif -ARM_ARCH("invalid", AK_INVALID, nullptr, nullptr, +ARM_ARCH("invalid", INVALID, "", "", ARMBuildAttrs::CPUArch::Pre_v4, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv2", AK_ARMV2, "2", "v2", ARMBuildAttrs::CPUArch::Pre_v4, +ARM_ARCH("armv2", ARMV2, "2", "v2", ARMBuildAttrs::CPUArch::Pre_v4, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv2a", AK_ARMV2A, "2A", "v2a", ARMBuildAttrs::CPUArch::Pre_v4, +ARM_ARCH("armv2a", ARMV2A, "2A", "v2a", ARMBuildAttrs::CPUArch::Pre_v4, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv3", AK_ARMV3, "3", "v3", ARMBuildAttrs::CPUArch::Pre_v4, +ARM_ARCH("armv3", ARMV3, "3", "v3", ARMBuildAttrs::CPUArch::Pre_v4, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv3m", AK_ARMV3M, "3M", "v3m", ARMBuildAttrs::CPUArch::Pre_v4, +ARM_ARCH("armv3m", ARMV3M, "3M", "v3m", ARMBuildAttrs::CPUArch::Pre_v4, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv4", AK_ARMV4, "4", "v4", ARMBuildAttrs::CPUArch::v4, +ARM_ARCH("armv4", ARMV4, "4", "v4", ARMBuildAttrs::CPUArch::v4, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv4t", AK_ARMV4T, "4T", "v4t", ARMBuildAttrs::CPUArch::v4T, +ARM_ARCH("armv4t", ARMV4T, "4T", "v4t", ARMBuildAttrs::CPUArch::v4T, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv5t", AK_ARMV5T, "5T", "v5", ARMBuildAttrs::CPUArch::v5T, +ARM_ARCH("armv5t", ARMV5T, "5T", "v5", ARMBuildAttrs::CPUArch::v5T, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv5te", AK_ARMV5TE, "5TE", "v5e", ARMBuildAttrs::CPUArch::v5TE, +ARM_ARCH("armv5te", ARMV5TE, "5TE", "v5e", ARMBuildAttrs::CPUArch::v5TE, FK_NONE, ARM::AEK_DSP) -ARM_ARCH("armv5tej", AK_ARMV5TEJ, "5TEJ", "v5e", ARMBuildAttrs::CPUArch::v5TEJ, +ARM_ARCH("armv5tej", ARMV5TEJ, "5TEJ", "v5e", ARMBuildAttrs::CPUArch::v5TEJ, FK_NONE, ARM::AEK_DSP) -ARM_ARCH("armv6", AK_ARMV6, "6", "v6", ARMBuildAttrs::CPUArch::v6, +ARM_ARCH("armv6", ARMV6, "6", "v6", ARMBuildAttrs::CPUArch::v6, FK_VFPV2, ARM::AEK_DSP) -ARM_ARCH("armv6k", AK_ARMV6K, "6K", "v6k", ARMBuildAttrs::CPUArch::v6K, +ARM_ARCH("armv6k", ARMV6K, "6K", "v6k", ARMBuildAttrs::CPUArch::v6K, FK_VFPV2, ARM::AEK_DSP) -ARM_ARCH("armv6t2", AK_ARMV6T2, "6T2", "v6t2", ARMBuildAttrs::CPUArch::v6T2, +ARM_ARCH("armv6t2", ARMV6T2, "6T2", "v6t2", ARMBuildAttrs::CPUArch::v6T2, FK_NONE, ARM::AEK_DSP) -ARM_ARCH("armv6kz", AK_ARMV6KZ, "6KZ", "v6kz", ARMBuildAttrs::CPUArch::v6KZ, +ARM_ARCH("armv6kz", ARMV6KZ, "6KZ", "v6kz", ARMBuildAttrs::CPUArch::v6KZ, FK_VFPV2, (ARM::AEK_SEC | ARM::AEK_DSP)) -ARM_ARCH("armv6-m", AK_ARMV6M, "6-M", "v6m", ARMBuildAttrs::CPUArch::v6_M, +ARM_ARCH("armv6-m", ARMV6M, "6-M", "v6m", ARMBuildAttrs::CPUArch::v6_M, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv7-a", AK_ARMV7A, "7-A", "v7", ARMBuildAttrs::CPUArch::v7, +ARM_ARCH("armv7-a", ARMV7A, "7-A", "v7", ARMBuildAttrs::CPUArch::v7, FK_NEON, ARM::AEK_DSP) -ARM_ARCH("armv7ve", AK_ARMV7VE, "7VE", "v7ve", ARMBuildAttrs::CPUArch::v7, +ARM_ARCH("armv7ve", ARMV7VE, "7VE", "v7ve", ARMBuildAttrs::CPUArch::v7, FK_NEON, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP)) -ARM_ARCH("armv7-r", AK_ARMV7R, "7-R", "v7r", ARMBuildAttrs::CPUArch::v7, +ARM_ARCH("armv7-r", ARMV7R, "7-R", "v7r", ARMBuildAttrs::CPUArch::v7, FK_NONE, (ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP)) -ARM_ARCH("armv7-m", AK_ARMV7M, "7-M", "v7m", ARMBuildAttrs::CPUArch::v7, +ARM_ARCH("armv7-m", ARMV7M, "7-M", "v7m", ARMBuildAttrs::CPUArch::v7, FK_NONE, ARM::AEK_HWDIVTHUMB) -ARM_ARCH("armv7e-m", AK_ARMV7EM, "7E-M", "v7em", ARMBuildAttrs::CPUArch::v7E_M, +ARM_ARCH("armv7e-m", ARMV7EM, "7E-M", "v7em", ARMBuildAttrs::CPUArch::v7E_M, FK_NONE, (ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP)) -ARM_ARCH("armv8-a", AK_ARMV8A, "8-A", "v8", ARMBuildAttrs::CPUArch::v8_A, +ARM_ARCH("armv8-a", ARMV8A, "8-A", "v8", ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_CRC)) -ARM_ARCH("armv8.1-a", AK_ARMV8_1A, "8.1-A", "v8.1a", +ARM_ARCH("armv8.1-a", ARMV8_1A, "8.1-A", "v8.1a", ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_CRC)) -ARM_ARCH("armv8.2-a", AK_ARMV8_2A, "8.2-A", "v8.2a", +ARM_ARCH("armv8.2-a", ARMV8_2A, "8.2-A", "v8.2a", ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_CRC | ARM::AEK_RAS)) -ARM_ARCH("armv8-r", AK_ARMV8R, "8-R", "v8r", ARMBuildAttrs::CPUArch::v8_R, +ARM_ARCH("armv8.3-a", ARMV8_3A, "8.3-A", "v8.3a", + ARMBuildAttrs::CPUArch::v8_A, FK_CRYPTO_NEON_FP_ARMV8, + (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | + ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_CRC | ARM::AEK_RAS)) +ARM_ARCH("armv8-r", ARMV8R, "8-R", "v8r", ARMBuildAttrs::CPUArch::v8_R, FK_NEON_FP_ARMV8, (ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB | ARM::AEK_DSP | ARM::AEK_CRC)) -ARM_ARCH("armv8-m.base", AK_ARMV8MBaseline, "8-M.Baseline", "v8m.base", +ARM_ARCH("armv8-m.base", ARMV8MBaseline, "8-M.Baseline", "v8m.base", ARMBuildAttrs::CPUArch::v8_M_Base, FK_NONE, ARM::AEK_HWDIVTHUMB) -ARM_ARCH("armv8-m.main", AK_ARMV8MMainline, "8-M.Mainline", "v8m.main", +ARM_ARCH("armv8-m.main", ARMV8MMainline, "8-M.Mainline", "v8m.main", ARMBuildAttrs::CPUArch::v8_M_Main, FK_FPV5_D16, ARM::AEK_HWDIVTHUMB) // Non-standard Arch names. -ARM_ARCH("iwmmxt", AK_IWMMXT, "iwmmxt", "", ARMBuildAttrs::CPUArch::v5TE, +ARM_ARCH("iwmmxt", IWMMXT, "iwmmxt", "", ARMBuildAttrs::CPUArch::v5TE, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("iwmmxt2", AK_IWMMXT2, "iwmmxt2", "", ARMBuildAttrs::CPUArch::v5TE, +ARM_ARCH("iwmmxt2", IWMMXT2, "iwmmxt2", "", ARMBuildAttrs::CPUArch::v5TE, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("xscale", AK_XSCALE, "xscale", "v5e", ARMBuildAttrs::CPUArch::v5TE, +ARM_ARCH("xscale", XSCALE, "xscale", "v5e", ARMBuildAttrs::CPUArch::v5TE, FK_NONE, ARM::AEK_NONE) -ARM_ARCH("armv7s", AK_ARMV7S, "7-S", "v7s", ARMBuildAttrs::CPUArch::v7, +ARM_ARCH("armv7s", ARMV7S, "7-S", "v7s", ARMBuildAttrs::CPUArch::v7, FK_NEON_VFPV4, ARM::AEK_DSP) -ARM_ARCH("armv7k", AK_ARMV7K, "7-K", "v7k", ARMBuildAttrs::CPUArch::v7, +ARM_ARCH("armv7k", ARMV7K, "7-K", "v7k", ARMBuildAttrs::CPUArch::v7, FK_NONE, ARM::AEK_DSP) #undef ARM_ARCH @@ -126,6 +130,7 @@ ARM_ARCH_EXT_NAME("invalid", ARM::AEK_INVALID, nullptr, nullptr) ARM_ARCH_EXT_NAME("none", ARM::AEK_NONE, nullptr, nullptr) ARM_ARCH_EXT_NAME("crc", ARM::AEK_CRC, "+crc", "-crc") ARM_ARCH_EXT_NAME("crypto", ARM::AEK_CRYPTO, "+crypto","-crypto") +ARM_ARCH_EXT_NAME("dotprod", ARM::AEK_DOTPROD, "+dotprod","-dotprod") ARM_ARCH_EXT_NAME("dsp", ARM::AEK_DSP, "+dsp", "-dsp") ARM_ARCH_EXT_NAME("fp", ARM::AEK_FP, nullptr, nullptr) ARM_ARCH_EXT_NAME("idiv", (ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB), nullptr, nullptr) @@ -155,101 +160,105 @@ ARM_HW_DIV_NAME("arm,thumb", (ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) #ifndef ARM_CPU_NAME #define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) #endif -ARM_CPU_NAME("arm2", AK_ARMV2, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm3", AK_ARMV2A, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm6", AK_ARMV3, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm7m", AK_ARMV3M, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm8", AK_ARMV4, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm810", AK_ARMV4, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("strongarm", AK_ARMV4, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("strongarm110", AK_ARMV4, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("strongarm1100", AK_ARMV4, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("strongarm1110", AK_ARMV4, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm7tdmi", AK_ARMV4T, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm7tdmi-s", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm710t", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm720t", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm9", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm9tdmi", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm920", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm920t", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm922t", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm9312", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm940t", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("ep9312", AK_ARMV4T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm10tdmi", AK_ARMV5T, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm1020t", AK_ARMV5T, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm9e", AK_ARMV5TE, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm946e-s", AK_ARMV5TE, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm966e-s", AK_ARMV5TE, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm968e-s", AK_ARMV5TE, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm10e", AK_ARMV5TE, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm1020e", AK_ARMV5TE, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm1022e", AK_ARMV5TE, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm926ej-s", AK_ARMV5TEJ, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm1136j-s", AK_ARMV6, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm1136jf-s", AK_ARMV6, FK_VFPV2, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm1136jz-s", AK_ARMV6, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm1176j-s", AK_ARMV6K, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm1176jz-s", AK_ARMV6KZ, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("mpcore", AK_ARMV6K, FK_VFPV2, false, ARM::AEK_NONE) -ARM_CPU_NAME("mpcorenovfp", AK_ARMV6K, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("arm1176jzf-s", AK_ARMV6KZ, FK_VFPV2, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm1156t2-s", AK_ARMV6T2, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("arm1156t2f-s", AK_ARMV6T2, FK_VFPV2, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m0", AK_ARMV6M, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m0plus", AK_ARMV6M, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m1", AK_ARMV6M, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("sc000", AK_ARMV6M, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-a5", AK_ARMV7A, FK_NEON_VFPV4, false, +ARM_CPU_NAME("arm2", ARMV2, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm3", ARMV2A, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm6", ARMV3, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm7m", ARMV3M, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm8", ARMV4, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm810", ARMV4, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("strongarm", ARMV4, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("strongarm110", ARMV4, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("strongarm1100", ARMV4, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("strongarm1110", ARMV4, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm7tdmi", ARMV4T, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm7tdmi-s", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm710t", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm720t", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm9", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm9tdmi", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm920", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm920t", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm922t", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm9312", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm940t", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("ep9312", ARMV4T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm10tdmi", ARMV5T, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm1020t", ARMV5T, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm9e", ARMV5TE, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm946e-s", ARMV5TE, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm966e-s", ARMV5TE, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm968e-s", ARMV5TE, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm10e", ARMV5TE, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm1020e", ARMV5TE, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm1022e", ARMV5TE, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm926ej-s", ARMV5TEJ, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm1136j-s", ARMV6, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm1136jf-s", ARMV6, FK_VFPV2, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm1136jz-s", ARMV6, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm1176j-s", ARMV6K, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm1176jz-s", ARMV6KZ, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("mpcore", ARMV6K, FK_VFPV2, false, ARM::AEK_NONE) +ARM_CPU_NAME("mpcorenovfp", ARMV6K, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("arm1176jzf-s", ARMV6KZ, FK_VFPV2, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm1156t2-s", ARMV6T2, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("arm1156t2f-s", ARMV6T2, FK_VFPV2, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m0", ARMV6M, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m0plus", ARMV6M, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m1", ARMV6M, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("sc000", ARMV6M, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-a5", ARMV7A, FK_NEON_VFPV4, false, (ARM::AEK_SEC | ARM::AEK_MP)) -ARM_CPU_NAME("cortex-a7", AK_ARMV7A, FK_NEON_VFPV4, false, +ARM_CPU_NAME("cortex-a7", ARMV7A, FK_NEON_VFPV4, false, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) -ARM_CPU_NAME("cortex-a8", AK_ARMV7A, FK_NEON, false, ARM::AEK_SEC) -ARM_CPU_NAME("cortex-a9", AK_ARMV7A, FK_NEON_FP16, false, (ARM::AEK_SEC | ARM::AEK_MP)) -ARM_CPU_NAME("cortex-a12", AK_ARMV7A, FK_NEON_VFPV4, false, +ARM_CPU_NAME("cortex-a8", ARMV7A, FK_NEON, false, ARM::AEK_SEC) +ARM_CPU_NAME("cortex-a9", ARMV7A, FK_NEON_FP16, false, (ARM::AEK_SEC | ARM::AEK_MP)) +ARM_CPU_NAME("cortex-a12", ARMV7A, FK_NEON_VFPV4, false, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) -ARM_CPU_NAME("cortex-a15", AK_ARMV7A, FK_NEON_VFPV4, false, +ARM_CPU_NAME("cortex-a15", ARMV7A, FK_NEON_VFPV4, false, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) -ARM_CPU_NAME("cortex-a17", AK_ARMV7A, FK_NEON_VFPV4, false, +ARM_CPU_NAME("cortex-a17", ARMV7A, FK_NEON_VFPV4, false, (ARM::AEK_SEC | ARM::AEK_MP | ARM::AEK_VIRT | ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) -ARM_CPU_NAME("krait", AK_ARMV7A, FK_NEON_VFPV4, false, +ARM_CPU_NAME("krait", ARMV7A, FK_NEON_VFPV4, false, (ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) -ARM_CPU_NAME("cortex-r4", AK_ARMV7R, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-r4f", AK_ARMV7R, FK_VFPV3_D16, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-r5", AK_ARMV7R, FK_VFPV3_D16, false, +ARM_CPU_NAME("cortex-r4", ARMV7R, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-r4f", ARMV7R, FK_VFPV3_D16, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-r5", ARMV7R, FK_VFPV3_D16, false, (ARM::AEK_MP | ARM::AEK_HWDIVARM)) -ARM_CPU_NAME("cortex-r7", AK_ARMV7R, FK_VFPV3_D16_FP16, false, +ARM_CPU_NAME("cortex-r7", ARMV7R, FK_VFPV3_D16_FP16, false, (ARM::AEK_MP | ARM::AEK_HWDIVARM)) -ARM_CPU_NAME("cortex-r8", AK_ARMV7R, FK_VFPV3_D16_FP16, false, +ARM_CPU_NAME("cortex-r8", ARMV7R, FK_VFPV3_D16_FP16, false, (ARM::AEK_MP | ARM::AEK_HWDIVARM)) -ARM_CPU_NAME("cortex-r52", AK_ARMV8R, FK_NEON_FP_ARMV8, true, ARM::AEK_NONE) -ARM_CPU_NAME("sc300", AK_ARMV7M, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m3", AK_ARMV7M, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m4", AK_ARMV7EM, FK_FPV4_SP_D16, true, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m7", AK_ARMV7EM, FK_FPV5_D16, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m23", AK_ARMV8MBaseline, FK_NONE, false, ARM::AEK_NONE) -ARM_CPU_NAME("cortex-m33", AK_ARMV8MMainline, FK_FPV5_SP_D16, false, ARM::AEK_DSP) -ARM_CPU_NAME("cortex-a32", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("cortex-a35", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("cortex-a53", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("cortex-a57", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("cortex-a72", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("cortex-a73", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("cyclone", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("exynos-m1", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("exynos-m2", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("exynos-m3", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) -ARM_CPU_NAME("kryo", AK_ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-r52", ARMV8R, FK_NEON_FP_ARMV8, true, ARM::AEK_NONE) +ARM_CPU_NAME("sc300", ARMV7M, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m3", ARMV7M, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m4", ARMV7EM, FK_FPV4_SP_D16, true, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m7", ARMV7EM, FK_FPV5_D16, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m23", ARMV8MBaseline, FK_NONE, false, ARM::AEK_NONE) +ARM_CPU_NAME("cortex-m33", ARMV8MMainline, FK_FPV5_SP_D16, false, ARM::AEK_DSP) +ARM_CPU_NAME("cortex-a32", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-a35", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-a53", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-a55", ARMV8_2A, FK_CRYPTO_NEON_FP_ARMV8, false, + (ARM::AEK_FP16 | ARM::AEK_DOTPROD)) +ARM_CPU_NAME("cortex-a57", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-a72", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-a73", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("cortex-a75", ARMV8_2A, FK_CRYPTO_NEON_FP_ARMV8, false, + (ARM::AEK_FP16 | ARM::AEK_DOTPROD)) +ARM_CPU_NAME("cyclone", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("exynos-m1", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("exynos-m2", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("exynos-m3", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) +ARM_CPU_NAME("kryo", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, ARM::AEK_CRC) // Non-standard Arch names. -ARM_CPU_NAME("iwmmxt", AK_IWMMXT, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("xscale", AK_XSCALE, FK_NONE, true, ARM::AEK_NONE) -ARM_CPU_NAME("swift", AK_ARMV7S, FK_NEON_VFPV4, true, +ARM_CPU_NAME("iwmmxt", IWMMXT, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("xscale", XSCALE, FK_NONE, true, ARM::AEK_NONE) +ARM_CPU_NAME("swift", ARMV7S, FK_NEON_VFPV4, true, (ARM::AEK_HWDIVARM | ARM::AEK_HWDIVTHUMB)) // Invalid CPU -ARM_CPU_NAME("invalid", AK_INVALID, FK_INVALID, true, ARM::AEK_INVALID) +ARM_CPU_NAME("invalid", INVALID, FK_INVALID, true, ARM::AEK_INVALID) #undef ARM_CPU_NAME diff --git a/include/llvm/Support/Allocator.h b/include/llvm/Support/Allocator.h index a5e662f4c588..a94aa8fb1f2a 100644 --- a/include/llvm/Support/Allocator.h +++ b/include/llvm/Support/Allocator.h @@ -269,6 +269,9 @@ public: // Pull in base class overloads. using AllocatorBase<BumpPtrAllocatorImpl>::Allocate; + // Bump pointer allocators are expected to never free their storage; and + // clients expect pointers to remain valid for non-dereferencing uses even + // after deallocation. void Deallocate(const void *Ptr, size_t Size) { __asan_poison_memory_region(Ptr, Size); } diff --git a/include/llvm/Support/AtomicOrdering.h b/include/llvm/Support/AtomicOrdering.h index 001804248b85..e93b755aa63b 100644 --- a/include/llvm/Support/AtomicOrdering.h +++ b/include/llvm/Support/AtomicOrdering.h @@ -42,7 +42,7 @@ bool operator>=(AtomicOrderingCABI, AtomicOrderingCABI) = delete; // Validate an integral value which isn't known to fit within the enum's range // is a valid AtomicOrderingCABI. -template <typename Int> static inline bool isValidAtomicOrderingCABI(Int I) { +template <typename Int> inline bool isValidAtomicOrderingCABI(Int I) { return (Int)AtomicOrderingCABI::relaxed <= I && I <= (Int)AtomicOrderingCABI::seq_cst; } @@ -72,13 +72,13 @@ bool operator>=(AtomicOrdering, AtomicOrdering) = delete; // Validate an integral value which isn't known to fit within the enum's range // is a valid AtomicOrdering. -template <typename Int> static inline bool isValidAtomicOrdering(Int I) { +template <typename Int> inline bool isValidAtomicOrdering(Int I) { return static_cast<Int>(AtomicOrdering::NotAtomic) <= I && I <= static_cast<Int>(AtomicOrdering::SequentiallyConsistent); } /// String used by LLVM IR to represent atomic ordering. -static inline const char *toIRString(AtomicOrdering ao) { +inline const char *toIRString(AtomicOrdering ao) { static const char *names[8] = {"not_atomic", "unordered", "monotonic", "consume", "acquire", "release", "acq_rel", "seq_cst"}; @@ -87,7 +87,7 @@ static inline const char *toIRString(AtomicOrdering ao) { /// Returns true if ao is stronger than other as defined by the AtomicOrdering /// lattice, which is based on C++'s definition. -static inline bool isStrongerThan(AtomicOrdering ao, AtomicOrdering other) { +inline bool isStrongerThan(AtomicOrdering ao, AtomicOrdering other) { static const bool lookup[8][8] = { // NA UN RX CO AC RE AR SC /* NotAtomic */ {false, false, false, false, false, false, false, false}, @@ -102,8 +102,7 @@ static inline bool isStrongerThan(AtomicOrdering ao, AtomicOrdering other) { return lookup[static_cast<size_t>(ao)][static_cast<size_t>(other)]; } -static inline bool isAtLeastOrStrongerThan(AtomicOrdering ao, - AtomicOrdering other) { +inline bool isAtLeastOrStrongerThan(AtomicOrdering ao, AtomicOrdering other) { static const bool lookup[8][8] = { // NA UN RX CO AC RE AR SC /* NotAtomic */ { true, false, false, false, false, false, false, false}, @@ -118,23 +117,23 @@ static inline bool isAtLeastOrStrongerThan(AtomicOrdering ao, return lookup[static_cast<size_t>(ao)][static_cast<size_t>(other)]; } -static inline bool isStrongerThanUnordered(AtomicOrdering ao) { +inline bool isStrongerThanUnordered(AtomicOrdering ao) { return isStrongerThan(ao, AtomicOrdering::Unordered); } -static inline bool isStrongerThanMonotonic(AtomicOrdering ao) { +inline bool isStrongerThanMonotonic(AtomicOrdering ao) { return isStrongerThan(ao, AtomicOrdering::Monotonic); } -static inline bool isAcquireOrStronger(AtomicOrdering ao) { +inline bool isAcquireOrStronger(AtomicOrdering ao) { return isAtLeastOrStrongerThan(ao, AtomicOrdering::Acquire); } -static inline bool isReleaseOrStronger(AtomicOrdering ao) { +inline bool isReleaseOrStronger(AtomicOrdering ao) { return isAtLeastOrStrongerThan(ao, AtomicOrdering::Release); } -static inline AtomicOrderingCABI toCABI(AtomicOrdering ao) { +inline AtomicOrderingCABI toCABI(AtomicOrdering ao) { static const AtomicOrderingCABI lookup[8] = { /* NotAtomic */ AtomicOrderingCABI::relaxed, /* Unordered */ AtomicOrderingCABI::relaxed, diff --git a/include/llvm/Support/BinaryByteStream.h b/include/llvm/Support/BinaryByteStream.h index 694be28e07e1..db1ccba1398b 100644 --- a/include/llvm/Support/BinaryByteStream.h +++ b/include/llvm/Support/BinaryByteStream.h @@ -41,7 +41,7 @@ public: Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef<uint8_t> &Buffer) override { - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; Buffer = Data.slice(Offset, Size); return Error::success(); @@ -49,7 +49,7 @@ public: Error readLongestContiguousChunk(uint32_t Offset, ArrayRef<uint8_t> &Buffer) override { - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; Buffer = Data.slice(Offset); return Error::success(); @@ -114,7 +114,7 @@ public: if (Buffer.empty()) return Error::success(); - if (auto EC = checkOffset(Offset, Buffer.size())) + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) return EC; uint8_t *DataPtr = const_cast<uint8_t *>(Data.data()); @@ -131,6 +131,76 @@ private: BinaryByteStream ImmutableStream; }; +/// \brief An implementation of WritableBinaryStream which can write at its end +/// causing the underlying data to grow. This class owns the underlying data. +class AppendingBinaryByteStream : public WritableBinaryStream { + std::vector<uint8_t> Data; + llvm::support::endianness Endian = llvm::support::little; + +public: + AppendingBinaryByteStream() = default; + AppendingBinaryByteStream(llvm::support::endianness Endian) + : Endian(Endian) {} + + void clear() { Data.clear(); } + + llvm::support::endianness getEndian() const override { return Endian; } + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) + return EC; + + Buffer = makeArrayRef(Data).slice(Offset, Size); + return Error::success(); + } + + void insert(uint32_t Offset, ArrayRef<uint8_t> Bytes) { + Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end()); + } + + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffsetForWrite(Offset, 1)) + return EC; + + Buffer = makeArrayRef(Data).slice(Offset); + return Error::success(); + } + + uint32_t getLength() override { return Data.size(); } + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) override { + if (Buffer.empty()) + return Error::success(); + + // This is well-defined for any case except where offset is strictly + // greater than the current length. If offset is equal to the current + // length, we can still grow. If offset is beyond the current length, we + // would have to decide how to deal with the intermediate uninitialized + // bytes. So we punt on that case for simplicity and just say it's an + // error. + if (Offset > getLength()) + return make_error<BinaryStreamError>(stream_error_code::invalid_offset); + + uint32_t RequiredSize = Offset + Buffer.size(); + if (RequiredSize > Data.size()) + Data.resize(RequiredSize); + + ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size()); + return Error::success(); + } + + Error commit() override { return Error::success(); } + + /// \brief Return the properties of this stream. + virtual BinaryStreamFlags getFlags() const override { + return BSF_Write | BSF_Append; + } + + MutableArrayRef<uint8_t> data() { return Data; } +}; + /// \brief An implementation of WritableBinaryStream backed by an llvm /// FileOutputBuffer. class FileBufferByteStream : public WritableBinaryStream { diff --git a/include/llvm/Support/BinaryItemStream.h b/include/llvm/Support/BinaryItemStream.h index fe7e6caeaafb..278723ddf8da 100644 --- a/include/llvm/Support/BinaryItemStream.h +++ b/include/llvm/Support/BinaryItemStream.h @@ -45,7 +45,7 @@ public: if (!ExpectedIndex) return ExpectedIndex.takeError(); const auto &Item = Items[*ExpectedIndex]; - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; if (Size > Traits::length(Item)) return make_error<BinaryStreamError>(stream_error_code::stream_too_short); diff --git a/include/llvm/Support/BinaryStream.h b/include/llvm/Support/BinaryStream.h index a227117e063e..d69a03eccfdb 100644 --- a/include/llvm/Support/BinaryStream.h +++ b/include/llvm/Support/BinaryStream.h @@ -11,6 +11,7 @@ #define LLVM_SUPPORT_BINARYSTREAM_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -18,6 +19,13 @@ namespace llvm { +enum BinaryStreamFlags { + BSF_None = 0, + BSF_Write = 1, // Stream supports writing. + BSF_Append = 2, // Writing can occur at offset == length. + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ BSF_Append) +}; + /// \brief An interface for accessing data in a stream-like format, but which /// discourages copying. Instead of specifying a buffer in which to copy /// data on a read, the API returns an ArrayRef to data owned by the stream's @@ -45,8 +53,11 @@ public: /// \brief Return the number of bytes of data in this stream. virtual uint32_t getLength() = 0; + /// \brief Return the properties of this stream. + virtual BinaryStreamFlags getFlags() const { return BSF_None; } + protected: - Error checkOffset(uint32_t Offset, uint32_t DataSize) { + Error checkOffsetForRead(uint32_t Offset, uint32_t DataSize) { if (Offset > getLength()) return make_error<BinaryStreamError>(stream_error_code::invalid_offset); if (getLength() < DataSize + Offset) @@ -71,6 +82,19 @@ public: /// \brief For buffered streams, commits changes to the backing store. virtual Error commit() = 0; + + /// \brief Return the properties of this stream. + BinaryStreamFlags getFlags() const override { return BSF_Write; } + +protected: + Error checkOffsetForWrite(uint32_t Offset, uint32_t DataSize) { + if (!(getFlags() & BSF_Append)) + return checkOffsetForRead(Offset, DataSize); + + if (Offset > getLength()) + return make_error<BinaryStreamError>(stream_error_code::invalid_offset); + return Error::success(); + } }; } // end namespace llvm diff --git a/include/llvm/Support/BinaryStreamRef.h b/include/llvm/Support/BinaryStreamRef.h index 6d5135cb258d..5cf355be6fe9 100644 --- a/include/llvm/Support/BinaryStreamRef.h +++ b/include/llvm/Support/BinaryStreamRef.h @@ -11,6 +11,7 @@ #define LLVM_SUPPORT_BINARYSTREAMREF_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/BinaryStream.h" #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/Error.h" @@ -24,47 +25,74 @@ namespace llvm { template <class RefType, class StreamType> class BinaryStreamRefBase { protected: BinaryStreamRefBase() = default; + explicit BinaryStreamRefBase(StreamType &BorrowedImpl) + : BorrowedImpl(&BorrowedImpl), ViewOffset(0) { + if (!(BorrowedImpl.getFlags() & BSF_Append)) + Length = BorrowedImpl.getLength(); + } + BinaryStreamRefBase(std::shared_ptr<StreamType> SharedImpl, uint32_t Offset, - uint32_t Length) + Optional<uint32_t> Length) : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()), ViewOffset(Offset), Length(Length) {} BinaryStreamRefBase(StreamType &BorrowedImpl, uint32_t Offset, - uint32_t Length) + Optional<uint32_t> Length) : BorrowedImpl(&BorrowedImpl), ViewOffset(Offset), Length(Length) {} - BinaryStreamRefBase(const BinaryStreamRefBase &Other) { - SharedImpl = Other.SharedImpl; - BorrowedImpl = Other.BorrowedImpl; - ViewOffset = Other.ViewOffset; - Length = Other.Length; - } + BinaryStreamRefBase(const BinaryStreamRefBase &Other) = default; + BinaryStreamRefBase &operator=(const BinaryStreamRefBase &Other) = default; + + BinaryStreamRefBase &operator=(BinaryStreamRefBase &&Other) = default; + BinaryStreamRefBase(BinaryStreamRefBase &&Other) = default; public: llvm::support::endianness getEndian() const { return BorrowedImpl->getEndian(); } - uint32_t getLength() const { return Length; } + uint32_t getLength() const { + if (Length.hasValue()) + return *Length; - /// Return a new BinaryStreamRef with the first \p N elements removed. + return BorrowedImpl ? (BorrowedImpl->getLength() - ViewOffset) : 0; + } + + /// Return a new BinaryStreamRef with the first \p N elements removed. If + /// this BinaryStreamRef is length-tracking, then the resulting one will be + /// too. RefType drop_front(uint32_t N) const { if (!BorrowedImpl) return RefType(); - N = std::min(N, Length); + N = std::min(N, getLength()); RefType Result(static_cast<const RefType &>(*this)); + if (N == 0) + return Result; + Result.ViewOffset += N; - Result.Length -= N; + if (Result.Length.hasValue()) + *Result.Length -= N; return Result; } - /// Return a new BinaryStreamRef with the first \p N elements removed. + /// Return a new BinaryStreamRef with the last \p N elements removed. If + /// this BinaryStreamRef is length-tracking and \p N is greater than 0, then + /// this BinaryStreamRef will no longer length-track. RefType drop_back(uint32_t N) const { if (!BorrowedImpl) return RefType(); - N = std::min(N, Length); RefType Result(static_cast<const RefType &>(*this)); - Result.Length -= N; + N = std::min(N, getLength()); + + if (N == 0) + return Result; + + // Since we're dropping non-zero bytes from the end, stop length-tracking + // by setting the length of the resulting StreamRef to an explicit value. + if (!Result.Length.hasValue()) + Result.Length = getLength(); + + *Result.Length -= N; return Result; } @@ -105,7 +133,7 @@ public: } protected: - Error checkOffset(uint32_t Offset, uint32_t DataSize) const { + Error checkOffsetForRead(uint32_t Offset, uint32_t DataSize) const { if (Offset > getLength()) return make_error<BinaryStreamError>(stream_error_code::invalid_offset); if (getLength() < DataSize + Offset) @@ -116,7 +144,7 @@ protected: std::shared_ptr<StreamType> SharedImpl; StreamType *BorrowedImpl = nullptr; uint32_t ViewOffset = 0; - uint32_t Length = 0; + Optional<uint32_t> Length; }; /// \brief BinaryStreamRef is to BinaryStream what ArrayRef is to an Array. It @@ -131,18 +159,22 @@ class BinaryStreamRef friend BinaryStreamRefBase<BinaryStreamRef, BinaryStream>; friend class WritableBinaryStreamRef; BinaryStreamRef(std::shared_ptr<BinaryStream> Impl, uint32_t ViewOffset, - uint32_t Length) + Optional<uint32_t> Length) : BinaryStreamRefBase(Impl, ViewOffset, Length) {} public: BinaryStreamRef() = default; BinaryStreamRef(BinaryStream &Stream); - BinaryStreamRef(BinaryStream &Stream, uint32_t Offset, uint32_t Length); + BinaryStreamRef(BinaryStream &Stream, uint32_t Offset, + Optional<uint32_t> Length); explicit BinaryStreamRef(ArrayRef<uint8_t> Data, llvm::support::endianness Endian); explicit BinaryStreamRef(StringRef Data, llvm::support::endianness Endian); - BinaryStreamRef(const BinaryStreamRef &Other); + BinaryStreamRef(const BinaryStreamRef &Other) = default; + BinaryStreamRef &operator=(const BinaryStreamRef &Other) = default; + BinaryStreamRef(BinaryStreamRef &&Other) = default; + BinaryStreamRef &operator=(BinaryStreamRef &&Other) = default; // Use BinaryStreamRef.slice() instead. BinaryStreamRef(BinaryStreamRef &S, uint32_t Offset, @@ -193,17 +225,31 @@ class WritableBinaryStreamRef WritableBinaryStream> { friend BinaryStreamRefBase<WritableBinaryStreamRef, WritableBinaryStream>; WritableBinaryStreamRef(std::shared_ptr<WritableBinaryStream> Impl, - uint32_t ViewOffset, uint32_t Length) + uint32_t ViewOffset, Optional<uint32_t> Length) : BinaryStreamRefBase(Impl, ViewOffset, Length) {} + Error checkOffsetForWrite(uint32_t Offset, uint32_t DataSize) const { + if (!(BorrowedImpl->getFlags() & BSF_Append)) + return checkOffsetForRead(Offset, DataSize); + + if (Offset > getLength()) + return make_error<BinaryStreamError>(stream_error_code::invalid_offset); + return Error::success(); + } + public: WritableBinaryStreamRef() = default; WritableBinaryStreamRef(WritableBinaryStream &Stream); WritableBinaryStreamRef(WritableBinaryStream &Stream, uint32_t Offset, - uint32_t Length); + Optional<uint32_t> Length); explicit WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data, llvm::support::endianness Endian); - WritableBinaryStreamRef(const WritableBinaryStreamRef &Other); + WritableBinaryStreamRef(const WritableBinaryStreamRef &Other) = default; + WritableBinaryStreamRef & + operator=(const WritableBinaryStreamRef &Other) = default; + + WritableBinaryStreamRef(WritableBinaryStreamRef &&Other) = default; + WritableBinaryStreamRef &operator=(WritableBinaryStreamRef &&Other) = default; // Use WritableBinaryStreamRef.slice() instead. WritableBinaryStreamRef(WritableBinaryStreamRef &S, uint32_t Offset, diff --git a/include/llvm/Support/CMakeLists.txt b/include/llvm/Support/CMakeLists.txt index 95752cf01856..bf662c77351d 100644 --- a/include/llvm/Support/CMakeLists.txt +++ b/include/llvm/Support/CMakeLists.txt @@ -1,38 +1,3 @@ -# Figure out if we can track VC revisions. -function(find_first_existing_file out_var) - foreach(file ${ARGN}) - if(EXISTS "${file}") - set(${out_var} "${file}" PARENT_SCOPE) - return() - endif() - endforeach() -endfunction() - -macro(find_first_existing_vc_file out_var path) - if ( LLVM_APPEND_VC_REV ) - find_program(git_executable NAMES git git.exe git.cmd) - # Run from a subdirectory to force git to print an absolute path. - execute_process(COMMAND ${git_executable} rev-parse --git-dir - WORKING_DIRECTORY ${path}/cmake - RESULT_VARIABLE git_result - OUTPUT_VARIABLE git_dir - ERROR_QUIET) - if(git_result EQUAL 0) - string(STRIP "${git_dir}" git_dir) - set(${out_var} "${git_dir}/logs/HEAD") - # some branchless cases (e.g. 'repo') may not yet have .git/logs/HEAD - if (NOT EXISTS "${git_dir}/logs/HEAD") - file(WRITE "${git_dir}/logs/HEAD" "") - endif() - else() - find_first_existing_file(${out_var} - "${path}/.svn/wc.db" # SVN 1.7 - "${path}/.svn/entries" # SVN 1.6 - ) - endif() - endif() -endmacro() - find_first_existing_vc_file(llvm_vc "${LLVM_MAIN_SRC_DIR}") # The VC revision include that we want to generate. @@ -40,7 +5,20 @@ set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSRevision.h") set(get_svn_script "${LLVM_CMAKE_PATH}/GenerateVersionFromCVS.cmake") -if(DEFINED llvm_vc) +file(WRITE "${version_inc}.empty" "") +if((DEFINED llvm_vc) AND LLVM_APPEND_VC_REV) + + execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files + "${version_inc}.empty" "${version_inc}" + RESULT_VARIABLE files_not_equal + OUTPUT_QUIET + ERROR_QUIET) + # Remove ${version_inc} if it's empty -- toggling LLVM_APPEND_VC_REV + # from OFF to ON. + if(NOT files_not_equal) + file(REMOVE "${version_inc}") + endif() + # Create custom target to generate the VC revision include. add_custom_command(OUTPUT "${version_inc}" DEPENDS "${llvm_vc}" "${get_svn_script}" @@ -49,13 +27,17 @@ if(DEFINED llvm_vc) "-DNAME=LLVM_REVISION" "-DHEADER_FILE=${version_inc}" -P "${get_svn_script}") - - # Mark the generated header as being generated. - set_source_files_properties("${version_inc}" - PROPERTIES GENERATED TRUE - HEADER_FILE_ONLY TRUE) else() - file(WRITE "${version_inc}" "") + # Make sure ${version_inc} is an empty file. + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${version_inc}.empty" "${version_inc}") endif() +file(REMOVE "${version_inc}.empty") + +# Mark the generated header as being generated. +set_source_files_properties("${version_inc}" + PROPERTIES GENERATED TRUE + HEADER_FILE_ONLY TRUE) add_custom_target(llvm_vcsrevision_h DEPENDS "${version_inc}") +set_target_properties(llvm_vcsrevision_h PROPERTIES FOLDER "Misc") diff --git a/include/llvm/Support/CachePruning.h b/include/llvm/Support/CachePruning.h index 46e34358573b..c577e9b8b631 100644 --- a/include/llvm/Support/CachePruning.h +++ b/include/llvm/Support/CachePruning.h @@ -46,6 +46,15 @@ struct CachePruningPolicy { /// of available space on the disk will be reduced to the amount of available /// space. A value of 0 disables the absolute size-based pruning. uint64_t MaxSizeBytes = 0; + + /// The maximum number of files in the cache directory. A value of 0 disables + /// the number of files based pruning. + /// + /// This defaults to 1000000 because with that many files there are + /// diminishing returns on the effectiveness of the cache, and some file + /// systems have a limit on how many files can be contained in a directory + /// (notably ext4, which is limited to around 6000000 files). + uint64_t MaxSizeFiles = 1000000; }; /// Parse the given string as a cache pruning policy. Defaults are taken from a diff --git a/include/llvm/Support/Chrono.h b/include/llvm/Support/Chrono.h index 6118ed0476ed..994068af3771 100644 --- a/include/llvm/Support/Chrono.h +++ b/include/llvm/Support/Chrono.h @@ -51,6 +51,20 @@ toTimePoint(std::time_t T) { raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP); +/// Format provider for TimePoint<> +/// +/// The options string is a strftime format string, with extensions: +/// - %L is millis: 000-999 +/// - %f is micros: 000000-999999 +/// - %N is nanos: 000000000 - 999999999 +/// +/// If no options are given, the default format is "%Y-%m-%d %H:%M:%S.%N". +template <> +struct format_provider<sys::TimePoint<>> { + static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS, + StringRef Style); +}; + /// Implementation of format_provider<T> for duration types. /// /// The options string of a duration type has the grammar: diff --git a/include/llvm/Support/CodeGen.h b/include/llvm/Support/CodeGen.h index 941c112b0dd2..5f9e33129587 100644 --- a/include/llvm/Support/CodeGen.h +++ b/include/llvm/Support/CodeGen.h @@ -25,7 +25,7 @@ namespace llvm { // Code model types. namespace CodeModel { // Sync changes with CodeGenCWrappers.h. - enum Model { Default, JITDefault, Small, Kernel, Medium, Large }; + enum Model { Small, Kernel, Medium, Large }; } namespace PICLevel { diff --git a/include/llvm/Support/CodeGenCWrappers.h b/include/llvm/Support/CodeGenCWrappers.h index 6db4433a4350..47971e80cefb 100644 --- a/include/llvm/Support/CodeGenCWrappers.h +++ b/include/llvm/Support/CodeGenCWrappers.h @@ -17,17 +17,20 @@ #define LLVM_SUPPORT_CODEGENCWRAPPERS_H #include "llvm-c/TargetMachine.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/ErrorHandling.h" namespace llvm { -inline CodeModel::Model unwrap(LLVMCodeModel Model) { +inline Optional<CodeModel::Model> unwrap(LLVMCodeModel Model, bool &JIT) { + JIT = false; switch (Model) { - case LLVMCodeModelDefault: - return CodeModel::Default; case LLVMCodeModelJITDefault: - return CodeModel::JITDefault; + JIT = true; + LLVM_FALLTHROUGH; + case LLVMCodeModelDefault: + return None; case LLVMCodeModelSmall: return CodeModel::Small; case LLVMCodeModelKernel: @@ -37,15 +40,11 @@ inline CodeModel::Model unwrap(LLVMCodeModel Model) { case LLVMCodeModelLarge: return CodeModel::Large; } - return CodeModel::Default; + return CodeModel::Small; } inline LLVMCodeModel wrap(CodeModel::Model Model) { switch (Model) { - case CodeModel::Default: - return LLVMCodeModelDefault; - case CodeModel::JITDefault: - return LLVMCodeModelJITDefault; case CodeModel::Small: return LLVMCodeModelSmall; case CodeModel::Kernel: @@ -61,4 +60,3 @@ inline LLVMCodeModel wrap(CodeModel::Model Model) { } // end llvm namespace #endif - diff --git a/include/llvm/Support/CodeGenCoverage.h b/include/llvm/Support/CodeGenCoverage.h new file mode 100644 index 000000000000..d5bd837bff28 --- /dev/null +++ b/include/llvm/Support/CodeGenCoverage.h @@ -0,0 +1,37 @@ +//== llvm/Support/CodeGenCoverage.h ------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file This file provides rule coverage tracking for tablegen-erated CodeGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H +#define LLVM_SUPPORT_CODEGENCOVERAGE_H + +#include "llvm/ADT/BitVector.h" + +namespace llvm { +class LLVMContext; +class MemoryBuffer; + +class CodeGenCoverage { +protected: + BitVector RuleCoverage; + +public: + CodeGenCoverage(); + + void setCovered(uint64_t RuleID); + bool isCovered(uint64_t RuleID); + + bool parse(MemoryBuffer &Buffer, StringRef BackendName); + bool emit(StringRef FilePrefix, StringRef BackendName) const; + void reset(); +}; +} // end namespace llvm + +#endif // ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H diff --git a/include/llvm/Support/CommandLine.h b/include/llvm/Support/CommandLine.h index 71d2f0293083..d1901db7c68e 100644 --- a/include/llvm/Support/CommandLine.h +++ b/include/llvm/Support/CommandLine.h @@ -66,12 +66,15 @@ bool ParseCommandLineOptions(int argc, const char *const *argv, void ParseEnvironmentOptions(const char *progName, const char *envvar, const char *Overview = ""); +// Function pointer type for printing version information. +using VersionPrinterTy = std::function<void(raw_ostream &)>; + ///===---------------------------------------------------------------------===// /// SetVersionPrinter - Override the default (LLVM specific) version printer /// used to print out the version when --version is given /// on the command line. This allows other systems using the /// CommandLine utilities to print their own version string. -void SetVersionPrinter(void (*func)()); +void SetVersionPrinter(VersionPrinterTy func); ///===---------------------------------------------------------------------===// /// AddExtraVersionPrinter - Add an extra printer to use in addition to the @@ -80,7 +83,7 @@ void SetVersionPrinter(void (*func)()); /// which will be called after the basic LLVM version /// printing is complete. Each can then add additional /// information specific to the tool. -void AddExtraVersionPrinter(void (*func)()); +void AddExtraVersionPrinter(VersionPrinterTy func); // PrintOptionValues - Print option values. // With -print-options print the difference between option values and defaults. @@ -263,7 +266,7 @@ public: StringRef ValueStr; // String describing what the value of this option is OptionCategory *Category; // The Category this option belongs to SmallPtrSet<SubCommand *, 4> Subs; // The subcommands this option belongs to. - bool FullyInitialized = false; // Has addArguemnt been called? + bool FullyInitialized = false; // Has addArgument been called? inline enum NumOccurrencesFlag getNumOccurrencesFlag() const { return (enum NumOccurrencesFlag)Occurrences; @@ -346,6 +349,8 @@ public: virtual void printOptionValue(size_t GlobalWidth, bool Force) const = 0; + virtual void setDefault() = 0; + static void printHelpStr(StringRef HelpStr, size_t Indent, size_t FirstLineIndentedBy); @@ -1315,6 +1320,20 @@ class opt : public Option, } } + template <class T, class = typename std::enable_if< + std::is_assignable<T&, T>::value>::type> + void setDefaultImpl() { + const OptionValue<DataType> &V = this->getDefault(); + if (V.hasValue()) + this->setValue(V.getValue()); + } + + template <class T, class = typename std::enable_if< + !std::is_assignable<T&, T>::value>::type> + void setDefaultImpl(...) {} + + void setDefault() override { setDefaultImpl<DataType>(); } + void done() { addArgument(); Parser.initialize(); @@ -1490,6 +1509,8 @@ class list : public Option, public list_storage<DataType, StorageClass> { void printOptionValue(size_t /*GlobalWidth*/, bool /*Force*/) const override { } + void setDefault() override {} + void done() { addArgument(); Parser.initialize(); @@ -1631,6 +1652,8 @@ class bits : public Option, public bits_storage<DataType, Storage> { void printOptionValue(size_t /*GlobalWidth*/, bool /*Force*/) const override { } + void setDefault() override {} + void done() { addArgument(); Parser.initialize(); @@ -1681,6 +1704,8 @@ class alias : public Option { void printOptionValue(size_t /*GlobalWidth*/, bool /*Force*/) const override { } + void setDefault() override { AliasFor->setDefault(); } + ValueExpected getValueExpectedFlagDefault() const override { return AliasFor->getValueExpectedFlag(); } @@ -1737,8 +1762,6 @@ void PrintVersionMessage(); /// This function just prints the help message, exactly the same way as if the /// -help or -help-hidden option had been given on the command line. /// -/// NOTE: THIS FUNCTION TERMINATES THE PROGRAM! -/// /// \param Hidden if true will print hidden options /// \param Categorized if true print options in categories void PrintHelpMessage(bool Hidden = false, bool Categorized = false); diff --git a/include/llvm/Support/ConvertUTF.h b/include/llvm/Support/ConvertUTF.h index bd439f360216..99ae171aeabb 100644 --- a/include/llvm/Support/ConvertUTF.h +++ b/include/llvm/Support/ConvertUTF.h @@ -242,10 +242,10 @@ bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr); * * \sa ConvertUTF8toUTF32 */ -static inline ConversionResult convertUTF8Sequence(const UTF8 **source, - const UTF8 *sourceEnd, - UTF32 *target, - ConversionFlags flags) { +inline ConversionResult convertUTF8Sequence(const UTF8 **source, + const UTF8 *sourceEnd, + UTF32 *target, + ConversionFlags flags) { if (*source == sourceEnd) return sourceExhausted; unsigned size = getNumBytesForUTF8(**source); diff --git a/include/llvm/Support/DebugCounter.h b/include/llvm/Support/DebugCounter.h index a533feae7fa3..52e1bd71a2f2 100644 --- a/include/llvm/Support/DebugCounter.h +++ b/include/llvm/Support/DebugCounter.h @@ -159,7 +159,7 @@ private: #define DEBUG_COUNTER(VARNAME, COUNTERNAME, DESC) \ static const unsigned VARNAME = \ - DebugCounter::registerCounter(COUNTERNAME, DESC); + DebugCounter::registerCounter(COUNTERNAME, DESC) } // namespace llvm #endif diff --git a/include/llvm/Support/Error.h b/include/llvm/Support/Error.h index 9a7fa0ae6356..8567af392fb0 100644 --- a/include/llvm/Support/Error.h +++ b/include/llvm/Support/Error.h @@ -246,18 +246,20 @@ public: } private: +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + // assertIsChecked() happens very frequently, but under normal circumstances + // is supposed to be a no-op. So we want it to be inlined, but having a bunch + // of debug prints can cause the function to be too large for inlining. So + // it's important that we define this function out of line so that it can't be + // inlined. + LLVM_ATTRIBUTE_NORETURN + void fatalUncheckedError() const; +#endif + void assertIsChecked() { #if LLVM_ENABLE_ABI_BREAKING_CHECKS - if (!getChecked() || getPtr()) { - dbgs() << "Program aborted due to an unhandled Error:\n"; - if (getPtr()) - getPtr()->log(dbgs()); - else - dbgs() - << "Error value was Success. (Note: Success values must still be " - "checked prior to being destroyed).\n"; - abort(); - } + if (LLVM_UNLIKELY(!getChecked() || getPtr())) + fatalUncheckedError(); #endif } @@ -407,235 +409,6 @@ inline Error joinErrors(Error E1, Error E2) { return ErrorList::join(std::move(E1), std::move(E2)); } -/// Helper for testing applicability of, and applying, handlers for -/// ErrorInfo types. -template <typename HandlerT> -class ErrorHandlerTraits - : public ErrorHandlerTraits<decltype( - &std::remove_reference<HandlerT>::type::operator())> {}; - -// Specialization functions of the form 'Error (const ErrT&)'. -template <typename ErrT> class ErrorHandlerTraits<Error (&)(ErrT &)> { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA<ErrT>(); - } - - template <typename HandlerT> - static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - return H(static_cast<ErrT &>(*E)); - } -}; - -// Specialization functions of the form 'void (const ErrT&)'. -template <typename ErrT> class ErrorHandlerTraits<void (&)(ErrT &)> { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA<ErrT>(); - } - - template <typename HandlerT> - static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - H(static_cast<ErrT &>(*E)); - return Error::success(); - } -}; - -/// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'. -template <typename ErrT> -class ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)> { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA<ErrT>(); - } - - template <typename HandlerT> - static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); - return H(std::move(SubE)); - } -}; - -/// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'. -template <typename ErrT> -class ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)> { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA<ErrT>(); - } - - template <typename HandlerT> - static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); - H(std::move(SubE)); - return Error::success(); - } -}; - -// Specialization for member functions of the form 'RetT (const ErrT&)'. -template <typename C, typename RetT, typename ErrT> -class ErrorHandlerTraits<RetT (C::*)(ErrT &)> - : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; - -// Specialization for member functions of the form 'RetT (const ErrT&) const'. -template <typename C, typename RetT, typename ErrT> -class ErrorHandlerTraits<RetT (C::*)(ErrT &) const> - : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; - -// Specialization for member functions of the form 'RetT (const ErrT&)'. -template <typename C, typename RetT, typename ErrT> -class ErrorHandlerTraits<RetT (C::*)(const ErrT &)> - : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; - -// Specialization for member functions of the form 'RetT (const ErrT&) const'. -template <typename C, typename RetT, typename ErrT> -class ErrorHandlerTraits<RetT (C::*)(const ErrT &) const> - : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; - -/// Specialization for member functions of the form -/// 'RetT (std::unique_ptr<ErrT>) const'. -template <typename C, typename RetT, typename ErrT> -class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)> - : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; - -/// Specialization for member functions of the form -/// 'RetT (std::unique_ptr<ErrT>) const'. -template <typename C, typename RetT, typename ErrT> -class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const> - : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; - -inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) { - return Error(std::move(Payload)); -} - -template <typename HandlerT, typename... HandlerTs> -Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload, - HandlerT &&Handler, HandlerTs &&... Handlers) { - if (ErrorHandlerTraits<HandlerT>::appliesTo(*Payload)) - return ErrorHandlerTraits<HandlerT>::apply(std::forward<HandlerT>(Handler), - std::move(Payload)); - return handleErrorImpl(std::move(Payload), - std::forward<HandlerTs>(Handlers)...); -} - -/// Pass the ErrorInfo(s) contained in E to their respective handlers. Any -/// unhandled errors (or Errors returned by handlers) are re-concatenated and -/// returned. -/// Because this function returns an error, its result must also be checked -/// or returned. If you intend to handle all errors use handleAllErrors -/// (which returns void, and will abort() on unhandled errors) instead. -template <typename... HandlerTs> -Error handleErrors(Error E, HandlerTs &&... Hs) { - if (!E) - return Error::success(); - - std::unique_ptr<ErrorInfoBase> Payload = E.takePayload(); - - if (Payload->isA<ErrorList>()) { - ErrorList &List = static_cast<ErrorList &>(*Payload); - Error R; - for (auto &P : List.Payloads) - R = ErrorList::join( - std::move(R), - handleErrorImpl(std::move(P), std::forward<HandlerTs>(Hs)...)); - return R; - } - - return handleErrorImpl(std::move(Payload), std::forward<HandlerTs>(Hs)...); -} - -/// Behaves the same as handleErrors, except that it requires that all -/// errors be handled by the given handlers. If any unhandled error remains -/// after the handlers have run, abort() will be called. -template <typename... HandlerTs> -void handleAllErrors(Error E, HandlerTs &&... Handlers) { - auto F = handleErrors(std::move(E), std::forward<HandlerTs>(Handlers)...); - // Cast 'F' to bool to set the 'Checked' flag if it's a success value: - (void)!F; -} - -/// Check that E is a non-error, then drop it. -inline void handleAllErrors(Error E) { - // Cast 'E' to a bool to set the 'Checked' flag if it's a success value: - (void)!E; -} - -/// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner -/// will be printed before the first one is logged. A newline will be printed -/// after each error. -/// -/// This is useful in the base level of your program to allow clean termination -/// (allowing clean deallocation of resources, etc.), while reporting error -/// information to the user. -void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner); - -/// Write all error messages (if any) in E to a string. The newline character -/// is used to separate error messages. -inline std::string toString(Error E) { - SmallVector<std::string, 2> Errors; - handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { - Errors.push_back(EI.message()); - }); - return join(Errors.begin(), Errors.end(), "\n"); -} - -/// Consume a Error without doing anything. This method should be used -/// only where an error can be considered a reasonable and expected return -/// value. -/// -/// Uses of this method are potentially indicative of design problems: If it's -/// legitimate to do nothing while processing an "error", the error-producer -/// might be more clearly refactored to return an Optional<T>. -inline void consumeError(Error Err) { - handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); -} - -/// Helper for Errors used as out-parameters. -/// -/// This helper is for use with the Error-as-out-parameter idiom, where an error -/// is passed to a function or method by reference, rather than being returned. -/// In such cases it is helpful to set the checked bit on entry to the function -/// so that the error can be written to (unchecked Errors abort on assignment) -/// and clear the checked bit on exit so that clients cannot accidentally forget -/// to check the result. This helper performs these actions automatically using -/// RAII: -/// -/// @code{.cpp} -/// Result foo(Error &Err) { -/// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set -/// // <body of foo> -/// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed. -/// } -/// @endcode -/// -/// ErrorAsOutParameter takes an Error* rather than Error& so that it can be -/// used with optional Errors (Error pointers that are allowed to be null). If -/// ErrorAsOutParameter took an Error reference, an instance would have to be -/// created inside every condition that verified that Error was non-null. By -/// taking an Error pointer we can just create one instance at the top of the -/// function. -class ErrorAsOutParameter { -public: - ErrorAsOutParameter(Error *Err) : Err(Err) { - // Raise the checked bit if Err is success. - if (Err) - (void)!!*Err; - } - - ~ErrorAsOutParameter() { - // Clear the checked bit. - if (Err && !*Err) - *Err = Error::success(); - } - -private: - Error *Err; -}; - /// Tagged union holding either a T or a Error. /// /// This class parallels ErrorOr, but replaces error_code with Error. Since @@ -861,19 +634,26 @@ private: #endif } +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + LLVM_ATTRIBUTE_NORETURN + LLVM_ATTRIBUTE_NOINLINE + void fatalUncheckedExpected() const { + dbgs() << "Expected<T> must be checked before access or destruction.\n"; + if (HasError) { + dbgs() << "Unchecked Expected<T> contained error:\n"; + (*getErrorStorage())->log(dbgs()); + } else + dbgs() << "Expected<T> value was in success state. (Note: Expected<T> " + "values in success mode must still be checked prior to being " + "destroyed).\n"; + abort(); + } +#endif + void assertIsChecked() { #if LLVM_ENABLE_ABI_BREAKING_CHECKS - if (Unchecked) { - dbgs() << "Expected<T> must be checked before access or destruction.\n"; - if (HasError) { - dbgs() << "Unchecked Expected<T> contained error:\n"; - (*getErrorStorage())->log(dbgs()); - } else - dbgs() << "Expected<T> value was in success state. (Note: Expected<T> " - "values in success mode must still be checked prior to being " - "destroyed).\n"; - abort(); - } + if (LLVM_UNLIKELY(Unchecked)) + fatalUncheckedExpected(); #endif } @@ -887,6 +667,344 @@ private: #endif }; +/// Report a serious error, calling any installed error handler. See +/// ErrorHandling.h. +LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, + bool gen_crash_diag = true); + +/// Report a fatal error if Err is a failure value. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns +/// // Error::success(). +/// Error foo(bool DoFallibleOperation); +/// +/// cantFail(foo(false)); +/// @endcode +inline void cantFail(Error Err, const char *Msg = nullptr) { + if (Err) { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + llvm_unreachable(Msg); + } +} + +/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and +/// returns the contained value. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns an int. +/// Expected<int> foo(bool DoFallibleOperation); +/// +/// int X = cantFail(foo(false)); +/// @endcode +template <typename T> +T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) { + if (ValOrErr) + return std::move(*ValOrErr); + else { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + llvm_unreachable(Msg); + } +} + +/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and +/// returns the contained reference. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns a Bar&. +/// Expected<Bar&> foo(bool DoFallibleOperation); +/// +/// Bar &X = cantFail(foo(false)); +/// @endcode +template <typename T> +T& cantFail(Expected<T&> ValOrErr, const char *Msg = nullptr) { + if (ValOrErr) + return *ValOrErr; + else { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + llvm_unreachable(Msg); + } +} + +/// Helper for testing applicability of, and applying, handlers for +/// ErrorInfo types. +template <typename HandlerT> +class ErrorHandlerTraits + : public ErrorHandlerTraits<decltype( + &std::remove_reference<HandlerT>::type::operator())> {}; + +// Specialization functions of the form 'Error (const ErrT&)'. +template <typename ErrT> class ErrorHandlerTraits<Error (&)(ErrT &)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA<ErrT>(); + } + + template <typename HandlerT> + static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + return H(static_cast<ErrT &>(*E)); + } +}; + +// Specialization functions of the form 'void (const ErrT&)'. +template <typename ErrT> class ErrorHandlerTraits<void (&)(ErrT &)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA<ErrT>(); + } + + template <typename HandlerT> + static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + H(static_cast<ErrT &>(*E)); + return Error::success(); + } +}; + +/// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'. +template <typename ErrT> +class ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA<ErrT>(); + } + + template <typename HandlerT> + static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); + return H(std::move(SubE)); + } +}; + +/// Specialization for functions of the form 'void (std::unique_ptr<ErrT>)'. +template <typename ErrT> +class ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA<ErrT>(); + } + + template <typename HandlerT> + static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); + H(std::move(SubE)); + return Error::success(); + } +}; + +// Specialization for member functions of the form 'RetT (const ErrT&)'. +template <typename C, typename RetT, typename ErrT> +class ErrorHandlerTraits<RetT (C::*)(ErrT &)> + : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; + +// Specialization for member functions of the form 'RetT (const ErrT&) const'. +template <typename C, typename RetT, typename ErrT> +class ErrorHandlerTraits<RetT (C::*)(ErrT &) const> + : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; + +// Specialization for member functions of the form 'RetT (const ErrT&)'. +template <typename C, typename RetT, typename ErrT> +class ErrorHandlerTraits<RetT (C::*)(const ErrT &)> + : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; + +// Specialization for member functions of the form 'RetT (const ErrT&) const'. +template <typename C, typename RetT, typename ErrT> +class ErrorHandlerTraits<RetT (C::*)(const ErrT &) const> + : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; + +/// Specialization for member functions of the form +/// 'RetT (std::unique_ptr<ErrT>)'. +template <typename C, typename RetT, typename ErrT> +class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)> + : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; + +/// Specialization for member functions of the form +/// 'RetT (std::unique_ptr<ErrT>) const'. +template <typename C, typename RetT, typename ErrT> +class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const> + : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; + +inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) { + return Error(std::move(Payload)); +} + +template <typename HandlerT, typename... HandlerTs> +Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload, + HandlerT &&Handler, HandlerTs &&... Handlers) { + if (ErrorHandlerTraits<HandlerT>::appliesTo(*Payload)) + return ErrorHandlerTraits<HandlerT>::apply(std::forward<HandlerT>(Handler), + std::move(Payload)); + return handleErrorImpl(std::move(Payload), + std::forward<HandlerTs>(Handlers)...); +} + +/// Pass the ErrorInfo(s) contained in E to their respective handlers. Any +/// unhandled errors (or Errors returned by handlers) are re-concatenated and +/// returned. +/// Because this function returns an error, its result must also be checked +/// or returned. If you intend to handle all errors use handleAllErrors +/// (which returns void, and will abort() on unhandled errors) instead. +template <typename... HandlerTs> +Error handleErrors(Error E, HandlerTs &&... Hs) { + if (!E) + return Error::success(); + + std::unique_ptr<ErrorInfoBase> Payload = E.takePayload(); + + if (Payload->isA<ErrorList>()) { + ErrorList &List = static_cast<ErrorList &>(*Payload); + Error R; + for (auto &P : List.Payloads) + R = ErrorList::join( + std::move(R), + handleErrorImpl(std::move(P), std::forward<HandlerTs>(Hs)...)); + return R; + } + + return handleErrorImpl(std::move(Payload), std::forward<HandlerTs>(Hs)...); +} + +/// Behaves the same as handleErrors, except that it requires that all +/// errors be handled by the given handlers. If any unhandled error remains +/// after the handlers have run, report_fatal_error() will be called. +template <typename... HandlerTs> +void handleAllErrors(Error E, HandlerTs &&... Handlers) { + cantFail(handleErrors(std::move(E), std::forward<HandlerTs>(Handlers)...)); +} + +/// Check that E is a non-error, then drop it. +/// If E is an error report_fatal_error will be called. +inline void handleAllErrors(Error E) { + cantFail(std::move(E)); +} + +/// Handle any errors (if present) in an Expected<T>, then try a recovery path. +/// +/// If the incoming value is a success value it is returned unmodified. If it +/// is a failure value then it the contained error is passed to handleErrors. +/// If handleErrors is able to handle the error then the RecoveryPath functor +/// is called to supply the final result. If handleErrors is not able to +/// handle all errors then the unhandled errors are returned. +/// +/// This utility enables the follow pattern: +/// +/// @code{.cpp} +/// enum FooStrategy { Aggressive, Conservative }; +/// Expected<Foo> foo(FooStrategy S); +/// +/// auto ResultOrErr = +/// handleExpected( +/// foo(Aggressive), +/// []() { return foo(Conservative); }, +/// [](AggressiveStrategyError&) { +/// // Implicitly conusme this - we'll recover by using a conservative +/// // strategy. +/// }); +/// +/// @endcode +template <typename T, typename RecoveryFtor, typename... HandlerTs> +Expected<T> handleExpected(Expected<T> ValOrErr, RecoveryFtor &&RecoveryPath, + HandlerTs &&... Handlers) { + if (ValOrErr) + return ValOrErr; + + if (auto Err = handleErrors(ValOrErr.takeError(), + std::forward<HandlerTs>(Handlers)...)) + return std::move(Err); + + return RecoveryPath(); +} + +/// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner +/// will be printed before the first one is logged. A newline will be printed +/// after each error. +/// +/// This is useful in the base level of your program to allow clean termination +/// (allowing clean deallocation of resources, etc.), while reporting error +/// information to the user. +void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner); + +/// Write all error messages (if any) in E to a string. The newline character +/// is used to separate error messages. +inline std::string toString(Error E) { + SmallVector<std::string, 2> Errors; + handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { + Errors.push_back(EI.message()); + }); + return join(Errors.begin(), Errors.end(), "\n"); +} + +/// Consume a Error without doing anything. This method should be used +/// only where an error can be considered a reasonable and expected return +/// value. +/// +/// Uses of this method are potentially indicative of design problems: If it's +/// legitimate to do nothing while processing an "error", the error-producer +/// might be more clearly refactored to return an Optional<T>. +inline void consumeError(Error Err) { + handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); +} + +/// Helper for Errors used as out-parameters. +/// +/// This helper is for use with the Error-as-out-parameter idiom, where an error +/// is passed to a function or method by reference, rather than being returned. +/// In such cases it is helpful to set the checked bit on entry to the function +/// so that the error can be written to (unchecked Errors abort on assignment) +/// and clear the checked bit on exit so that clients cannot accidentally forget +/// to check the result. This helper performs these actions automatically using +/// RAII: +/// +/// @code{.cpp} +/// Result foo(Error &Err) { +/// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set +/// // <body of foo> +/// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed. +/// } +/// @endcode +/// +/// ErrorAsOutParameter takes an Error* rather than Error& so that it can be +/// used with optional Errors (Error pointers that are allowed to be null). If +/// ErrorAsOutParameter took an Error reference, an instance would have to be +/// created inside every condition that verified that Error was non-null. By +/// taking an Error pointer we can just create one instance at the top of the +/// function. +class ErrorAsOutParameter { +public: + ErrorAsOutParameter(Error *Err) : Err(Err) { + // Raise the checked bit if Err is success. + if (Err) + (void)!!*Err; + } + + ~ErrorAsOutParameter() { + // Clear the checked bit. + if (Err && !*Err) + *Err = Error::success(); + } + +private: + Error *Err; +}; + /// Helper for Expected<T>s used as out-parameters. /// /// See ErrorAsOutParameter. @@ -1032,71 +1150,6 @@ private: std::function<int(const Error &)> GetExitCode; }; -/// Report a serious error, calling any installed error handler. See -/// ErrorHandling.h. -LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, - bool gen_crash_diag = true); - -/// Report a fatal error if Err is a failure value. -/// -/// This function can be used to wrap calls to fallible functions ONLY when it -/// is known that the Error will always be a success value. E.g. -/// -/// @code{.cpp} -/// // foo only attempts the fallible operation if DoFallibleOperation is -/// // true. If DoFallibleOperation is false then foo always returns -/// // Error::success(). -/// Error foo(bool DoFallibleOperation); -/// -/// cantFail(foo(false)); -/// @endcode -inline void cantFail(Error Err) { - if (Err) - llvm_unreachable("Failure value returned from cantFail wrapped call"); -} - -/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and -/// returns the contained value. -/// -/// This function can be used to wrap calls to fallible functions ONLY when it -/// is known that the Error will always be a success value. E.g. -/// -/// @code{.cpp} -/// // foo only attempts the fallible operation if DoFallibleOperation is -/// // true. If DoFallibleOperation is false then foo always returns an int. -/// Expected<int> foo(bool DoFallibleOperation); -/// -/// int X = cantFail(foo(false)); -/// @endcode -template <typename T> -T cantFail(Expected<T> ValOrErr) { - if (ValOrErr) - return std::move(*ValOrErr); - else - llvm_unreachable("Failure value returned from cantFail wrapped call"); -} - -/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and -/// returns the contained reference. -/// -/// This function can be used to wrap calls to fallible functions ONLY when it -/// is known that the Error will always be a success value. E.g. -/// -/// @code{.cpp} -/// // foo only attempts the fallible operation if DoFallibleOperation is -/// // true. If DoFallibleOperation is false then foo always returns a Bar&. -/// Expected<Bar&> foo(bool DoFallibleOperation); -/// -/// Bar &X = cantFail(foo(false)); -/// @endcode -template <typename T> -T& cantFail(Expected<T&> ValOrErr) { - if (ValOrErr) - return *ValOrErr; - else - llvm_unreachable("Failure value returned from cantFail wrapped call"); -} - } // end namespace llvm #endif // LLVM_SUPPORT_ERROR_H diff --git a/include/llvm/Support/FileOutputBuffer.h b/include/llvm/Support/FileOutputBuffer.h index 2c054c75374b..6aed423a01e3 100644 --- a/include/llvm/Support/FileOutputBuffer.h +++ b/include/llvm/Support/FileOutputBuffer.h @@ -17,7 +17,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" -#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" namespace llvm { @@ -30,7 +30,6 @@ namespace llvm { /// not committed, the file will be deleted in the FileOutputBuffer destructor. class FileOutputBuffer { public: - enum { F_executable = 1 /// set the 'x' bit on the resulting file }; @@ -38,52 +37,37 @@ public: /// Factory method to create an OutputBuffer object which manages a read/write /// buffer of the specified size. When committed, the buffer will be written /// to the file at the specified path. - static ErrorOr<std::unique_ptr<FileOutputBuffer>> + static Expected<std::unique_ptr<FileOutputBuffer>> create(StringRef FilePath, size_t Size, unsigned Flags = 0); /// Returns a pointer to the start of the buffer. - uint8_t *getBufferStart() { - return (uint8_t*)Region->data(); - } + virtual uint8_t *getBufferStart() const = 0; /// Returns a pointer to the end of the buffer. - uint8_t *getBufferEnd() { - return (uint8_t*)Region->data() + Region->size(); - } + virtual uint8_t *getBufferEnd() const = 0; /// Returns size of the buffer. - size_t getBufferSize() const { - return Region->size(); - } + virtual size_t getBufferSize() const = 0; /// Returns path where file will show up if buffer is committed. - StringRef getPath() const { - return FinalPath; - } + StringRef getPath() const { return FinalPath; } /// Flushes the content of the buffer to its file and deallocates the /// buffer. If commit() is not called before this object's destructor /// is called, the file is deleted in the destructor. The optional parameter /// is used if it turns out you want the file size to be smaller than /// initially requested. - std::error_code commit(); + virtual Error commit() = 0; /// If this object was previously committed, the destructor just deletes /// this object. If this object was not committed, the destructor /// deallocates the buffer and the target file is never written. - ~FileOutputBuffer(); - -private: - FileOutputBuffer(const FileOutputBuffer &) = delete; - FileOutputBuffer &operator=(const FileOutputBuffer &) = delete; + virtual ~FileOutputBuffer() {} - FileOutputBuffer(std::unique_ptr<llvm::sys::fs::mapped_file_region> R, - StringRef Path, StringRef TempPath, bool IsRegular); +protected: + FileOutputBuffer(StringRef Path) : FinalPath(Path) {} - std::unique_ptr<llvm::sys::fs::mapped_file_region> Region; - SmallString<128> FinalPath; - SmallString<128> TempPath; - bool IsRegular; + std::string FinalPath; }; } // end namespace llvm diff --git a/include/llvm/Support/FileSystem.h b/include/llvm/Support/FileSystem.h index 21c5fcdb7145..b1683ba5ddb3 100644 --- a/include/llvm/Support/FileSystem.h +++ b/include/llvm/Support/FileSystem.h @@ -31,6 +31,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MD5.h" @@ -141,65 +142,48 @@ public: uint64_t getFile() const { return File; } }; -/// file_status - Represents the result of a call to stat and friends. It has -/// a platform-specific member to store the result. -class file_status -{ - friend bool equivalent(file_status A, file_status B); - +/// Represents the result of a call to directory_iterator::status(). This is a +/// subset of the information returned by a regular sys::fs::status() call, and +/// represents the information provided by Windows FileFirstFile/FindNextFile. +class basic_file_status { +protected: #if defined(LLVM_ON_UNIX) - dev_t fs_st_dev = 0; - nlink_t fs_st_nlinks = 0; - ino_t fs_st_ino = 0; time_t fs_st_atime = 0; time_t fs_st_mtime = 0; uid_t fs_st_uid = 0; gid_t fs_st_gid = 0; off_t fs_st_size = 0; #elif defined (LLVM_ON_WIN32) - uint32_t NumLinks = 0; uint32_t LastAccessedTimeHigh = 0; uint32_t LastAccessedTimeLow = 0; uint32_t LastWriteTimeHigh = 0; uint32_t LastWriteTimeLow = 0; - uint32_t VolumeSerialNumber = 0; uint32_t FileSizeHigh = 0; uint32_t FileSizeLow = 0; - uint32_t FileIndexHigh = 0; - uint32_t FileIndexLow = 0; #endif file_type Type = file_type::status_error; perms Perms = perms_not_known; public: - #if defined(LLVM_ON_UNIX) - file_status() = default; + basic_file_status() = default; - file_status(file_type Type) : Type(Type) {} + explicit basic_file_status(file_type Type) : Type(Type) {} - file_status(file_type Type, perms Perms, dev_t Dev, nlink_t Links, ino_t Ino, - time_t ATime, time_t MTime, uid_t UID, gid_t GID, off_t Size) - : fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino), fs_st_atime(ATime), - fs_st_mtime(MTime), fs_st_uid(UID), fs_st_gid(GID), fs_st_size(Size), - Type(Type), Perms(Perms) {} - #elif defined(LLVM_ON_WIN32) - file_status() = default; - - file_status(file_type Type) : Type(Type) {} - - file_status(file_type Type, perms Perms, uint32_t LinkCount, - uint32_t LastAccessTimeHigh, uint32_t LastAccessTimeLow, - uint32_t LastWriteTimeHigh, uint32_t LastWriteTimeLow, - uint32_t VolumeSerialNumber, uint32_t FileSizeHigh, - uint32_t FileSizeLow, uint32_t FileIndexHigh, - uint32_t FileIndexLow) - : NumLinks(LinkCount), LastAccessedTimeHigh(LastAccessTimeHigh), + #if defined(LLVM_ON_UNIX) + basic_file_status(file_type Type, perms Perms, time_t ATime, time_t MTime, + uid_t UID, gid_t GID, off_t Size) + : fs_st_atime(ATime), fs_st_mtime(MTime), fs_st_uid(UID), fs_st_gid(GID), + fs_st_size(Size), Type(Type), Perms(Perms) {} +#elif defined(LLVM_ON_WIN32) + basic_file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh, + uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh, + uint32_t LastWriteTimeLow, uint32_t FileSizeHigh, + uint32_t FileSizeLow) + : LastAccessedTimeHigh(LastAccessTimeHigh), LastAccessedTimeLow(LastAccessTimeLow), LastWriteTimeHigh(LastWriteTimeHigh), - LastWriteTimeLow(LastWriteTimeLow), - VolumeSerialNumber(VolumeSerialNumber), FileSizeHigh(FileSizeHigh), - FileSizeLow(FileSizeLow), FileIndexHigh(FileIndexHigh), - FileIndexLow(FileIndexLow), Type(Type), Perms(Perms) {} + LastWriteTimeLow(LastWriteTimeLow), FileSizeHigh(FileSizeHigh), + FileSizeLow(FileSizeLow), Type(Type), Perms(Perms) {} #endif // getters @@ -207,8 +191,6 @@ public: perms permissions() const { return Perms; } TimePoint<> getLastAccessedTime() const; TimePoint<> getLastModificationTime() const; - UniqueID getUniqueID() const; - uint32_t getLinkCount() const; #if defined(LLVM_ON_UNIX) uint32_t getUser() const { return fs_st_uid; } @@ -233,6 +215,49 @@ public: void permissions(perms p) { Perms = p; } }; +/// Represents the result of a call to sys::fs::status(). +class file_status : public basic_file_status { + friend bool equivalent(file_status A, file_status B); + + #if defined(LLVM_ON_UNIX) + dev_t fs_st_dev = 0; + nlink_t fs_st_nlinks = 0; + ino_t fs_st_ino = 0; + #elif defined (LLVM_ON_WIN32) + uint32_t NumLinks = 0; + uint32_t VolumeSerialNumber = 0; + uint32_t FileIndexHigh = 0; + uint32_t FileIndexLow = 0; + #endif + +public: + file_status() = default; + + explicit file_status(file_type Type) : basic_file_status(Type) {} + + #if defined(LLVM_ON_UNIX) + file_status(file_type Type, perms Perms, dev_t Dev, nlink_t Links, ino_t Ino, + time_t ATime, time_t MTime, uid_t UID, gid_t GID, off_t Size) + : basic_file_status(Type, Perms, ATime, MTime, UID, GID, Size), + fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino) {} + #elif defined(LLVM_ON_WIN32) + file_status(file_type Type, perms Perms, uint32_t LinkCount, + uint32_t LastAccessTimeHigh, uint32_t LastAccessTimeLow, + uint32_t LastWriteTimeHigh, uint32_t LastWriteTimeLow, + uint32_t VolumeSerialNumber, uint32_t FileSizeHigh, + uint32_t FileSizeLow, uint32_t FileIndexHigh, + uint32_t FileIndexLow) + : basic_file_status(Type, Perms, LastAccessTimeHigh, LastAccessTimeLow, + LastWriteTimeHigh, LastWriteTimeLow, FileSizeHigh, + FileSizeLow), + NumLinks(LinkCount), VolumeSerialNumber(VolumeSerialNumber), + FileIndexHigh(FileIndexHigh), FileIndexLow(FileIndexLow) {} + #endif + + UniqueID getUniqueID() const; + uint32_t getLinkCount() const; +}; + /// @} /// @name Physical Operators /// @{ @@ -343,7 +368,11 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting = true); /// platform-specific error code. std::error_code remove_directories(const Twine &path, bool IgnoreErrors = true); -/// @brief Rename \a from to \a to. Files are renamed as if by POSIX rename(). +/// @brief Rename \a from to \a to. +/// +/// Files are renamed as if by POSIX rename(), except that on Windows there may +/// be a short interval of time during which the destination file does not +/// exist. /// /// @param from The path to rename from. /// @param to The path to rename to. This is created. @@ -379,10 +408,10 @@ ErrorOr<MD5::MD5Result> md5_contents(const Twine &Path); /// @brief Does file exist? /// -/// @param status A file_status previously returned from stat. +/// @param status A basic_file_status previously returned from stat. /// @returns True if the file represented by status exists, false if it does /// not. -bool exists(file_status status); +bool exists(const basic_file_status &status); enum class AccessMode { Exist, Write, Execute }; @@ -481,9 +510,9 @@ file_type get_file_type(const Twine &Path, bool Follow = true); /// @brief Does status represent a directory? /// -/// @param status A file_status previously returned from status. +/// @param status A basic_file_status previously returned from status. /// @returns status.type() == file_type::directory_file. -bool is_directory(file_status status); +bool is_directory(const basic_file_status &status); /// @brief Is path a directory? /// @@ -503,9 +532,9 @@ inline bool is_directory(const Twine &Path) { /// @brief Does status represent a regular file? /// -/// @param status A file_status previously returned from status. +/// @param status A basic_file_status previously returned from status. /// @returns status_known(status) && status.type() == file_type::regular_file. -bool is_regular_file(file_status status); +bool is_regular_file(const basic_file_status &status); /// @brief Is path a regular file? /// @@ -527,9 +556,9 @@ inline bool is_regular_file(const Twine &Path) { /// @brief Does status represent a symlink file? /// -/// @param status A file_status previously returned from status. +/// @param status A basic_file_status previously returned from status. /// @returns status_known(status) && status.type() == file_type::symlink_file. -bool is_symlink_file(file_status status); +bool is_symlink_file(const basic_file_status &status); /// @brief Is path a symlink file? /// @@ -552,9 +581,9 @@ inline bool is_symlink_file(const Twine &Path) { /// @brief Does this status represent something that exists but is not a /// directory or regular file? /// -/// @param status A file_status previously returned from status. +/// @param status A basic_file_status previously returned from status. /// @returns exists(s) && !is_regular_file(s) && !is_directory(s) -bool is_other(file_status status); +bool is_other(const basic_file_status &status); /// @brief Is path something that exists but is not a directory, /// regular file, or symlink? @@ -627,7 +656,7 @@ std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time); /// /// @param s Input file status. /// @returns True if status() != status_error. -bool status_known(file_status s); +bool status_known(const basic_file_status &s); /// @brief Is status available? /// @@ -637,6 +666,29 @@ bool status_known(file_status s); /// platform-specific error_code. std::error_code status_known(const Twine &path, bool &result); +enum OpenFlags : unsigned { + F_None = 0, + + /// F_Excl - When opening a file, this flag makes raw_fd_ostream + /// report an error if the file already exists. + F_Excl = 1, + + /// F_Append - When opening a file, if it already exists append to the + /// existing file instead of returning an error. This may not be specified + /// with F_Excl. + F_Append = 2, + + /// The file should be opened in text mode on platforms that make this + /// distinction. + F_Text = 4, + + /// Open the file for read and write. + F_RW = 8, + + /// Delete the file on close. Only makes a difference on windows. + F_Delete = 16 +}; + /// @brief Create a uniquely named file. /// /// Generates a unique path suitable for a temporary file and then opens it as a @@ -660,12 +712,51 @@ std::error_code status_known(const Twine &path, bool &result); /// otherwise a platform-specific error_code. std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl<char> &ResultPath, - unsigned Mode = all_read | all_write); + unsigned Mode = all_read | all_write, + sys::fs::OpenFlags Flags = sys::fs::F_RW); /// @brief Simpler version for clients that don't want an open file. std::error_code createUniqueFile(const Twine &Model, SmallVectorImpl<char> &ResultPath); +/// Represents a temporary file. +/// +/// The temporary file must be eventually discarded or given a final name and +/// kept. +/// +/// The destructor doesn't implicitly discard because there is no way to +/// properly handle errors in a destructor. +class TempFile { + bool Done = false; + TempFile(StringRef Name, int FD); + +public: + /// This creates a temporary file with createUniqueFile and schedules it for + /// deletion with sys::RemoveFileOnSignal. + static Expected<TempFile> create(const Twine &Model, + unsigned Mode = all_read | all_write); + TempFile(TempFile &&Other); + TempFile &operator=(TempFile &&Other); + + // Name of the temporary file. + std::string TmpName; + + // The open file descriptor. + int FD = -1; + + // Keep this with the given name. + Error keep(const Twine &Name); + + // Keep this with the temporary name. + Error keep(); + + // Delete the file. + Error discard(); + + // This checks that keep or delete was called. + ~TempFile(); +}; + /// @brief Create a file in the system temporary directory. /// /// The filename is of the form prefix-random_chars.suffix. Since the directory @@ -676,7 +767,8 @@ std::error_code createUniqueFile(const Twine &Model, /// running the assembler. std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, int &ResultFD, - SmallVectorImpl<char> &ResultPath); + SmallVectorImpl<char> &ResultPath, + sys::fs::OpenFlags Flags = sys::fs::F_RW); /// @brief Simpler version for clients that don't want an open file. std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, @@ -685,32 +777,6 @@ std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, std::error_code createUniqueDirectory(const Twine &Prefix, SmallVectorImpl<char> &ResultPath); -/// @brief Fetch a path to an open file, as specified by a file descriptor -/// -/// @param FD File descriptor to a currently open file -/// @param ResultPath The buffer into which to write the path -std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath); - -enum OpenFlags : unsigned { - F_None = 0, - - /// F_Excl - When opening a file, this flag makes raw_fd_ostream - /// report an error if the file already exists. - F_Excl = 1, - - /// F_Append - When opening a file, if it already exists append to the - /// existing file instead of returning an error. This may not be specified - /// with F_Excl. - F_Append = 2, - - /// The file should be opened in text mode on platforms that make this - /// distinction. - F_Text = 4, - - /// Open the file for read and write. - F_RW = 8 -}; - inline OpenFlags operator|(OpenFlags A, OpenFlags B) { return OpenFlags(unsigned(A) | unsigned(B)); } @@ -751,7 +817,7 @@ public: private: /// Platform-specific mapping state. - uint64_t Size; + size_t Size; void *Mapping; std::error_code init(int FD, uint64_t Offset, mapmode Mode); @@ -764,12 +830,12 @@ public: /// \param fd An open file descriptor to map. mapped_file_region takes /// ownership if closefd is true. It must have been opended in the correct /// mode. - mapped_file_region(int fd, mapmode mode, uint64_t length, uint64_t offset, + mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset, std::error_code &ec); ~mapped_file_region(); - uint64_t size() const; + size_t size() const; char *data() const; /// Get a const view of the data. Modifying this memory has undefined @@ -795,24 +861,25 @@ std::string getMainExecutable(const char *argv0, void *MainExecAddr); class directory_entry { std::string Path; bool FollowSymlinks; - mutable file_status Status; + basic_file_status Status; public: explicit directory_entry(const Twine &path, bool follow_symlinks = true, - file_status st = file_status()) + basic_file_status st = basic_file_status()) : Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} directory_entry() = default; - void assign(const Twine &path, file_status st = file_status()) { + void assign(const Twine &path, basic_file_status st = basic_file_status()) { Path = path.str(); Status = st; } - void replace_filename(const Twine &filename, file_status st = file_status()); + void replace_filename(const Twine &filename, + basic_file_status st = basic_file_status()); const std::string &path() const { return Path; } - std::error_code status(file_status &result) const; + ErrorOr<basic_file_status> status() const; bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; } bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); } @@ -931,9 +998,9 @@ public: if (State->HasNoPushRequest) State->HasNoPushRequest = false; else { - file_status st; - if ((ec = State->Stack.top()->status(st))) return *this; - if (is_directory(st)) { + ErrorOr<basic_file_status> st = State->Stack.top()->status(); + if (!st) return *this; + if (is_directory(*st)) { State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow)); if (ec) return *this; if (State->Stack.top() != end_itr) { diff --git a/include/llvm/Support/FormatVariadic.h b/include/llvm/Support/FormatVariadic.h index 408c6d8b2e0d..8c08a7d9488f 100644 --- a/include/llvm/Support/FormatVariadic.h +++ b/include/llvm/Support/FormatVariadic.h @@ -230,9 +230,8 @@ public: // For a given parameter of type T, the following steps are executed in order // until a match is found: // -// 1. If the parameter is of class type, and contains a method -// void format(raw_ostream &Stream, StringRef Options) -// Then this method is invoked to produce the formatted output. The +// 1. If the parameter is of class type, and inherits from format_adapter, +// Then format() is invoked on it to produce the formatted output. The // implementation should write the formatted text into `Stream`. // 2. If there is a suitable template specialization of format_provider<> // for type T containing a method whose signature is: @@ -259,6 +258,13 @@ inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object<decltype( std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...)); } +// Allow a formatv_object to be formatted (no options supported). +template <typename T> struct format_provider<formatv_object<T>> { + static void format(const formatv_object<T> &V, raw_ostream &OS, StringRef) { + OS << V; + } +}; + } // end namespace llvm #endif // LLVM_SUPPORT_FORMATVARIADIC_H diff --git a/include/llvm/Support/FormatVariadicDetails.h b/include/llvm/Support/FormatVariadicDetails.h index b4a564ffc26c..9b60462209dc 100644 --- a/include/llvm/Support/FormatVariadicDetails.h +++ b/include/llvm/Support/FormatVariadicDetails.h @@ -31,7 +31,7 @@ template <typename T> class provider_format_adapter : public format_adapter { T Item; public: - explicit provider_format_adapter(T &&Item) : Item(Item) {} + explicit provider_format_adapter(T &&Item) : Item(std::forward<T>(Item)) {} void format(llvm::raw_ostream &S, StringRef Options) override { format_provider<typename std::decay<T>::type>::format(Item, S, Options); diff --git a/include/llvm/Support/GCOV.h b/include/llvm/Support/GCOV.h deleted file mode 100644 index 02016e7dbd62..000000000000 --- a/include/llvm/Support/GCOV.h +++ /dev/null @@ -1,460 +0,0 @@ -//===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This header provides the interface to read and write coverage files that -// use 'gcov' format. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_SUPPORT_GCOV_H -#define LLVM_SUPPORT_GCOV_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/iterator.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <memory> -#include <string> -#include <utility> - -namespace llvm { - -class GCOVFunction; -class GCOVBlock; -class FileInfo; - -namespace GCOV { - -enum GCOVVersion { V402, V404, V704 }; - -/// \brief A struct for passing gcov options between functions. -struct Options { - Options(bool A, bool B, bool C, bool F, bool P, bool U, bool L, bool N) - : AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F), - PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N) {} - - bool AllBlocks; - bool BranchInfo; - bool BranchCount; - bool FuncCoverage; - bool PreservePaths; - bool UncondBranch; - bool LongFileNames; - bool NoOutput; -}; - -} // end namespace GCOV - -/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific -/// read operations. -class GCOVBuffer { -public: - GCOVBuffer(MemoryBuffer *B) : Buffer(B) {} - - /// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer. - bool readGCNOFormat() { - StringRef File = Buffer->getBuffer().slice(0, 4); - if (File != "oncg") { - errs() << "Unexpected file type: " << File << ".\n"; - return false; - } - Cursor = 4; - return true; - } - - /// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer. - bool readGCDAFormat() { - StringRef File = Buffer->getBuffer().slice(0, 4); - if (File != "adcg") { - errs() << "Unexpected file type: " << File << ".\n"; - return false; - } - Cursor = 4; - return true; - } - - /// readGCOVVersion - Read GCOV version. - bool readGCOVVersion(GCOV::GCOVVersion &Version) { - StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (VersionStr == "*204") { - Cursor += 4; - Version = GCOV::V402; - return true; - } - if (VersionStr == "*404") { - Cursor += 4; - Version = GCOV::V404; - return true; - } - if (VersionStr == "*704") { - Cursor += 4; - Version = GCOV::V704; - return true; - } - errs() << "Unexpected version: " << VersionStr << ".\n"; - return false; - } - - /// readFunctionTag - If cursor points to a function tag then increment the - /// cursor and return true otherwise return false. - bool readFunctionTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || - Tag[3] != '\1') { - return false; - } - Cursor += 4; - return true; - } - - /// readBlockTag - If cursor points to a block tag then increment the - /// cursor and return true otherwise return false. - bool readBlockTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' || - Tag[3] != '\x01') { - return false; - } - Cursor += 4; - return true; - } - - /// readEdgeTag - If cursor points to an edge tag then increment the - /// cursor and return true otherwise return false. - bool readEdgeTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' || - Tag[3] != '\x01') { - return false; - } - Cursor += 4; - return true; - } - - /// readLineTag - If cursor points to a line tag then increment the - /// cursor and return true otherwise return false. - bool readLineTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' || - Tag[3] != '\x01') { - return false; - } - Cursor += 4; - return true; - } - - /// readArcTag - If cursor points to an gcda arc tag then increment the - /// cursor and return true otherwise return false. - bool readArcTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' || - Tag[3] != '\1') { - return false; - } - Cursor += 4; - return true; - } - - /// readObjectTag - If cursor points to an object summary tag then increment - /// the cursor and return true otherwise return false. - bool readObjectTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || - Tag[3] != '\xa1') { - return false; - } - Cursor += 4; - return true; - } - - /// readProgramTag - If cursor points to a program summary tag then increment - /// the cursor and return true otherwise return false. - bool readProgramTag() { - StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4); - if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' || - Tag[3] != '\xa3') { - return false; - } - Cursor += 4; - return true; - } - - bool readInt(uint32_t &Val) { - if (Buffer->getBuffer().size() < Cursor + 4) { - errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n"; - return false; - } - StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4); - Cursor += 4; - Val = *(const uint32_t *)(Str.data()); - return true; - } - - bool readInt64(uint64_t &Val) { - uint32_t Lo, Hi; - if (!readInt(Lo) || !readInt(Hi)) - return false; - Val = ((uint64_t)Hi << 32) | Lo; - return true; - } - - bool readString(StringRef &Str) { - uint32_t Len = 0; - // Keep reading until we find a non-zero length. This emulates gcov's - // behaviour, which appears to do the same. - while (Len == 0) - if (!readInt(Len)) - return false; - Len *= 4; - if (Buffer->getBuffer().size() < Cursor + Len) { - errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n"; - return false; - } - Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first; - Cursor += Len; - return true; - } - - uint64_t getCursor() const { return Cursor; } - void advanceCursor(uint32_t n) { Cursor += n * 4; } - -private: - MemoryBuffer *Buffer; - uint64_t Cursor = 0; -}; - -/// GCOVFile - Collects coverage information for one pair of coverage file -/// (.gcno and .gcda). -class GCOVFile { -public: - GCOVFile() = default; - - bool readGCNO(GCOVBuffer &Buffer); - bool readGCDA(GCOVBuffer &Buffer); - uint32_t getChecksum() const { return Checksum; } - void print(raw_ostream &OS) const; - void dump() const; - void collectLineCounts(FileInfo &FI); - -private: - bool GCNOInitialized = false; - GCOV::GCOVVersion Version; - uint32_t Checksum = 0; - SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions; - uint32_t RunCount = 0; - uint32_t ProgramCount = 0; -}; - -/// GCOVEdge - Collects edge information. -struct GCOVEdge { - GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {} - - GCOVBlock &Src; - GCOVBlock &Dst; - uint64_t Count = 0; -}; - -/// GCOVFunction - Collects function information. -class GCOVFunction { -public: - using BlockIterator = pointee_iterator<SmallVectorImpl< - std::unique_ptr<GCOVBlock>>::const_iterator>; - - GCOVFunction(GCOVFile &P) : Parent(P) {} - - bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version); - bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version); - StringRef getName() const { return Name; } - StringRef getFilename() const { return Filename; } - size_t getNumBlocks() const { return Blocks.size(); } - uint64_t getEntryCount() const; - uint64_t getExitCount() const; - - BlockIterator block_begin() const { return Blocks.begin(); } - BlockIterator block_end() const { return Blocks.end(); } - iterator_range<BlockIterator> blocks() const { - return make_range(block_begin(), block_end()); - } - - void print(raw_ostream &OS) const; - void dump() const; - void collectLineCounts(FileInfo &FI); - -private: - GCOVFile &Parent; - uint32_t Ident = 0; - uint32_t Checksum; - uint32_t LineNumber = 0; - StringRef Name; - StringRef Filename; - SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks; - SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges; -}; - -/// GCOVBlock - Collects block information. -class GCOVBlock { - struct EdgeWeight { - EdgeWeight(GCOVBlock *D) : Dst(D) {} - - GCOVBlock *Dst; - uint64_t Count = 0; - }; - - struct SortDstEdgesFunctor { - bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) { - return E1->Dst.Number < E2->Dst.Number; - } - }; - -public: - using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator; - - GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {} - ~GCOVBlock(); - - const GCOVFunction &getParent() const { return Parent; } - void addLine(uint32_t N) { Lines.push_back(N); } - uint32_t getLastLine() const { return Lines.back(); } - void addCount(size_t DstEdgeNo, uint64_t N); - uint64_t getCount() const { return Counter; } - - void addSrcEdge(GCOVEdge *Edge) { - assert(&Edge->Dst == this); // up to caller to ensure edge is valid - SrcEdges.push_back(Edge); - } - - void addDstEdge(GCOVEdge *Edge) { - assert(&Edge->Src == this); // up to caller to ensure edge is valid - // Check if adding this edge causes list to become unsorted. - if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number) - DstEdgesAreSorted = false; - DstEdges.push_back(Edge); - } - - size_t getNumSrcEdges() const { return SrcEdges.size(); } - size_t getNumDstEdges() const { return DstEdges.size(); } - void sortDstEdges(); - - EdgeIterator src_begin() const { return SrcEdges.begin(); } - EdgeIterator src_end() const { return SrcEdges.end(); } - iterator_range<EdgeIterator> srcs() const { - return make_range(src_begin(), src_end()); - } - - EdgeIterator dst_begin() const { return DstEdges.begin(); } - EdgeIterator dst_end() const { return DstEdges.end(); } - iterator_range<EdgeIterator> dsts() const { - return make_range(dst_begin(), dst_end()); - } - - void print(raw_ostream &OS) const; - void dump() const; - void collectLineCounts(FileInfo &FI); - -private: - GCOVFunction &Parent; - uint32_t Number; - uint64_t Counter = 0; - bool DstEdgesAreSorted = true; - SmallVector<GCOVEdge *, 16> SrcEdges; - SmallVector<GCOVEdge *, 16> DstEdges; - SmallVector<uint32_t, 16> Lines; -}; - -class FileInfo { - // It is unlikely--but possible--for multiple functions to be on the same - // line. - // Therefore this typedef allows LineData.Functions to store multiple - // functions - // per instance. This is rare, however, so optimize for the common case. - using FunctionVector = SmallVector<const GCOVFunction *, 1>; - using FunctionLines = DenseMap<uint32_t, FunctionVector>; - using BlockVector = SmallVector<const GCOVBlock *, 4>; - using BlockLines = DenseMap<uint32_t, BlockVector>; - - struct LineData { - LineData() = default; - - BlockLines Blocks; - FunctionLines Functions; - uint32_t LastLine = 0; - }; - - struct GCOVCoverage { - GCOVCoverage(StringRef Name) : Name(Name) {} - - StringRef Name; - - uint32_t LogicalLines = 0; - uint32_t LinesExec = 0; - - uint32_t Branches = 0; - uint32_t BranchesExec = 0; - uint32_t BranchesTaken = 0; - }; - -public: - FileInfo(const GCOV::Options &Options) : Options(Options) {} - - void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) { - if (Line > LineInfo[Filename].LastLine) - LineInfo[Filename].LastLine = Line; - LineInfo[Filename].Blocks[Line - 1].push_back(Block); - } - - void addFunctionLine(StringRef Filename, uint32_t Line, - const GCOVFunction *Function) { - if (Line > LineInfo[Filename].LastLine) - LineInfo[Filename].LastLine = Line; - LineInfo[Filename].Functions[Line - 1].push_back(Function); - } - - void setRunCount(uint32_t Runs) { RunCount = Runs; } - void setProgramCount(uint32_t Programs) { ProgramCount = Programs; } - void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile, - StringRef GCDAFile); - -private: - std::string getCoveragePath(StringRef Filename, StringRef MainFilename); - std::unique_ptr<raw_ostream> openCoveragePath(StringRef CoveragePath); - void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const; - void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block, - uint32_t LineIndex, uint32_t &BlockNo) const; - void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block, - GCOVCoverage &Coverage, uint32_t &EdgeNo); - void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo, - uint64_t Count) const; - - void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const; - void printFuncCoverage(raw_ostream &OS) const; - void printFileCoverage(raw_ostream &OS) const; - - const GCOV::Options &Options; - StringMap<LineData> LineInfo; - uint32_t RunCount = 0; - uint32_t ProgramCount = 0; - - using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>; - using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>; - - FileCoverageList FileCoverages; - FuncCoverageMap FuncCoverages; -}; - -} // end namespace llvm - -#endif // LLVM_SUPPORT_GCOV_H diff --git a/include/llvm/Support/GenericDomTree.h b/include/llvm/Support/GenericDomTree.h index 706320fed9a7..635c87a106f0 100644 --- a/include/llvm/Support/GenericDomTree.h +++ b/include/llvm/Support/GenericDomTree.h @@ -14,8 +14,8 @@ /// graph types. /// /// Unlike ADT/* graph algorithms, generic dominator tree has more requirements -/// on the graph's NodeRef. The NodeRef should be a pointer and, depending on -/// the implementation, e.g. NodeRef->getParent() return the parent node. +/// on the graph's NodeRef. The NodeRef should be a pointer and, +/// NodeRef->getParent() must return the parent node that is also a pointer. /// /// FIXME: Maybe GenericDomTree needs a TreeTraits, instead of GraphTraits. /// @@ -24,12 +24,6 @@ #ifndef LLVM_SUPPORT_GENERICDOMTREE_H #define LLVM_SUPPORT_GENERICDOMTREE_H -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/GraphTraits.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> #include <cstddef> @@ -38,6 +32,13 @@ #include <type_traits> #include <utility> #include <vector> +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { @@ -45,7 +46,7 @@ template <typename NodeT, bool IsPostDom> class DominatorTreeBase; namespace DomTreeBuilder { -template <class DomTreeT> +template <typename DomTreeT> struct SemiNCAInfo; } // namespace DomTreeBuilder @@ -187,17 +188,51 @@ void PrintDomTree(const DomTreeNodeBase<NodeT> *N, raw_ostream &O, namespace DomTreeBuilder { // The routines below are provided in a separate header but referenced here. -template <typename DomTreeT, typename FuncT> -void Calculate(DomTreeT &DT, FuncT &F); +template <typename DomTreeT> +void Calculate(DomTreeT &DT); -template <class DomTreeT> +template <typename DomTreeT> void InsertEdge(DomTreeT &DT, typename DomTreeT::NodePtr From, typename DomTreeT::NodePtr To); -template <class DomTreeT> +template <typename DomTreeT> void DeleteEdge(DomTreeT &DT, typename DomTreeT::NodePtr From, typename DomTreeT::NodePtr To); +// UpdateKind and Update are used by the batch update API and it's easiest to +// define them here. +enum class UpdateKind : unsigned char { Insert, Delete }; + +template <typename NodePtr> +struct Update { + using NodeKindPair = PointerIntPair<NodePtr, 1, UpdateKind>; + + NodePtr From; + NodeKindPair ToAndKind; + + Update(UpdateKind Kind, NodePtr From, NodePtr To) + : From(From), ToAndKind(To, Kind) {} + + UpdateKind getKind() const { return ToAndKind.getInt(); } + NodePtr getFrom() const { return From; } + NodePtr getTo() const { return ToAndKind.getPointer(); } + bool operator==(const Update &RHS) const { + return From == RHS.From && ToAndKind == RHS.ToAndKind; + } + + friend raw_ostream &operator<<(raw_ostream &OS, const Update &U) { + OS << (U.getKind() == UpdateKind::Insert ? "Insert " : "Delete "); + U.getFrom()->printAsOperand(OS, false); + OS << " -> "; + U.getTo()->printAsOperand(OS, false); + return OS; + } +}; + +template <typename DomTreeT> +void ApplyUpdates(DomTreeT &DT, + ArrayRef<typename DomTreeT::UpdateType> Updates); + template <typename DomTreeT> bool Verify(const DomTreeT &DT); } // namespace DomTreeBuilder @@ -208,14 +243,30 @@ bool Verify(const DomTreeT &DT); /// various graphs in the LLVM IR or in the code generator. template <typename NodeT, bool IsPostDom> class DominatorTreeBase { + public: + static_assert(std::is_pointer<typename GraphTraits<NodeT *>::NodeRef>::value, + "Currently DominatorTreeBase supports only pointer nodes"); + using NodeType = NodeT; + using NodePtr = NodeT *; + using ParentPtr = decltype(std::declval<NodeT *>()->getParent()); + static_assert(std::is_pointer<ParentPtr>::value, + "Currently NodeT's parent must be a pointer type"); + using ParentType = typename std::remove_pointer<ParentPtr>::type; + static constexpr bool IsPostDominator = IsPostDom; + + using UpdateType = DomTreeBuilder::Update<NodePtr>; + using UpdateKind = DomTreeBuilder::UpdateKind; + static constexpr UpdateKind Insert = UpdateKind::Insert; + static constexpr UpdateKind Delete = UpdateKind::Delete; + protected: - std::vector<NodeT *> Roots; + // Dominators always have a single root, postdominators can have more. + SmallVector<NodeT *, IsPostDom ? 4 : 1> Roots; using DomTreeNodeMapType = DenseMap<NodeT *, std::unique_ptr<DomTreeNodeBase<NodeT>>>; DomTreeNodeMapType DomTreeNodes; DomTreeNodeBase<NodeT> *RootNode; - using ParentPtr = decltype(std::declval<NodeT *>()->getParent()); ParentPtr Parent = nullptr; mutable bool DFSInfoValid = false; @@ -224,12 +275,6 @@ class DominatorTreeBase { friend struct DomTreeBuilder::SemiNCAInfo<DominatorTreeBase>; public: - static_assert(std::is_pointer<typename GraphTraits<NodeT *>::NodeRef>::value, - "Currently DominatorTreeBase supports only pointer nodes"); - using NodeType = NodeT; - using NodePtr = NodeT *; - static constexpr bool IsPostDominator = IsPostDom; - DominatorTreeBase() {} DominatorTreeBase(DominatorTreeBase &&Arg) @@ -260,7 +305,7 @@ class DominatorTreeBase { /// multiple blocks if we are computing post dominators. For forward /// dominators, this will always be a single block (the entry node). /// - const std::vector<NodeT *> &getRoots() const { return Roots; } + const SmallVectorImpl<NodeT *> &getRoots() const { return Roots; } /// isPostDominator - Returns true if analysis based of postdoms /// @@ -412,14 +457,15 @@ class DominatorTreeBase { } /// findNearestCommonDominator - Find nearest common dominator basic block - /// for basic block A and B. If there is no such block then return NULL. + /// for basic block A and B. If there is no such block then return nullptr. NodeT *findNearestCommonDominator(NodeT *A, NodeT *B) const { + assert(A && B && "Pointers are not valid"); assert(A->getParent() == B->getParent() && "Two blocks are not in same function"); // If either A or B is a entry block then it is nearest common dominator // (for forward-dominators). - if (!this->isPostDominator()) { + if (!isPostDominator()) { NodeT &Entry = A->getParent()->front(); if (A == &Entry || B == &Entry) return &Entry; @@ -457,6 +503,41 @@ class DominatorTreeBase { // API to update (Post)DominatorTree information based on modifications to // the CFG... + /// Inform the dominator tree about a sequence of CFG edge insertions and + /// deletions and perform a batch update on the tree. + /// + /// This function should be used when there were multiple CFG updates after + /// the last dominator tree update. It takes care of performing the updates + /// in sync with the CFG and optimizes away the redundant operations that + /// cancel each other. + /// The functions expects the sequence of updates to be balanced. Eg.: + /// - {{Insert, A, B}, {Delete, A, B}, {Insert, A, B}} is fine, because + /// logically it results in a single insertions. + /// - {{Insert, A, B}, {Insert, A, B}} is invalid, because it doesn't make + /// sense to insert the same edge twice. + /// + /// What's more, the functions assumes that it's safe to ask every node in the + /// CFG about its children and inverse children. This implies that deletions + /// of CFG edges must not delete the CFG nodes before calling this function. + /// + /// Batch updates should be generally faster when performing longer sequences + /// of updates than calling insertEdge/deleteEdge manually multiple times, as + /// it can reorder the updates and remove redundant ones internally. + /// The batch updater is also able to detect sequences of zero and exactly one + /// update -- it's optimized to do less work in these cases. + /// + /// Note that for postdominators it automatically takes care of applying + /// updates on reverse edges internally (so there's no need to swap the + /// From and To pointers when constructing DominatorTree::UpdateType). + /// The type of updates is the same for DomTreeBase<T> and PostDomTreeBase<T> + /// with the same template parameter T. + /// + /// \param Updates An unordered sequence of updates to perform. + /// + void applyUpdates(ArrayRef<UpdateType> Updates) { + DomTreeBuilder::ApplyUpdates(*this, Updates); + } + /// Inform the dominator tree about a CFG edge insertion and update the tree. /// /// This function has to be called just before or just after making the update @@ -476,11 +557,10 @@ class DominatorTreeBase { /// Inform the dominator tree about a CFG edge deletion and update the tree. /// - /// This function has to be called just after making the update - /// on the actual CFG. There cannot be any other updates that the dominator - /// tree doesn't know about. The only exception is when the deletion that the - /// tree is informed about makes some (domominator) subtree unreachable -- in - /// this case, it is fine to perform deletions within this subtree. + /// This function has to be called just after making the update on the actual + /// CFG. An internal functions checks if the edge doesn't exist in the CFG in + /// DEBUG mode. There cannot be any other updates that the + /// dominator tree doesn't know about. /// /// Note that for postdominators it automatically takes care of deleting /// a reverse edge internally (so there's no need to swap the parameters). @@ -559,11 +639,12 @@ class DominatorTreeBase { assert(Node && "Removing node that isn't in dominator tree."); assert(Node->getChildren().empty() && "Node is not a leaf node."); + DFSInfoValid = false; + // Remove node from immediate dominator's children list. DomTreeNodeBase<NodeT> *IDom = Node->getIDom(); if (IDom) { - typename std::vector<DomTreeNodeBase<NodeT> *>::iterator I = - find(IDom->Children, Node); + const auto I = find(IDom->Children, Node); assert(I != IDom->Children.end() && "Not in immediate dominator children set!"); // I am no longer your child... @@ -571,6 +652,15 @@ class DominatorTreeBase { } DomTreeNodes.erase(BB); + + if (!IsPostDom) return; + + // Remember to update PostDominatorTree roots. + auto RIt = llvm::find(Roots, BB); + if (RIt != Roots.end()) { + std::swap(*RIt, Roots.back()); + Roots.pop_back(); + } } /// splitBlock - BB is split and now it has one successor. Update dominator @@ -586,7 +676,7 @@ class DominatorTreeBase { /// void print(raw_ostream &O) const { O << "=============================--------------------------------\n"; - if (this->isPostDominator()) + if (IsPostDominator) O << "Inorder PostDominator Tree: "; else O << "Inorder Dominator Tree: "; @@ -596,6 +686,14 @@ class DominatorTreeBase { // The postdom tree can have a null root if there are no returns. if (getRootNode()) PrintDomTree<NodeT>(getRootNode(), O, 1); + if (IsPostDominator) { + O << "Roots: "; + for (const NodePtr Block : Roots) { + Block->printAsOperand(O, false); + O << " "; + } + O << "\n"; + } } public: @@ -607,28 +705,25 @@ public: return; } - unsigned DFSNum = 0; - SmallVector<std::pair<const DomTreeNodeBase<NodeT> *, typename DomTreeNodeBase<NodeT>::const_iterator>, 32> WorkStack; const DomTreeNodeBase<NodeT> *ThisRoot = getRootNode(); - + assert((!Parent || ThisRoot) && "Empty constructed DomTree"); if (!ThisRoot) return; - // Even in the case of multiple exits that form the post dominator root - // nodes, do not iterate over all exits, but start from the virtual root - // node. Otherwise bbs, that are not post dominated by any exit but by the - // virtual root node, will never be assigned a DFS number. - WorkStack.push_back(std::make_pair(ThisRoot, ThisRoot->begin())); + // Both dominators and postdominators have a single root node. In the case + // case of PostDominatorTree, this node is a virtual root. + WorkStack.push_back({ThisRoot, ThisRoot->begin()}); + + unsigned DFSNum = 0; ThisRoot->DFSNumIn = DFSNum++; while (!WorkStack.empty()) { const DomTreeNodeBase<NodeT> *Node = WorkStack.back().first; - typename DomTreeNodeBase<NodeT>::const_iterator ChildIt = - WorkStack.back().second; + const auto ChildIt = WorkStack.back().second; // If we visited all of the children of this node, "recurse" back up the // stack setting the DFOutNum. @@ -640,7 +735,7 @@ public: const DomTreeNodeBase<NodeT> *Child = *ChildIt; ++WorkStack.back().second; - WorkStack.push_back(std::make_pair(Child, Child->begin())); + WorkStack.push_back({Child, Child->begin()}); Child->DFSNumIn = DFSNum++; } } @@ -650,23 +745,9 @@ public: } /// recalculate - compute a dominator tree for the given function - template <class FT> void recalculate(FT &F) { - using TraitsTy = GraphTraits<FT *>; - reset(); - Parent = &F; - - if (!IsPostDominator) { - // Initialize root - NodeT *entry = TraitsTy::getEntryNode(&F); - addRoot(entry); - } else { - // Initialize the roots list - for (auto *Node : nodes(&F)) - if (TraitsTy::child_begin(Node) == TraitsTy::child_end(Node)) - addRoot(Node); - } - - DomTreeBuilder::Calculate(*this, F); + void recalculate(ParentType &Func) { + Parent = &Func; + DomTreeBuilder::Calculate(*this); } /// verify - check parent and sibling property diff --git a/include/llvm/Support/GenericDomTreeConstruction.h b/include/llvm/Support/GenericDomTreeConstruction.h index be90afa4c3c8..8f801662d0fb 100644 --- a/include/llvm/Support/GenericDomTreeConstruction.h +++ b/include/llvm/Support/GenericDomTreeConstruction.h @@ -21,8 +21,8 @@ /// faster than the almost-linear O(n*alpha(n)) version, even for large CFGs. /// /// The file uses the Depth Based Search algorithm to perform incremental -/// upates (insertion and deletions). The implemented algorithm is based on this -/// publication: +/// updates (insertion and deletions). The implemented algorithm is based on +/// this publication: /// /// An Experimental Study of Dynamic Dominators /// Loukas Georgiadis, et al., April 12 2016, pp. 5-7, 9-10: @@ -34,8 +34,10 @@ #define LLVM_SUPPORT_GENERICDOMTREECONSTRUCTION_H #include <queue> +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Debug.h" #include "llvm/Support/GenericDomTree.h" @@ -45,25 +47,12 @@ namespace llvm { namespace DomTreeBuilder { -template <typename NodePtr, bool Inverse> -struct ChildrenGetter { - static auto Get(NodePtr N) -> decltype(reverse(children<NodePtr>(N))) { - return reverse(children<NodePtr>(N)); - } -}; - -template <typename NodePtr> -struct ChildrenGetter<NodePtr, true> { - static auto Get(NodePtr N) -> decltype(inverse_children<NodePtr>(N)) { - return inverse_children<NodePtr>(N); - } -}; - template <typename DomTreeT> struct SemiNCAInfo { using NodePtr = typename DomTreeT::NodePtr; using NodeT = typename DomTreeT::NodeType; using TreeNodePtr = DomTreeNodeBase<NodeT> *; + using RootsT = decltype(DomTreeT::Roots); static constexpr bool IsPostDom = DomTreeT::IsPostDominator; // Information record used by Semi-NCA during tree construction. @@ -81,11 +70,99 @@ struct SemiNCAInfo { std::vector<NodePtr> NumToNode = {nullptr}; DenseMap<NodePtr, InfoRec> NodeToInfo; + using UpdateT = typename DomTreeT::UpdateType; + struct BatchUpdateInfo { + SmallVector<UpdateT, 4> Updates; + using NodePtrAndKind = PointerIntPair<NodePtr, 1, UpdateKind>; + + // In order to be able to walk a CFG that is out of sync with the CFG + // DominatorTree last knew about, use the list of updates to reconstruct + // previous CFG versions of the current CFG. For each node, we store a set + // of its virtually added/deleted future successors and predecessors. + // Note that these children are from the future relative to what the + // DominatorTree knows about -- using them to gets us some snapshot of the + // CFG from the past (relative to the state of the CFG). + DenseMap<NodePtr, SmallDenseSet<NodePtrAndKind, 4>> FutureSuccessors; + DenseMap<NodePtr, SmallDenseSet<NodePtrAndKind, 4>> FuturePredecessors; + // Remembers if the whole tree was recalculated at some point during the + // current batch update. + bool IsRecalculated = false; + }; + + BatchUpdateInfo *BatchUpdates; + using BatchUpdatePtr = BatchUpdateInfo *; + + // If BUI is a nullptr, then there's no batch update in progress. + SemiNCAInfo(BatchUpdatePtr BUI) : BatchUpdates(BUI) {} + void clear() { NumToNode = {nullptr}; // Restore to initial state with a dummy start node. NodeToInfo.clear(); + // Don't reset the pointer to BatchUpdateInfo here -- if there's an update + // in progress, we need this information to continue it. } + template <bool Inverse> + struct ChildrenGetter { + using ResultTy = SmallVector<NodePtr, 8>; + + static ResultTy Get(NodePtr N, std::integral_constant<bool, false>) { + auto RChildren = reverse(children<NodePtr>(N)); + return ResultTy(RChildren.begin(), RChildren.end()); + } + + static ResultTy Get(NodePtr N, std::integral_constant<bool, true>) { + auto IChildren = inverse_children<NodePtr>(N); + return ResultTy(IChildren.begin(), IChildren.end()); + } + + using Tag = std::integral_constant<bool, Inverse>; + + // The function below is the core part of the batch updater. It allows the + // Depth Based Search algorithm to perform incremental updates in lockstep + // with updates to the CFG. We emulated lockstep CFG updates by getting its + // next snapshots by reverse-applying future updates. + static ResultTy Get(NodePtr N, BatchUpdatePtr BUI) { + ResultTy Res = Get(N, Tag()); + // If there's no batch update in progress, simply return node's children. + if (!BUI) return Res; + + // CFG children are actually its *most current* children, and we have to + // reverse-apply the future updates to get the node's children at the + // point in time the update was performed. + auto &FutureChildren = (Inverse != IsPostDom) ? BUI->FuturePredecessors + : BUI->FutureSuccessors; + auto FCIt = FutureChildren.find(N); + if (FCIt == FutureChildren.end()) return Res; + + for (auto ChildAndKind : FCIt->second) { + const NodePtr Child = ChildAndKind.getPointer(); + const UpdateKind UK = ChildAndKind.getInt(); + + // Reverse-apply the future update. + if (UK == UpdateKind::Insert) { + // If there's an insertion in the future, it means that the edge must + // exist in the current CFG, but was not present in it before. + assert(llvm::find(Res, Child) != Res.end() + && "Expected child not found in the CFG"); + Res.erase(std::remove(Res.begin(), Res.end(), Child), Res.end()); + DEBUG(dbgs() << "\tHiding edge " << BlockNamePrinter(N) << " -> " + << BlockNamePrinter(Child) << "\n"); + } else { + // If there's an deletion in the future, it means that the edge cannot + // exist in the current CFG, but existed in it before. + assert(llvm::find(Res, Child) == Res.end() && + "Unexpected child found in the CFG"); + DEBUG(dbgs() << "\tShowing virtual edge " << BlockNamePrinter(N) + << " -> " << BlockNamePrinter(Child) << "\n"); + Res.push_back(Child); + } + } + + return Res; + } + }; + NodePtr getIDom(NodePtr BB) const { auto InfoIt = NodeToInfo.find(BB); if (InfoIt == NodeToInfo.end()) return nullptr; @@ -131,7 +208,11 @@ struct SemiNCAInfo { // Custom DFS implementation which can skip nodes based on a provided // predicate. It also collects ReverseChildren so that we don't have to spend // time getting predecessors in SemiNCA. - template <bool Inverse, typename DescendCondition> + // + // If IsReverse is set to true, the DFS walk will be performed backwards + // relative to IsPostDom -- using reverse edges for dominators and forward + // edges for postdominators. + template <bool IsReverse = false, typename DescendCondition> unsigned runDFS(NodePtr V, unsigned LastNum, DescendCondition Condition, unsigned AttachToNum) { assert(V); @@ -148,10 +229,12 @@ struct SemiNCAInfo { BBInfo.Label = BB; NumToNode.push_back(BB); - for (const NodePtr Succ : ChildrenGetter<NodePtr, Inverse>::Get(BB)) { + constexpr bool Direction = IsReverse != IsPostDom; // XOR. + for (const NodePtr Succ : + ChildrenGetter<Direction>::Get(BB, BatchUpdates)) { const auto SIT = NodeToInfo.find(Succ); // Don't visit nodes more than once but remember to collect - // RerverseChildren. + // ReverseChildren. if (SIT != NodeToInfo.end() && SIT->second.DFSNum != 0) { if (Succ != BB) SIT->second.ReverseChildren.push_back(BB); continue; @@ -256,51 +339,244 @@ struct SemiNCAInfo { } } - template <typename DescendCondition> - unsigned doFullDFSWalk(const DomTreeT &DT, DescendCondition DC) { - unsigned Num = 0; + // PostDominatorTree always has a virtual root that represents a virtual CFG + // node that serves as a single exit from the function. All the other exits + // (CFG nodes with terminators and nodes in infinite loops are logically + // connected to this virtual CFG exit node). + // This functions maps a nullptr CFG node to the virtual root tree node. + void addVirtualRoot() { + assert(IsPostDom && "Only postdominators have a virtual root"); + assert(NumToNode.size() == 1 && "SNCAInfo must be freshly constructed"); - if (DT.Roots.size() > 1) { - auto &BBInfo = NodeToInfo[nullptr]; - BBInfo.DFSNum = BBInfo.Semi = ++Num; - BBInfo.Label = nullptr; + auto &BBInfo = NodeToInfo[nullptr]; + BBInfo.DFSNum = BBInfo.Semi = 1; + BBInfo.Label = nullptr; - NumToNode.push_back(nullptr); // NumToNode[n] = V; + NumToNode.push_back(nullptr); // NumToNode[1] = nullptr; + } + + // For postdominators, nodes with no forward successors are trivial roots that + // are always selected as tree roots. Roots with forward successors correspond + // to CFG nodes within infinite loops. + static bool HasForwardSuccessors(const NodePtr N, BatchUpdatePtr BUI) { + assert(N && "N must be a valid node"); + return !ChildrenGetter<false>::Get(N, BUI).empty(); + } + + static NodePtr GetEntryNode(const DomTreeT &DT) { + assert(DT.Parent && "Parent not set"); + return GraphTraits<typename DomTreeT::ParentPtr>::getEntryNode(DT.Parent); + } + + // Finds all roots without relaying on the set of roots already stored in the + // tree. + // We define roots to be some non-redundant set of the CFG nodes + static RootsT FindRoots(const DomTreeT &DT, BatchUpdatePtr BUI) { + assert(DT.Parent && "Parent pointer is not set"); + RootsT Roots; + + // For dominators, function entry CFG node is always a tree root node. + if (!IsPostDom) { + Roots.push_back(GetEntryNode(DT)); + return Roots; } - if (DT.isPostDominator()) { - for (auto *Root : DT.Roots) Num = runDFS<true>(Root, Num, DC, 1); - } else { - assert(DT.Roots.size() == 1); - Num = runDFS<false>(DT.Roots[0], Num, DC, Num); + SemiNCAInfo SNCA(BUI); + + // PostDominatorTree always has a virtual root. + SNCA.addVirtualRoot(); + unsigned Num = 1; + + DEBUG(dbgs() << "\t\tLooking for trivial roots\n"); + + // Step #1: Find all the trivial roots that are going to will definitely + // remain tree roots. + unsigned Total = 0; + // It may happen that there are some new nodes in the CFG that are result of + // the ongoing batch update, but we cannot really pretend that they don't + // exist -- we won't see any outgoing or incoming edges to them, so it's + // fine to discover them here, as they would end up appearing in the CFG at + // some point anyway. + for (const NodePtr N : nodes(DT.Parent)) { + ++Total; + // If it has no *successors*, it is definitely a root. + if (!HasForwardSuccessors(N, BUI)) { + Roots.push_back(N); + // Run DFS not to walk this part of CFG later. + Num = SNCA.runDFS(N, Num, AlwaysDescend, 1); + DEBUG(dbgs() << "Found a new trivial root: " << BlockNamePrinter(N) + << "\n"); + DEBUG(dbgs() << "Last visited node: " + << BlockNamePrinter(SNCA.NumToNode[Num]) << "\n"); + } } - return Num; + DEBUG(dbgs() << "\t\tLooking for non-trivial roots\n"); + + // Step #2: Find all non-trivial root candidates. Those are CFG nodes that + // are reverse-unreachable were not visited by previous DFS walks (i.e. CFG + // nodes in infinite loops). + bool HasNonTrivialRoots = false; + // Accounting for the virtual exit, see if we had any reverse-unreachable + // nodes. + if (Total + 1 != Num) { + HasNonTrivialRoots = true; + // Make another DFS pass over all other nodes to find the + // reverse-unreachable blocks, and find the furthest paths we'll be able + // to make. + // Note that this looks N^2, but it's really 2N worst case, if every node + // is unreachable. This is because we are still going to only visit each + // unreachable node once, we may just visit it in two directions, + // depending on how lucky we get. + SmallPtrSet<NodePtr, 4> ConnectToExitBlock; + for (const NodePtr I : nodes(DT.Parent)) { + if (SNCA.NodeToInfo.count(I) == 0) { + DEBUG(dbgs() << "\t\t\tVisiting node " << BlockNamePrinter(I) + << "\n"); + // Find the furthest away we can get by following successors, then + // follow them in reverse. This gives us some reasonable answer about + // the post-dom tree inside any infinite loop. In particular, it + // guarantees we get to the farthest away point along *some* + // path. This also matches the GCC's behavior. + // If we really wanted a totally complete picture of dominance inside + // this infinite loop, we could do it with SCC-like algorithms to find + // the lowest and highest points in the infinite loop. In theory, it + // would be nice to give the canonical backedge for the loop, but it's + // expensive and does not always lead to a minimal set of roots. + DEBUG(dbgs() << "\t\t\tRunning forward DFS\n"); + + const unsigned NewNum = SNCA.runDFS<true>(I, Num, AlwaysDescend, Num); + const NodePtr FurthestAway = SNCA.NumToNode[NewNum]; + DEBUG(dbgs() << "\t\t\tFound a new furthest away node " + << "(non-trivial root): " + << BlockNamePrinter(FurthestAway) << "\n"); + ConnectToExitBlock.insert(FurthestAway); + Roots.push_back(FurthestAway); + DEBUG(dbgs() << "\t\t\tPrev DFSNum: " << Num << ", new DFSNum: " + << NewNum << "\n\t\t\tRemoving DFS info\n"); + for (unsigned i = NewNum; i > Num; --i) { + const NodePtr N = SNCA.NumToNode[i]; + DEBUG(dbgs() << "\t\t\t\tRemoving DFS info for " + << BlockNamePrinter(N) << "\n"); + SNCA.NodeToInfo.erase(N); + SNCA.NumToNode.pop_back(); + } + const unsigned PrevNum = Num; + DEBUG(dbgs() << "\t\t\tRunning reverse DFS\n"); + Num = SNCA.runDFS(FurthestAway, Num, AlwaysDescend, 1); + for (unsigned i = PrevNum + 1; i <= Num; ++i) + DEBUG(dbgs() << "\t\t\t\tfound node " + << BlockNamePrinter(SNCA.NumToNode[i]) << "\n"); + } + } + } + + DEBUG(dbgs() << "Total: " << Total << ", Num: " << Num << "\n"); + DEBUG(dbgs() << "Discovered CFG nodes:\n"); + DEBUG(for (size_t i = 0; i <= Num; ++i) dbgs() + << i << ": " << BlockNamePrinter(SNCA.NumToNode[i]) << "\n"); + + assert((Total + 1 == Num) && "Everything should have been visited"); + + // Step #3: If we found some non-trivial roots, make them non-redundant. + if (HasNonTrivialRoots) RemoveRedundantRoots(DT, BUI, Roots); + + DEBUG(dbgs() << "Found roots: "); + DEBUG(for (auto *Root : Roots) dbgs() << BlockNamePrinter(Root) << " "); + DEBUG(dbgs() << "\n"); + + return Roots; } - void calculateFromScratch(DomTreeT &DT, const unsigned NumBlocks) { + // This function only makes sense for postdominators. + // We define roots to be some set of CFG nodes where (reverse) DFS walks have + // to start in order to visit all the CFG nodes (including the + // reverse-unreachable ones). + // When the search for non-trivial roots is done it may happen that some of + // the non-trivial roots are reverse-reachable from other non-trivial roots, + // which makes them redundant. This function removes them from the set of + // input roots. + static void RemoveRedundantRoots(const DomTreeT &DT, BatchUpdatePtr BUI, + RootsT &Roots) { + assert(IsPostDom && "This function is for postdominators only"); + DEBUG(dbgs() << "Removing redundant roots\n"); + + SemiNCAInfo SNCA(BUI); + + for (unsigned i = 0; i < Roots.size(); ++i) { + auto &Root = Roots[i]; + // Trivial roots are always non-redundant. + if (!HasForwardSuccessors(Root, BUI)) continue; + DEBUG(dbgs() << "\tChecking if " << BlockNamePrinter(Root) + << " remains a root\n"); + SNCA.clear(); + // Do a forward walk looking for the other roots. + const unsigned Num = SNCA.runDFS<true>(Root, 0, AlwaysDescend, 0); + // Skip the start node and begin from the second one (note that DFS uses + // 1-based indexing). + for (unsigned x = 2; x <= Num; ++x) { + const NodePtr N = SNCA.NumToNode[x]; + // If we wound another root in a (forward) DFS walk, remove the current + // root from the set of roots, as it is reverse-reachable from the other + // one. + if (llvm::find(Roots, N) != Roots.end()) { + DEBUG(dbgs() << "\tForward DFS walk found another root " + << BlockNamePrinter(N) << "\n\tRemoving root " + << BlockNamePrinter(Root) << "\n"); + std::swap(Root, Roots.back()); + Roots.pop_back(); + + // Root at the back takes the current root's place. + // Start the next loop iteration with the same index. + --i; + break; + } + } + } + } + + template <typename DescendCondition> + void doFullDFSWalk(const DomTreeT &DT, DescendCondition DC) { + if (!IsPostDom) { + assert(DT.Roots.size() == 1 && "Dominators should have a singe root"); + runDFS(DT.Roots[0], 0, DC, 0); + return; + } + + addVirtualRoot(); + unsigned Num = 1; + for (const NodePtr Root : DT.Roots) Num = runDFS(Root, Num, DC, 0); + } + + static void CalculateFromScratch(DomTreeT &DT, BatchUpdatePtr BUI) { + auto *Parent = DT.Parent; + DT.reset(); + DT.Parent = Parent; + SemiNCAInfo SNCA(nullptr); // Since we are rebuilding the whole tree, + // there's no point doing it incrementally. + // Step #0: Number blocks in depth-first order and initialize variables used // in later stages of the algorithm. - const unsigned LastDFSNum = doFullDFSWalk(DT, AlwaysDescend); + DT.Roots = FindRoots(DT, nullptr); + SNCA.doFullDFSWalk(DT, AlwaysDescend); - runSemiNCA(DT); + SNCA.runSemiNCA(DT); + if (BUI) { + BUI->IsRecalculated = true; + DEBUG(dbgs() << "DomTree recalculated, skipping future batch updates\n"); + } if (DT.Roots.empty()) return; - // Add a node for the root. This node might be the actual root, if there is - // one exit block, or it may be the virtual exit (denoted by - // (BasicBlock *)0) which postdominates all real exits if there are multiple - // exit blocks, or an infinite loop. - // It might be that some blocks did not get a DFS number (e.g., blocks of - // infinite loops). In these cases an artificial exit node is required. - const bool MultipleRoots = DT.Roots.size() > 1 || (DT.isPostDominator() && - LastDFSNum != NumBlocks); - NodePtr Root = !MultipleRoots ? DT.Roots[0] : nullptr; + // Add a node for the root. If the tree is a PostDominatorTree it will be + // the virtual exit (denoted by (BasicBlock *) nullptr) which postdominates + // all real exits (including multiple exit blocks, infinite loops). + NodePtr Root = IsPostDom ? nullptr : DT.Roots[0]; DT.RootNode = (DT.DomTreeNodes[Root] = llvm::make_unique<DomTreeNodeBase<NodeT>>(Root, nullptr)) .get(); - attachNewSubtree(DT, DT.RootNode); + SNCA.attachNewSubtree(DT, DT.RootNode); } void attachNewSubtree(DomTreeT& DT, const TreeNodePtr AttachTo) { @@ -317,11 +593,11 @@ struct SemiNCAInfo { NodePtr ImmDom = getIDom(W); - // Get or calculate the node for the immediate dominator + // Get or calculate the node for the immediate dominator. TreeNodePtr IDomNode = getNodeForBlock(ImmDom, DT); // Add a new tree node for this BasicBlock, and link it as a child of - // IDomNode + // IDomNode. DT.DomTreeNodes[W] = IDomNode->addChild( llvm::make_unique<DomTreeNodeBase<NodeT>>(W, IDomNode)); } @@ -357,31 +633,105 @@ struct SemiNCAInfo { SmallVector<TreeNodePtr, 8> VisitedNotAffectedQueue; }; - static void InsertEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) { - assert(From && To && "Cannot connect nullptrs"); + static void InsertEdge(DomTreeT &DT, const BatchUpdatePtr BUI, + const NodePtr From, const NodePtr To) { + assert((From || IsPostDom) && + "From has to be a valid CFG node or a virtual root"); + assert(To && "Cannot be a nullptr"); DEBUG(dbgs() << "Inserting edge " << BlockNamePrinter(From) << " -> " << BlockNamePrinter(To) << "\n"); - const TreeNodePtr FromTN = DT.getNode(From); - - // Ignore edges from unreachable nodes. - if (!FromTN) return; + TreeNodePtr FromTN = DT.getNode(From); + + if (!FromTN) { + // Ignore edges from unreachable nodes for (forward) dominators. + if (!IsPostDom) return; + + // The unreachable node becomes a new root -- a tree node for it. + TreeNodePtr VirtualRoot = DT.getNode(nullptr); + FromTN = + (DT.DomTreeNodes[From] = VirtualRoot->addChild( + llvm::make_unique<DomTreeNodeBase<NodeT>>(From, VirtualRoot))) + .get(); + DT.Roots.push_back(From); + } DT.DFSInfoValid = false; const TreeNodePtr ToTN = DT.getNode(To); if (!ToTN) - InsertUnreachable(DT, FromTN, To); + InsertUnreachable(DT, BUI, FromTN, To); else - InsertReachable(DT, FromTN, ToTN); + InsertReachable(DT, BUI, FromTN, ToTN); + } + + // Determines if some existing root becomes reverse-reachable after the + // insertion. Rebuilds the whole tree if that situation happens. + static bool UpdateRootsBeforeInsertion(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr From, + const TreeNodePtr To) { + assert(IsPostDom && "This function is only for postdominators"); + // Destination node is not attached to the virtual root, so it cannot be a + // root. + if (!DT.isVirtualRoot(To->getIDom())) return false; + + auto RIt = llvm::find(DT.Roots, To->getBlock()); + if (RIt == DT.Roots.end()) + return false; // To is not a root, nothing to update. + + DEBUG(dbgs() << "\t\tAfter the insertion, " << BlockNamePrinter(To) + << " is no longer a root\n\t\tRebuilding the tree!!!\n"); + + CalculateFromScratch(DT, BUI); + return true; + } + + // Updates the set of roots after insertion or deletion. This ensures that + // roots are the same when after a series of updates and when the tree would + // be built from scratch. + static void UpdateRootsAfterUpdate(DomTreeT &DT, const BatchUpdatePtr BUI) { + assert(IsPostDom && "This function is only for postdominators"); + + // The tree has only trivial roots -- nothing to update. + if (std::none_of(DT.Roots.begin(), DT.Roots.end(), [BUI](const NodePtr N) { + return HasForwardSuccessors(N, BUI); + })) + return; + + // Recalculate the set of roots. + DT.Roots = FindRoots(DT, BUI); + for (const NodePtr R : DT.Roots) { + const TreeNodePtr TN = DT.getNode(R); + // A CFG node was selected as a tree root, but the corresponding tree node + // is not connected to the virtual root. This is because the incremental + // algorithm does not really know or use the set of roots and can make a + // different (implicit) decision about which nodes within an infinite loop + // becomes a root. + if (DT.isVirtualRoot(TN->getIDom())) { + DEBUG(dbgs() << "Root " << BlockNamePrinter(R) + << " is not virtual root's child\n" + << "The entire tree needs to be rebuilt\n"); + // It should be possible to rotate the subtree instead of recalculating + // the whole tree, but this situation happens extremely rarely in + // practice. + CalculateFromScratch(DT, BUI); + return; + } + } } // Handles insertion to a node already in the dominator tree. - static void InsertReachable(DomTreeT &DT, const TreeNodePtr From, - const TreeNodePtr To) { + static void InsertReachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr From, const TreeNodePtr To) { DEBUG(dbgs() << "\tReachable " << BlockNamePrinter(From->getBlock()) << " -> " << BlockNamePrinter(To->getBlock()) << "\n"); + if (IsPostDom && UpdateRootsBeforeInsertion(DT, BUI, From, To)) return; + // DT.findNCD expects both pointers to be valid. When From is a virtual + // root, then its CFG block pointer is a nullptr, so we have to 'compute' + // the NCD manually. const NodePtr NCDBlock = - DT.findNearestCommonDominator(From->getBlock(), To->getBlock()); + (From->getBlock() && To->getBlock()) + ? DT.findNearestCommonDominator(From->getBlock(), To->getBlock()) + : nullptr; assert(NCDBlock || DT.isPostDominator()); const TreeNodePtr NCD = DT.getNode(NCDBlock); assert(NCD); @@ -410,53 +760,61 @@ struct SemiNCAInfo { II.AffectedQueue.push_back(CurrentNode); // Discover and collect affected successors of the current node. - VisitInsertion(DT, CurrentNode, CurrentNode->getLevel(), NCD, II); + VisitInsertion(DT, BUI, CurrentNode, CurrentNode->getLevel(), NCD, II); } // Finish by updating immediate dominators and levels. - UpdateInsertion(DT, NCD, II); + UpdateInsertion(DT, BUI, NCD, II); } // Visits an affected node and collect its affected successors. - static void VisitInsertion(DomTreeT &DT, const TreeNodePtr TN, - const unsigned RootLevel, const TreeNodePtr NCD, - InsertionInfo &II) { + static void VisitInsertion(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr TN, const unsigned RootLevel, + const TreeNodePtr NCD, InsertionInfo &II) { const unsigned NCDLevel = NCD->getLevel(); DEBUG(dbgs() << "Visiting " << BlockNamePrinter(TN) << "\n"); - assert(TN->getBlock()); - for (const NodePtr Succ : - ChildrenGetter<NodePtr, IsPostDom>::Get(TN->getBlock())) { - const TreeNodePtr SuccTN = DT.getNode(Succ); - assert(SuccTN && "Unreachable successor found at reachable insertion"); - const unsigned SuccLevel = SuccTN->getLevel(); - - DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ) - << ", level = " << SuccLevel << "\n"); - - // Succ dominated by subtree From -- not affected. - // (Based on the lemma 2.5 from the second paper.) - if (SuccLevel > RootLevel) { - DEBUG(dbgs() << "\t\tDominated by subtree From\n"); - if (II.Visited.count(SuccTN) != 0) continue; - - DEBUG(dbgs() << "\t\tMarking visited not affected " - << BlockNamePrinter(Succ) << "\n"); - II.Visited.insert(SuccTN); - II.VisitedNotAffectedQueue.push_back(SuccTN); - VisitInsertion(DT, SuccTN, RootLevel, NCD, II); - } else if ((SuccLevel > NCDLevel + 1) && II.Affected.count(SuccTN) == 0) { - DEBUG(dbgs() << "\t\tMarking affected and adding " - << BlockNamePrinter(Succ) << " to a Bucket\n"); - II.Affected.insert(SuccTN); - II.Bucket.push({SuccLevel, SuccTN}); + SmallVector<TreeNodePtr, 8> Stack = {TN}; + assert(TN->getBlock() && II.Visited.count(TN) && "Preconditions!"); + + do { + TreeNodePtr Next = Stack.pop_back_val(); + + for (const NodePtr Succ : + ChildrenGetter<IsPostDom>::Get(Next->getBlock(), BUI)) { + const TreeNodePtr SuccTN = DT.getNode(Succ); + assert(SuccTN && "Unreachable successor found at reachable insertion"); + const unsigned SuccLevel = SuccTN->getLevel(); + + DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ) + << ", level = " << SuccLevel << "\n"); + + // Succ dominated by subtree From -- not affected. + // (Based on the lemma 2.5 from the second paper.) + if (SuccLevel > RootLevel) { + DEBUG(dbgs() << "\t\tDominated by subtree From\n"); + if (II.Visited.count(SuccTN) != 0) + continue; + + DEBUG(dbgs() << "\t\tMarking visited not affected " + << BlockNamePrinter(Succ) << "\n"); + II.Visited.insert(SuccTN); + II.VisitedNotAffectedQueue.push_back(SuccTN); + Stack.push_back(SuccTN); + } else if ((SuccLevel > NCDLevel + 1) && + II.Affected.count(SuccTN) == 0) { + DEBUG(dbgs() << "\t\tMarking affected and adding " + << BlockNamePrinter(Succ) << " to a Bucket\n"); + II.Affected.insert(SuccTN); + II.Bucket.push({SuccLevel, SuccTN}); + } } - } + } while (!Stack.empty()); } // Updates immediate dominators and levels after insertion. - static void UpdateInsertion(DomTreeT &DT, const TreeNodePtr NCD, - InsertionInfo &II) { + static void UpdateInsertion(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr NCD, InsertionInfo &II) { DEBUG(dbgs() << "Updating NCD = " << BlockNamePrinter(NCD) << "\n"); for (const TreeNodePtr TN : II.AffectedQueue) { @@ -466,6 +824,7 @@ struct SemiNCAInfo { } UpdateLevelsAfterInsertion(II); + if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI); } static void UpdateLevelsAfterInsertion(InsertionInfo &II) { @@ -480,36 +839,35 @@ struct SemiNCAInfo { } // Handles insertion to previously unreachable nodes. - static void InsertUnreachable(DomTreeT &DT, const TreeNodePtr From, - const NodePtr To) { + static void InsertUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr From, const NodePtr To) { DEBUG(dbgs() << "Inserting " << BlockNamePrinter(From) << " -> (unreachable) " << BlockNamePrinter(To) << "\n"); // Collect discovered edges to already reachable nodes. SmallVector<std::pair<NodePtr, TreeNodePtr>, 8> DiscoveredEdgesToReachable; // Discover and connect nodes that became reachable with the insertion. - ComputeUnreachableDominators(DT, To, From, DiscoveredEdgesToReachable); + ComputeUnreachableDominators(DT, BUI, To, From, DiscoveredEdgesToReachable); DEBUG(dbgs() << "Inserted " << BlockNamePrinter(From) << " -> (prev unreachable) " << BlockNamePrinter(To) << "\n"); - DEBUG(DT.print(dbgs())); - // Used the discovered edges and inset discovered connecting (incoming) // edges. for (const auto &Edge : DiscoveredEdgesToReachable) { DEBUG(dbgs() << "\tInserting discovered connecting edge " << BlockNamePrinter(Edge.first) << " -> " << BlockNamePrinter(Edge.second) << "\n"); - InsertReachable(DT, DT.getNode(Edge.first), Edge.second); + InsertReachable(DT, BUI, DT.getNode(Edge.first), Edge.second); } } // Connects nodes that become reachable with an insertion. static void ComputeUnreachableDominators( - DomTreeT &DT, const NodePtr Root, const TreeNodePtr Incoming, + DomTreeT &DT, const BatchUpdatePtr BUI, const NodePtr Root, + const TreeNodePtr Incoming, SmallVectorImpl<std::pair<NodePtr, TreeNodePtr>> - &DiscoveredConnectingEdges) { + &DiscoveredConnectingEdges) { assert(!DT.getNode(Root) && "Root must not be reachable"); // Visit only previously unreachable nodes. @@ -522,50 +880,16 @@ struct SemiNCAInfo { return false; }; - SemiNCAInfo SNCA; - SNCA.runDFS<IsPostDom>(Root, 0, UnreachableDescender, 0); + SemiNCAInfo SNCA(BUI); + SNCA.runDFS(Root, 0, UnreachableDescender, 0); SNCA.runSemiNCA(DT); SNCA.attachNewSubtree(DT, Incoming); DEBUG(dbgs() << "After adding unreachable nodes\n"); - DEBUG(DT.print(dbgs())); } - // Checks if the tree contains all reachable nodes in the input graph. - bool verifyReachability(const DomTreeT &DT) { - clear(); - doFullDFSWalk(DT, AlwaysDescend); - - for (auto &NodeToTN : DT.DomTreeNodes) { - const TreeNodePtr TN = NodeToTN.second.get(); - const NodePtr BB = TN->getBlock(); - - // Virtual root has a corresponding virtual CFG node. - if (DT.isVirtualRoot(TN)) continue; - - if (NodeToInfo.count(BB) == 0) { - errs() << "DomTree node " << BlockNamePrinter(BB) - << " not found by DFS walk!\n"; - errs().flush(); - - return false; - } - } - - for (const NodePtr N : NumToNode) { - if (N && !DT.getNode(N)) { - errs() << "CFG node " << BlockNamePrinter(N) - << " not found in the DomTree!\n"; - errs().flush(); - - return false; - } - } - - return true; - } - - static void DeleteEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) { + static void DeleteEdge(DomTreeT &DT, const BatchUpdatePtr BUI, + const NodePtr From, const NodePtr To) { assert(From && To && "Cannot disconnect nullptrs"); DEBUG(dbgs() << "Deleting edge " << BlockNamePrinter(From) << " -> " << BlockNamePrinter(To) << "\n"); @@ -574,8 +898,8 @@ struct SemiNCAInfo { // Ensure that the edge was in fact deleted from the CFG before informing // the DomTree about it. // The check is O(N), so run it only in debug configuration. - auto IsSuccessor = [](const NodePtr SuccCandidate, const NodePtr Of) { - auto Successors = ChildrenGetter<NodePtr, IsPostDom>::Get(Of); + auto IsSuccessor = [BUI](const NodePtr SuccCandidate, const NodePtr Of) { + auto Successors = ChildrenGetter<IsPostDom>::Get(Of, BUI); return llvm::find(Successors, SuccCandidate) != Successors.end(); }; (void)IsSuccessor; @@ -587,27 +911,37 @@ struct SemiNCAInfo { if (!FromTN) return; const TreeNodePtr ToTN = DT.getNode(To); - assert(ToTN && "To already unreachable -- there is no edge to delete"); + if (!ToTN) { + DEBUG(dbgs() << "\tTo (" << BlockNamePrinter(To) + << ") already unreachable -- there is no edge to delete\n"); + return; + } + const NodePtr NCDBlock = DT.findNearestCommonDominator(From, To); const TreeNodePtr NCD = DT.getNode(NCDBlock); // To dominates From -- nothing to do. if (ToTN == NCD) return; + DT.DFSInfoValid = false; + const TreeNodePtr ToIDom = ToTN->getIDom(); DEBUG(dbgs() << "\tNCD " << BlockNamePrinter(NCD) << ", ToIDom " << BlockNamePrinter(ToIDom) << "\n"); // To remains reachable after deletion. // (Based on the caption under Figure 4. from the second paper.) - if (FromTN != ToIDom || HasProperSupport(DT, ToTN)) - DeleteReachable(DT, FromTN, ToTN); + if (FromTN != ToIDom || HasProperSupport(DT, BUI, ToTN)) + DeleteReachable(DT, BUI, FromTN, ToTN); else - DeleteUnreachable(DT, ToTN); + DeleteUnreachable(DT, BUI, ToTN); + + if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI); } // Handles deletions that leave destination nodes reachable. - static void DeleteReachable(DomTreeT &DT, const TreeNodePtr FromTN, + static void DeleteReachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr FromTN, const TreeNodePtr ToTN) { DEBUG(dbgs() << "Deleting reachable " << BlockNamePrinter(FromTN) << " -> " << BlockNamePrinter(ToTN) << "\n"); @@ -625,7 +959,7 @@ struct SemiNCAInfo { // scratch. if (!PrevIDomSubTree) { DEBUG(dbgs() << "The entire tree needs to be rebuilt\n"); - DT.recalculate(*DT.Parent); + CalculateFromScratch(DT, BUI); return; } @@ -637,8 +971,8 @@ struct SemiNCAInfo { DEBUG(dbgs() << "\tTop of subtree: " << BlockNamePrinter(ToIDomTN) << "\n"); - SemiNCAInfo SNCA; - SNCA.runDFS<IsPostDom>(ToIDom, 0, DescendBelow, 0); + SemiNCAInfo SNCA(BUI); + SNCA.runDFS(ToIDom, 0, DescendBelow, 0); DEBUG(dbgs() << "\tRunning Semi-NCA\n"); SNCA.runSemiNCA(DT, Level); SNCA.reattachExistingSubtree(DT, PrevIDomSubTree); @@ -646,10 +980,11 @@ struct SemiNCAInfo { // Checks if a node has proper support, as defined on the page 3 and later // explained on the page 7 of the second paper. - static bool HasProperSupport(DomTreeT &DT, const TreeNodePtr TN) { + static bool HasProperSupport(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr TN) { DEBUG(dbgs() << "IsReachableFromIDom " << BlockNamePrinter(TN) << "\n"); for (const NodePtr Pred : - ChildrenGetter<NodePtr, !IsPostDom>::Get(TN->getBlock())) { + ChildrenGetter<!IsPostDom>::Get(TN->getBlock(), BUI)) { DEBUG(dbgs() << "\tPred " << BlockNamePrinter(Pred) << "\n"); if (!DT.getNode(Pred)) continue; @@ -669,12 +1004,24 @@ struct SemiNCAInfo { // Handle deletions that make destination node unreachable. // (Based on the lemma 2.7 from the second paper.) - static void DeleteUnreachable(DomTreeT &DT, const TreeNodePtr ToTN) { + static void DeleteUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr ToTN) { DEBUG(dbgs() << "Deleting unreachable subtree " << BlockNamePrinter(ToTN) << "\n"); assert(ToTN); assert(ToTN->getBlock()); + if (IsPostDom) { + // Deletion makes a region reverse-unreachable and creates a new root. + // Simulate that by inserting an edge from the virtual root to ToTN and + // adding it as a new root. + DEBUG(dbgs() << "\tDeletion made a region reverse-unreachable\n"); + DEBUG(dbgs() << "\tAdding new root " << BlockNamePrinter(ToTN) << "\n"); + DT.Roots.push_back(ToTN->getBlock()); + InsertReachable(DT, BUI, DT.getNode(nullptr), ToTN); + return; + } + SmallVector<NodePtr, 16> AffectedQueue; const unsigned Level = ToTN->getLevel(); @@ -690,13 +1037,13 @@ struct SemiNCAInfo { return false; }; - SemiNCAInfo SNCA; + SemiNCAInfo SNCA(BUI); unsigned LastDFSNum = - SNCA.runDFS<IsPostDom>(ToTN->getBlock(), 0, DescendAndCollect, 0); + SNCA.runDFS(ToTN->getBlock(), 0, DescendAndCollect, 0); TreeNodePtr MinNode = ToTN; - // Identify the top of the subtree to rebuilt by finding the NCD of all + // Identify the top of the subtree to rebuild by finding the NCD of all // the affected nodes. for (const NodePtr N : AffectedQueue) { const TreeNodePtr TN = DT.getNode(N); @@ -715,7 +1062,7 @@ struct SemiNCAInfo { // Root reached, rebuild the whole tree from scratch. if (!MinNode->getIDom()) { DEBUG(dbgs() << "The entire tree needs to be rebuilt\n"); - DT.recalculate(*DT.Parent); + CalculateFromScratch(DT, BUI); return; } @@ -744,7 +1091,7 @@ struct SemiNCAInfo { const TreeNodePtr ToTN = DT.getNode(To); return ToTN && ToTN->getLevel() > MinLevel; }; - SNCA.runDFS<IsPostDom>(MinNode->getBlock(), 0, DescendBelow, 0); + SNCA.runDFS(MinNode->getBlock(), 0, DescendBelow, 0); DEBUG(dbgs() << "Previous IDom(MinNode) = " << BlockNamePrinter(PrevIDom) << "\nRunning Semi-NCA\n"); @@ -771,9 +1118,222 @@ struct SemiNCAInfo { } //~~ + //===--------------------- DomTree Batch Updater --------------------------=== + //~~ + + static void ApplyUpdates(DomTreeT &DT, ArrayRef<UpdateT> Updates) { + const size_t NumUpdates = Updates.size(); + if (NumUpdates == 0) + return; + + // Take the fast path for a single update and avoid running the batch update + // machinery. + if (NumUpdates == 1) { + const auto &Update = Updates.front(); + if (Update.getKind() == UpdateKind::Insert) + DT.insertEdge(Update.getFrom(), Update.getTo()); + else + DT.deleteEdge(Update.getFrom(), Update.getTo()); + + return; + } + + BatchUpdateInfo BUI; + LegalizeUpdates(Updates, BUI.Updates); + + const size_t NumLegalized = BUI.Updates.size(); + BUI.FutureSuccessors.reserve(NumLegalized); + BUI.FuturePredecessors.reserve(NumLegalized); + + // Use the legalized future updates to initialize future successors and + // predecessors. Note that these sets will only decrease size over time, as + // the next CFG snapshots slowly approach the actual (current) CFG. + for (UpdateT &U : BUI.Updates) { + BUI.FutureSuccessors[U.getFrom()].insert({U.getTo(), U.getKind()}); + BUI.FuturePredecessors[U.getTo()].insert({U.getFrom(), U.getKind()}); + } + + DEBUG(dbgs() << "About to apply " << NumLegalized << " updates\n"); + DEBUG(if (NumLegalized < 32) for (const auto &U + : reverse(BUI.Updates)) dbgs() + << '\t' << U << "\n"); + DEBUG(dbgs() << "\n"); + + // If the DominatorTree was recalculated at some point, stop the batch + // updates. Full recalculations ignore batch updates and look at the actual + // CFG. + for (size_t i = 0; i < NumLegalized && !BUI.IsRecalculated; ++i) + ApplyNextUpdate(DT, BUI); + } + + // This function serves double purpose: + // a) It removes redundant updates, which makes it easier to reverse-apply + // them when traversing CFG. + // b) It optimizes away updates that cancel each other out, as the end result + // is the same. + // + // It relies on the property of the incremental updates that says that the + // order of updates doesn't matter. This allows us to reorder them and end up + // with the exact same DomTree every time. + // + // Following the same logic, the function doesn't care about the order of + // input updates, so it's OK to pass it an unordered sequence of updates, that + // doesn't make sense when applied sequentially, eg. performing double + // insertions or deletions and then doing an opposite update. + // + // In the future, it should be possible to schedule updates in way that + // minimizes the amount of work needed done during incremental updates. + static void LegalizeUpdates(ArrayRef<UpdateT> AllUpdates, + SmallVectorImpl<UpdateT> &Result) { + DEBUG(dbgs() << "Legalizing " << AllUpdates.size() << " updates\n"); + // Count the total number of inserions of each edge. + // Each insertion adds 1 and deletion subtracts 1. The end number should be + // one of {-1 (deletion), 0 (NOP), +1 (insertion)}. Otherwise, the sequence + // of updates contains multiple updates of the same kind and we assert for + // that case. + SmallDenseMap<std::pair<NodePtr, NodePtr>, int, 4> Operations; + Operations.reserve(AllUpdates.size()); + + for (const auto &U : AllUpdates) { + NodePtr From = U.getFrom(); + NodePtr To = U.getTo(); + if (IsPostDom) std::swap(From, To); // Reverse edge for postdominators. + + Operations[{From, To}] += (U.getKind() == UpdateKind::Insert ? 1 : -1); + } + + Result.clear(); + Result.reserve(Operations.size()); + for (auto &Op : Operations) { + const int NumInsertions = Op.second; + assert(std::abs(NumInsertions) <= 1 && "Unbalanced operations!"); + if (NumInsertions == 0) continue; + const UpdateKind UK = + NumInsertions > 0 ? UpdateKind::Insert : UpdateKind::Delete; + Result.push_back({UK, Op.first.first, Op.first.second}); + } + + // Make the order consistent by not relying on pointer values within the + // set. Reuse the old Operations map. + // In the future, we should sort by something else to minimize the amount + // of work needed to perform the series of updates. + for (size_t i = 0, e = AllUpdates.size(); i != e; ++i) { + const auto &U = AllUpdates[i]; + if (!IsPostDom) + Operations[{U.getFrom(), U.getTo()}] = int(i); + else + Operations[{U.getTo(), U.getFrom()}] = int(i); + } + + std::sort(Result.begin(), Result.end(), + [&Operations](const UpdateT &A, const UpdateT &B) { + return Operations[{A.getFrom(), A.getTo()}] > + Operations[{B.getFrom(), B.getTo()}]; + }); + } + + static void ApplyNextUpdate(DomTreeT &DT, BatchUpdateInfo &BUI) { + assert(!BUI.Updates.empty() && "No updates to apply!"); + UpdateT CurrentUpdate = BUI.Updates.pop_back_val(); + DEBUG(dbgs() << "Applying update: " << CurrentUpdate << "\n"); + + // Move to the next snapshot of the CFG by removing the reverse-applied + // current update. + auto &FS = BUI.FutureSuccessors[CurrentUpdate.getFrom()]; + FS.erase({CurrentUpdate.getTo(), CurrentUpdate.getKind()}); + if (FS.empty()) BUI.FutureSuccessors.erase(CurrentUpdate.getFrom()); + + auto &FP = BUI.FuturePredecessors[CurrentUpdate.getTo()]; + FP.erase({CurrentUpdate.getFrom(), CurrentUpdate.getKind()}); + if (FP.empty()) BUI.FuturePredecessors.erase(CurrentUpdate.getTo()); + + if (CurrentUpdate.getKind() == UpdateKind::Insert) + InsertEdge(DT, &BUI, CurrentUpdate.getFrom(), CurrentUpdate.getTo()); + else + DeleteEdge(DT, &BUI, CurrentUpdate.getFrom(), CurrentUpdate.getTo()); + } + + //~~ //===--------------- DomTree correctness verification ---------------------=== //~~ + // Check if the tree has correct roots. A DominatorTree always has a single + // root which is the function's entry node. A PostDominatorTree can have + // multiple roots - one for each node with no successors and for infinite + // loops. + bool verifyRoots(const DomTreeT &DT) { + if (!DT.Parent && !DT.Roots.empty()) { + errs() << "Tree has no parent but has roots!\n"; + errs().flush(); + return false; + } + + if (!IsPostDom) { + if (DT.Roots.empty()) { + errs() << "Tree doesn't have a root!\n"; + errs().flush(); + return false; + } + + if (DT.getRoot() != GetEntryNode(DT)) { + errs() << "Tree's root is not its parent's entry node!\n"; + errs().flush(); + return false; + } + } + + RootsT ComputedRoots = FindRoots(DT, nullptr); + if (DT.Roots.size() != ComputedRoots.size() || + !std::is_permutation(DT.Roots.begin(), DT.Roots.end(), + ComputedRoots.begin())) { + errs() << "Tree has different roots than freshly computed ones!\n"; + errs() << "\tPDT roots: "; + for (const NodePtr N : DT.Roots) errs() << BlockNamePrinter(N) << ", "; + errs() << "\n\tComputed roots: "; + for (const NodePtr N : ComputedRoots) + errs() << BlockNamePrinter(N) << ", "; + errs() << "\n"; + errs().flush(); + return false; + } + + return true; + } + + // Checks if the tree contains all reachable nodes in the input graph. + bool verifyReachability(const DomTreeT &DT) { + clear(); + doFullDFSWalk(DT, AlwaysDescend); + + for (auto &NodeToTN : DT.DomTreeNodes) { + const TreeNodePtr TN = NodeToTN.second.get(); + const NodePtr BB = TN->getBlock(); + + // Virtual root has a corresponding virtual CFG node. + if (DT.isVirtualRoot(TN)) continue; + + if (NodeToInfo.count(BB) == 0) { + errs() << "DomTree node " << BlockNamePrinter(BB) + << " not found by DFS walk!\n"; + errs().flush(); + + return false; + } + } + + for (const NodePtr N : NumToNode) { + if (N && !DT.getNode(N)) { + errs() << "CFG node " << BlockNamePrinter(N) + << " not found in the DomTree!\n"; + errs().flush(); + + return false; + } + } + + return true; + } + // Check if for every parent with a level L in the tree all of its children // have level L + 1. static bool VerifyLevels(const DomTreeT &DT) { @@ -805,35 +1365,97 @@ struct SemiNCAInfo { return true; } - // Checks if for every edge From -> To in the graph - // NCD(From, To) == IDom(To) or To. - bool verifyNCD(const DomTreeT &DT) { - clear(); - doFullDFSWalk(DT, AlwaysDescend); + // Check if the computed DFS numbers are correct. Note that DFS info may not + // be valid, and when that is the case, we don't verify the numbers. + static bool VerifyDFSNumbers(const DomTreeT &DT) { + if (!DT.DFSInfoValid || !DT.Parent) + return true; - for (auto &BlockToInfo : NodeToInfo) { - auto &Info = BlockToInfo.second; + const NodePtr RootBB = IsPostDom ? nullptr : DT.getRoots()[0]; + const TreeNodePtr Root = DT.getNode(RootBB); - const NodePtr From = NumToNode[Info.Parent]; - if (!From) continue; + auto PrintNodeAndDFSNums = [](const TreeNodePtr TN) { + errs() << BlockNamePrinter(TN) << " {" << TN->getDFSNumIn() << ", " + << TN->getDFSNumOut() << '}'; + }; - const NodePtr To = BlockToInfo.first; - const TreeNodePtr ToTN = DT.getNode(To); - assert(ToTN); - - const NodePtr NCD = DT.findNearestCommonDominator(From, To); - const TreeNodePtr NCDTN = DT.getNode(NCD); - const TreeNodePtr ToIDom = ToTN->getIDom(); - if (NCDTN != ToTN && NCDTN != ToIDom) { - errs() << "NearestCommonDominator verification failed:\n\tNCD(From:" - << BlockNamePrinter(From) << ", To:" << BlockNamePrinter(To) - << ") = " << BlockNamePrinter(NCD) - << ",\t (should be To or IDom[To]: " << BlockNamePrinter(ToIDom) - << ")\n"; + // Verify the root's DFS In number. Although DFS numbering would also work + // if we started from some other value, we assume 0-based numbering. + if (Root->getDFSNumIn() != 0) { + errs() << "DFSIn number for the tree root is not:\n\t"; + PrintNodeAndDFSNums(Root); + errs() << '\n'; + errs().flush(); + return false; + } + + // For each tree node verify if children's DFS numbers cover their parent's + // DFS numbers with no gaps. + for (const auto &NodeToTN : DT.DomTreeNodes) { + const TreeNodePtr Node = NodeToTN.second.get(); + + // Handle tree leaves. + if (Node->getChildren().empty()) { + if (Node->getDFSNumIn() + 1 != Node->getDFSNumOut()) { + errs() << "Tree leaf should have DFSOut = DFSIn + 1:\n\t"; + PrintNodeAndDFSNums(Node); + errs() << '\n'; + errs().flush(); + return false; + } + + continue; + } + + // Make a copy and sort it such that it is possible to check if there are + // no gaps between DFS numbers of adjacent children. + SmallVector<TreeNodePtr, 8> Children(Node->begin(), Node->end()); + std::sort(Children.begin(), Children.end(), + [](const TreeNodePtr Ch1, const TreeNodePtr Ch2) { + return Ch1->getDFSNumIn() < Ch2->getDFSNumIn(); + }); + + auto PrintChildrenError = [Node, &Children, PrintNodeAndDFSNums]( + const TreeNodePtr FirstCh, const TreeNodePtr SecondCh) { + assert(FirstCh); + + errs() << "Incorrect DFS numbers for:\n\tParent "; + PrintNodeAndDFSNums(Node); + + errs() << "\n\tChild "; + PrintNodeAndDFSNums(FirstCh); + + if (SecondCh) { + errs() << "\n\tSecond child "; + PrintNodeAndDFSNums(SecondCh); + } + + errs() << "\nAll children: "; + for (const TreeNodePtr Ch : Children) { + PrintNodeAndDFSNums(Ch); + errs() << ", "; + } + + errs() << '\n'; errs().flush(); + }; + if (Children.front()->getDFSNumIn() != Node->getDFSNumIn() + 1) { + PrintChildrenError(Children.front(), nullptr); return false; } + + if (Children.back()->getDFSNumOut() + 1 != Node->getDFSNumOut()) { + PrintChildrenError(Children.back(), nullptr); + return false; + } + + for (size_t i = 0, e = Children.size() - 1; i != e; ++i) { + if (Children[i]->getDFSNumOut() + 1 != Children[i + 1]->getDFSNumIn()) { + PrintChildrenError(Children[i], Children[i + 1]); + return false; + } + } } return true; @@ -888,6 +1510,8 @@ struct SemiNCAInfo { const NodePtr BB = TN->getBlock(); if (!BB || TN->getChildren().empty()) continue; + DEBUG(dbgs() << "Verifying parent property of node " + << BlockNamePrinter(TN) << "\n"); clear(); doFullDFSWalk(DT, [BB](NodePtr From, NodePtr To) { return From != BB && To != BB; @@ -945,33 +1569,37 @@ struct SemiNCAInfo { } }; - -template <class DomTreeT, class FuncT> -void Calculate(DomTreeT &DT, FuncT &F) { - SemiNCAInfo<DomTreeT> SNCA; - SNCA.calculateFromScratch(DT, GraphTraits<FuncT *>::size(&F)); +template <class DomTreeT> +void Calculate(DomTreeT &DT) { + SemiNCAInfo<DomTreeT>::CalculateFromScratch(DT, nullptr); } template <class DomTreeT> void InsertEdge(DomTreeT &DT, typename DomTreeT::NodePtr From, typename DomTreeT::NodePtr To) { if (DT.isPostDominator()) std::swap(From, To); - SemiNCAInfo<DomTreeT>::InsertEdge(DT, From, To); + SemiNCAInfo<DomTreeT>::InsertEdge(DT, nullptr, From, To); } template <class DomTreeT> void DeleteEdge(DomTreeT &DT, typename DomTreeT::NodePtr From, typename DomTreeT::NodePtr To) { if (DT.isPostDominator()) std::swap(From, To); - SemiNCAInfo<DomTreeT>::DeleteEdge(DT, From, To); + SemiNCAInfo<DomTreeT>::DeleteEdge(DT, nullptr, From, To); +} + +template <class DomTreeT> +void ApplyUpdates(DomTreeT &DT, + ArrayRef<typename DomTreeT::UpdateType> Updates) { + SemiNCAInfo<DomTreeT>::ApplyUpdates(DT, Updates); } template <class DomTreeT> bool Verify(const DomTreeT &DT) { - SemiNCAInfo<DomTreeT> SNCA; - return SNCA.verifyReachability(DT) && SNCA.VerifyLevels(DT) && - SNCA.verifyNCD(DT) && SNCA.verifyParentProperty(DT) && - SNCA.verifySiblingProperty(DT); + SemiNCAInfo<DomTreeT> SNCA(nullptr); + return SNCA.verifyRoots(DT) && SNCA.verifyReachability(DT) && + SNCA.VerifyLevels(DT) && SNCA.verifyParentProperty(DT) && + SNCA.verifySiblingProperty(DT) && SNCA.VerifyDFSNumbers(DT); } } // namespace DomTreeBuilder diff --git a/include/llvm/Support/Host.h b/include/llvm/Support/Host.h index be93dd99032e..a4b0a340c568 100644 --- a/include/llvm/Support/Host.h +++ b/include/llvm/Support/Host.h @@ -15,7 +15,6 @@ #define LLVM_SUPPORT_HOST_H #include "llvm/ADT/StringMap.h" -#include "llvm/Support/MemoryBuffer.h" #if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) #include <endian.h> @@ -92,6 +91,7 @@ constexpr bool IsBigEndianHost = false; StringRef getHostCPUNameForPowerPC(const StringRef &ProcCpuinfoContent); StringRef getHostCPUNameForARM(const StringRef &ProcCpuinfoContent); StringRef getHostCPUNameForS390x(const StringRef &ProcCpuinfoContent); + StringRef getHostCPUNameForBPF(); } } } diff --git a/include/llvm/Support/KnownBits.h b/include/llvm/Support/KnownBits.h index 2c77d40559b9..7a4de3e5ff12 100644 --- a/include/llvm/Support/KnownBits.h +++ b/include/llvm/Support/KnownBits.h @@ -25,7 +25,7 @@ struct KnownBits { APInt One; private: - // Internal constructor for creating a ConstantRange from two APInts. + // Internal constructor for creating a KnownBits from two APInts. KnownBits(APInt Zero, APInt One) : Zero(std::move(Zero)), One(std::move(One)) {} @@ -193,6 +193,10 @@ public: unsigned countMaxPopulation() const { return getBitWidth() - Zero.countPopulation(); } + + /// Compute known bits resulting from adding LHS and RHS. + static KnownBits computeForAddSub(bool Add, bool NSW, const KnownBits &LHS, + KnownBits RHS); }; } // end namespace llvm diff --git a/include/llvm/Support/LEB128.h b/include/llvm/Support/LEB128.h index 29640db69218..6af6e9f34474 100644 --- a/include/llvm/Support/LEB128.h +++ b/include/llvm/Support/LEB128.h @@ -21,23 +21,25 @@ namespace llvm { /// Utility function to encode a SLEB128 value to an output stream. inline void encodeSLEB128(int64_t Value, raw_ostream &OS, - unsigned Padding = 0) { + unsigned PadTo = 0) { bool More; + unsigned Count = 0; do { uint8_t Byte = Value & 0x7f; // NOTE: this assumes that this signed shift is an arithmetic right shift. Value >>= 7; More = !((((Value == 0 ) && ((Byte & 0x40) == 0)) || ((Value == -1) && ((Byte & 0x40) != 0)))); - if (More || Padding != 0) + Count++; + if (More || Count < PadTo) Byte |= 0x80; // Mark this byte to show that more bytes will follow. OS << char(Byte); } while (More); // Pad with 0x80 and emit a terminating byte at the end. - if (Padding != 0) { + if (Count < PadTo) { uint8_t PadValue = Value < 0 ? 0x7f : 0x00; - for (; Padding != 1; --Padding) + for (; Count < PadTo - 1; ++Count) OS << char(PadValue | 0x80); OS << char(PadValue); } @@ -45,8 +47,9 @@ inline void encodeSLEB128(int64_t Value, raw_ostream &OS, /// Utility function to encode a SLEB128 value to a buffer. Returns /// the length in bytes of the encoded value. -inline unsigned encodeSLEB128(int64_t Value, uint8_t *p, unsigned Padding = 0) { +inline unsigned encodeSLEB128(int64_t Value, uint8_t *p, unsigned PadTo = 0) { uint8_t *orig_p = p; + unsigned Count = 0; bool More; do { uint8_t Byte = Value & 0x7f; @@ -54,15 +57,16 @@ inline unsigned encodeSLEB128(int64_t Value, uint8_t *p, unsigned Padding = 0) { Value >>= 7; More = !((((Value == 0 ) && ((Byte & 0x40) == 0)) || ((Value == -1) && ((Byte & 0x40) != 0)))); - if (More || Padding != 0) + Count++; + if (More || Count < PadTo) Byte |= 0x80; // Mark this byte to show that more bytes will follow. *p++ = Byte; } while (More); // Pad with 0x80 and emit a terminating byte at the end. - if (Padding != 0) { + if (Count < PadTo) { uint8_t PadValue = Value < 0 ? 0x7f : 0x00; - for (; Padding != 1; --Padding) + for (; Count < PadTo - 1; ++Count) *p++ = (PadValue | 0x80); *p++ = PadValue; } @@ -71,42 +75,48 @@ inline unsigned encodeSLEB128(int64_t Value, uint8_t *p, unsigned Padding = 0) { /// Utility function to encode a ULEB128 value to an output stream. inline void encodeULEB128(uint64_t Value, raw_ostream &OS, - unsigned Padding = 0) { + unsigned PadTo = 0) { + unsigned Count = 0; do { uint8_t Byte = Value & 0x7f; Value >>= 7; - if (Value != 0 || Padding != 0) + Count++; + if (Value != 0 || Count < PadTo) Byte |= 0x80; // Mark this byte to show that more bytes will follow. OS << char(Byte); } while (Value != 0); // Pad with 0x80 and emit a null byte at the end. - if (Padding != 0) { - for (; Padding != 1; --Padding) + if (Count < PadTo) { + for (; Count < PadTo - 1; ++Count) OS << '\x80'; OS << '\x00'; + Count++; } } /// Utility function to encode a ULEB128 value to a buffer. Returns /// the length in bytes of the encoded value. inline unsigned encodeULEB128(uint64_t Value, uint8_t *p, - unsigned Padding = 0) { + unsigned PadTo = 0) { uint8_t *orig_p = p; + unsigned Count = 0; do { uint8_t Byte = Value & 0x7f; Value >>= 7; - if (Value != 0 || Padding != 0) + Count++; + if (Value != 0 || Count < PadTo) Byte |= 0x80; // Mark this byte to show that more bytes will follow. *p++ = Byte; } while (Value != 0); // Pad with 0x80 and emit a null byte at the end. - if (Padding != 0) { - for (; Padding != 1; --Padding) + if (Count < PadTo) { + for (; Count < PadTo - 1; ++Count) *p++ = '\x80'; *p++ = '\x00'; } + return (unsigned)(p - orig_p); } diff --git a/include/llvm/Support/LockFileManager.h b/include/llvm/Support/LockFileManager.h index 13d252425b93..1e417bdd5b25 100644 --- a/include/llvm/Support/LockFileManager.h +++ b/include/llvm/Support/LockFileManager.h @@ -11,6 +11,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" #include <system_error> #include <utility> // for std::pair @@ -53,10 +54,10 @@ public: private: SmallString<128> FileName; SmallString<128> LockFileName; - SmallString<128> UniqueLockFileName; + Optional<sys::fs::TempFile> UniqueLockFile; Optional<std::pair<std::string, int> > Owner; - Optional<std::error_code> Error; + std::error_code ErrorCode; std::string ErrorDiagMsg; LockFileManager(const LockFileManager &) = delete; @@ -88,8 +89,8 @@ public: std::string getErrorMessage() const; /// \brief Set error and error message - void setError(std::error_code &EC, StringRef ErrorMsg = "") { - Error = EC; + void setError(const std::error_code &EC, StringRef ErrorMsg = "") { + ErrorCode = EC; ErrorDiagMsg = ErrorMsg.str(); } }; diff --git a/include/llvm/Support/LowLevelTypeImpl.h b/include/llvm/Support/LowLevelTypeImpl.h index c79dd0c29507..099fa4618997 100644 --- a/include/llvm/Support/LowLevelTypeImpl.h +++ b/include/llvm/Support/LowLevelTypeImpl.h @@ -137,51 +137,6 @@ public: return scalar(getScalarSizeInBits()); } - /// Get a low-level type with half the size of the original, by halving the - /// size of the scalar type involved. For example `s32` will become `s16`, - /// `<2 x s32>` will become `<2 x s16>`. - LLT halfScalarSize() const { - assert(!IsPointer && getScalarSizeInBits() > 1 && - getScalarSizeInBits() % 2 == 0 && "cannot half size of this type"); - return LLT{/*isPointer=*/false, IsVector ? true : false, - IsVector ? getNumElements() : (uint16_t)0, - getScalarSizeInBits() / 2, /*AddressSpace=*/0}; - } - - /// Get a low-level type with twice the size of the original, by doubling the - /// size of the scalar type involved. For example `s32` will become `s64`, - /// `<2 x s32>` will become `<2 x s64>`. - LLT doubleScalarSize() const { - assert(!IsPointer && "cannot change size of this type"); - return LLT{/*isPointer=*/false, IsVector ? true : false, - IsVector ? getNumElements() : (uint16_t)0, - getScalarSizeInBits() * 2, /*AddressSpace=*/0}; - } - - /// Get a low-level type with half the size of the original, by halving the - /// number of vector elements of the scalar type involved. The source must be - /// a vector type with an even number of elements. For example `<4 x s32>` - /// will become `<2 x s32>`, `<2 x s32>` will become `s32`. - LLT halfElements() const { - assert(isVector() && getNumElements() % 2 == 0 && "cannot half odd vector"); - if (getNumElements() == 2) - return scalar(getScalarSizeInBits()); - - return LLT{/*isPointer=*/false, /*isVector=*/true, - (uint16_t)(getNumElements() / 2), getScalarSizeInBits(), - /*AddressSpace=*/0}; - } - - /// Get a low-level type with twice the size of the original, by doubling the - /// number of vector elements of the scalar type involved. The source must be - /// a vector type. For example `<2 x s32>` will become `<4 x s32>`. Doubling - /// the number of elements in sN produces <2 x sN>. - LLT doubleElements() const { - return LLT{IsPointer ? true : false, /*isVector=*/true, - (uint16_t)(getNumElements() * 2), getScalarSizeInBits(), - IsPointer ? getAddressSpace() : 0}; - } - void print(raw_ostream &OS) const; bool operator==(const LLT &RHS) const { diff --git a/include/llvm/Support/MathExtras.h b/include/llvm/Support/MathExtras.h index fd29865c8475..a37a16784e2a 100644 --- a/include/llvm/Support/MathExtras.h +++ b/include/llvm/Support/MathExtras.h @@ -424,7 +424,7 @@ constexpr inline bool isPowerOf2_32(uint32_t Value) { /// Return true if the argument is a power of two > 0 (64 bit edition.) constexpr inline bool isPowerOf2_64(uint64_t Value) { - return Value && !(Value & (Value - int64_t(1L))); + return Value && !(Value & (Value - 1)); } /// Return a byte-swapped representation of the 16-bit argument. @@ -687,6 +687,11 @@ template <uint64_t Align> constexpr inline uint64_t alignTo(uint64_t Value) { return (Value + Align - 1) / Align * Align; } +/// Returns the integer ceil(Numerator / Denominator). +inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) { + return alignTo(Numerator, Denominator) / Denominator; +} + /// \c alignTo for contexts where a constant expression is required. /// \sa alignTo /// diff --git a/include/llvm/Support/Memory.h b/include/llvm/Support/Memory.h index 8103aea2fa25..3140dc6eef42 100644 --- a/include/llvm/Support/Memory.h +++ b/include/llvm/Support/Memory.h @@ -109,51 +109,10 @@ namespace sys { static std::error_code protectMappedMemory(const MemoryBlock &Block, unsigned Flags); - /// This method allocates a block of Read/Write/Execute memory that is - /// suitable for executing dynamically generated code (e.g. JIT). An - /// attempt to allocate \p NumBytes bytes of virtual memory is made. - /// \p NearBlock may point to an existing allocation in which case - /// an attempt is made to allocate more memory near the existing block. - /// - /// On success, this returns a non-null memory block, otherwise it returns - /// a null memory block and fills in *ErrMsg. - /// - /// @brief Allocate Read/Write/Execute memory. - static MemoryBlock AllocateRWX(size_t NumBytes, - const MemoryBlock *NearBlock, - std::string *ErrMsg = nullptr); - - /// This method releases a block of Read/Write/Execute memory that was - /// allocated with the AllocateRWX method. It should not be used to - /// release any memory block allocated any other way. - /// - /// On success, this returns false, otherwise it returns true and fills - /// in *ErrMsg. - /// @brief Release Read/Write/Execute memory. - static bool ReleaseRWX(MemoryBlock &block, std::string *ErrMsg = nullptr); - /// InvalidateInstructionCache - Before the JIT can run a block of code /// that has been emitted it must invalidate the instruction cache on some /// platforms. static void InvalidateInstructionCache(const void *Addr, size_t Len); - - /// setExecutable - Before the JIT can run a block of code, it has to be - /// given read and executable privilege. Return true if it is already r-x - /// or the system is able to change its previlege. - static bool setExecutable(MemoryBlock &M, std::string *ErrMsg = nullptr); - - /// setWritable - When adding to a block of code, the JIT may need - /// to mark a block of code as RW since the protections are on page - /// boundaries, and the JIT internal allocations are not page aligned. - static bool setWritable(MemoryBlock &M, std::string *ErrMsg = nullptr); - - /// setRangeExecutable - Mark the page containing a range of addresses - /// as executable. - static bool setRangeExecutable(const void *Addr, size_t Size); - - /// setRangeWritable - Mark the page containing a range of addresses - /// as writable. - static bool setRangeWritable(const void *Addr, size_t Size); }; /// Owning version of MemoryBlock. diff --git a/include/llvm/Support/MemoryBuffer.h b/include/llvm/Support/MemoryBuffer.h index 73f0251a6b6e..59c93f15d7b8 100644 --- a/include/llvm/Support/MemoryBuffer.h +++ b/include/llvm/Support/MemoryBuffer.h @@ -136,7 +136,8 @@ public: /// Map a subrange of the specified file as a MemoryBuffer. static ErrorOr<std::unique_ptr<MemoryBuffer>> - getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, bool IsVolatile = false); + getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, + bool IsVolatile = false); //===--------------------------------------------------------------------===// // Provided for performance analysis. diff --git a/include/llvm/Support/Parallel.h b/include/llvm/Support/Parallel.h index e36e0cc29e14..6bc0a6bbaf2b 100644 --- a/include/llvm/Support/Parallel.h +++ b/include/llvm/Support/Parallel.h @@ -158,11 +158,11 @@ void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) { TaskSize = 1; TaskGroup TG; - while (TaskSize <= std::distance(Begin, End)) { + while (TaskSize < std::distance(Begin, End)) { TG.spawn([=, &Fn] { std::for_each(Begin, Begin + TaskSize, Fn); }); Begin += TaskSize; } - TG.spawn([=, &Fn] { std::for_each(Begin, End, Fn); }); + std::for_each(Begin, End, Fn); } template <class IndexTy, class FuncTy> @@ -179,10 +179,8 @@ void parallel_for_each_n(IndexTy Begin, IndexTy End, FuncTy Fn) { Fn(J); }); } - TG.spawn([=, &Fn] { - for (IndexTy J = I; J < End; ++J) - Fn(J); - }); + for (IndexTy J = I; J < End; ++J) + Fn(J); } #endif diff --git a/include/llvm/Support/PointerLikeTypeTraits.h b/include/llvm/Support/PointerLikeTypeTraits.h index 521a49684e45..794230d606a4 100644 --- a/include/llvm/Support/PointerLikeTypeTraits.h +++ b/include/llvm/Support/PointerLikeTypeTraits.h @@ -22,11 +22,7 @@ namespace llvm { /// A traits type that is used to handle pointer types and things that are just /// wrappers for pointers as a uniform entity. -template <typename T> class PointerLikeTypeTraits { - // getAsVoidPointer - // getFromVoidPointer - // getNumLowBitsAvailable -}; +template <typename T> struct PointerLikeTypeTraits; namespace detail { /// A tiny meta function to compute the log2 of a compile time constant. @@ -34,19 +30,36 @@ template <size_t N> struct ConstantLog2 : std::integral_constant<size_t, ConstantLog2<N / 2>::value + 1> {}; template <> struct ConstantLog2<1> : std::integral_constant<size_t, 0> {}; -} + +// Provide a trait to check if T is pointer-like. +template <typename T, typename U = void> struct HasPointerLikeTypeTraits { + static const bool value = false; +}; + +// sizeof(T) is valid only for a complete T. +template <typename T> struct HasPointerLikeTypeTraits< + T, decltype((sizeof(PointerLikeTypeTraits<T>) + sizeof(T)), void())> { + static const bool value = true; +}; + +template <typename T> struct IsPointerLike { + static const bool value = HasPointerLikeTypeTraits<T>::value; +}; + +template <typename T> struct IsPointerLike<T *> { + static const bool value = true; +}; +} // namespace detail // Provide PointerLikeTypeTraits for non-cvr pointers. -template <typename T> class PointerLikeTypeTraits<T *> { -public: +template <typename T> struct PointerLikeTypeTraits<T *> { static inline void *getAsVoidPointer(T *P) { return P; } static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); } enum { NumLowBitsAvailable = detail::ConstantLog2<alignof(T)>::value }; }; -template <> class PointerLikeTypeTraits<void *> { -public: +template <> struct PointerLikeTypeTraits<void *> { static inline void *getAsVoidPointer(void *P) { return P; } static inline void *getFromVoidPointer(void *P) { return P; } @@ -61,10 +74,9 @@ public: }; // Provide PointerLikeTypeTraits for const things. -template <typename T> class PointerLikeTypeTraits<const T> { +template <typename T> struct PointerLikeTypeTraits<const T> { typedef PointerLikeTypeTraits<T> NonConst; -public: static inline const void *getAsVoidPointer(const T P) { return NonConst::getAsVoidPointer(P); } @@ -75,10 +87,9 @@ public: }; // Provide PointerLikeTypeTraits for const pointers. -template <typename T> class PointerLikeTypeTraits<const T *> { +template <typename T> struct PointerLikeTypeTraits<const T *> { typedef PointerLikeTypeTraits<T *> NonConst; -public: static inline const void *getAsVoidPointer(const T *P) { return NonConst::getAsVoidPointer(const_cast<T *>(P)); } @@ -89,8 +100,7 @@ public: }; // Provide PointerLikeTypeTraits for uintptr_t. -template <> class PointerLikeTypeTraits<uintptr_t> { -public: +template <> struct PointerLikeTypeTraits<uintptr_t> { static inline void *getAsVoidPointer(uintptr_t P) { return reinterpret_cast<void *>(P); } diff --git a/include/llvm/Support/Printable.h b/include/llvm/Support/Printable.h index 28e875e8ff5e..cb55d41316e3 100644 --- a/include/llvm/Support/Printable.h +++ b/include/llvm/Support/Printable.h @@ -42,7 +42,7 @@ public: : Print(std::move(Print)) {} }; -static inline raw_ostream &operator<<(raw_ostream &OS, const Printable &P) { +inline raw_ostream &operator<<(raw_ostream &OS, const Printable &P) { P.Print(OS); return OS; } diff --git a/include/llvm/Support/Process.h b/include/llvm/Support/Process.h index 780c7e2ddd6f..82b0d9f6ba28 100644 --- a/include/llvm/Support/Process.h +++ b/include/llvm/Support/Process.h @@ -80,9 +80,15 @@ public: /// This function searches for an existing file in the list of directories /// in a PATH like environment variable, and returns the first file found, /// according to the order of the entries in the PATH like environment - /// variable. - static Optional<std::string> FindInEnvPath(const std::string& EnvName, - const std::string& FileName); + /// variable. If an ignore list is specified, then any folder which is in + /// the PATH like environment variable but is also in IgnoreList is not + /// considered. + static Optional<std::string> FindInEnvPath(StringRef EnvName, + StringRef FileName, + ArrayRef<std::string> IgnoreList); + + static Optional<std::string> FindInEnvPath(StringRef EnvName, + StringRef FileName); /// This function returns a SmallVector containing the arguments passed from /// the operating system to the program. This function expects to be handed diff --git a/include/llvm/Support/Program.h b/include/llvm/Support/Program.h index 055f016d8243..06fd35078145 100644 --- a/include/llvm/Support/Program.h +++ b/include/llvm/Support/Program.h @@ -15,12 +15,12 @@ #define LLVM_SUPPORT_PROGRAM_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorOr.h" #include <system_error> namespace llvm { -class StringRef; - namespace sys { /// This is the OS-specific separator for PATH like environment variables: @@ -69,7 +69,7 @@ struct ProcessInfo { /// \returns The fully qualified path to the first \p Name in \p Paths if it /// exists. \p Name if \p Name has slashes in it. Otherwise an error. ErrorOr<std::string> - findProgramByName(StringRef Name, ArrayRef<StringRef> Paths = None); + findProgramByName(StringRef Name, ArrayRef<StringRef> Paths = {}); // These functions change the specified standard stream (stdin or stdout) to // binary mode. They return errc::success if the specified stream @@ -84,33 +84,33 @@ struct ProcessInfo { /// This function waits for the program to finish, so should be avoided in /// library functions that aren't expected to block. Consider using /// ExecuteNoWait() instead. - /// @returns an integer result code indicating the status of the program. + /// \returns an integer result code indicating the status of the program. /// A zero or positive value indicates the result code of the program. /// -1 indicates failure to execute /// -2 indicates a crash during execution or timeout int ExecuteAndWait( StringRef Program, ///< Path of the program to be executed. It is ///< presumed this is the result of the findProgramByName method. - const char **args, ///< A vector of strings that are passed to the + const char **Args, ///< A vector of strings that are passed to the ///< program. The first element should be the name of the program. ///< The list *must* be terminated by a null char* entry. - const char **env = nullptr, ///< An optional vector of strings to use for + const char **Env = nullptr, ///< An optional vector of strings to use for ///< the program's environment. If not provided, the current program's ///< environment will be used. - const StringRef **redirects = nullptr, ///< An optional array of pointers - ///< to paths. If the array is null, no redirection is done. The array - ///< should have a size of at least three. The inferior process's - ///< stdin(0), stdout(1), and stderr(2) will be redirected to the - ///< corresponding paths. - ///< When an empty path is passed in, the corresponding file - ///< descriptor will be disconnected (ie, /dev/null'd) in a portable - ///< way. - unsigned secondsToWait = 0, ///< If non-zero, this specifies the amount + ArrayRef<Optional<StringRef>> Redirects = {}, ///< + ///< An array of optional paths. Should have a size of zero or three. + ///< If the array is empty, no redirections are performed. + ///< Otherwise, the inferior process's stdin(0), stdout(1), and stderr(2) + ///< will be redirected to the corresponding paths, if the optional path + ///< is present (not \c llvm::None). + ///< When an empty path is passed in, the corresponding file descriptor + ///< will be disconnected (ie, /dev/null'd) in a portable way. + unsigned SecondsToWait = 0, ///< If non-zero, this specifies the amount ///< of time to wait for the child process to exit. If the time ///< expires, the child is killed and this call returns. If zero, ///< this function will wait until the child finishes or forever if ///< it doesn't. - unsigned memoryLimit = 0, ///< If non-zero, this specifies max. amount + unsigned MemoryLimit = 0, ///< If non-zero, this specifies max. amount ///< of memory can be allocated by process. If memory usage will be ///< higher limit, the child is killed and this call returns. If zero ///< - no memory limit. @@ -122,17 +122,20 @@ struct ProcessInfo { /// Similar to ExecuteAndWait, but returns immediately. /// @returns The \see ProcessInfo of the newly launced process. - /// \note On Microsoft Windows systems, users will need to either call \see - /// Wait until the process finished execution or win32 CloseHandle() API on - /// ProcessInfo.ProcessHandle to avoid memory leaks. - ProcessInfo - ExecuteNoWait(StringRef Program, const char **args, const char **env = nullptr, - const StringRef **redirects = nullptr, unsigned memoryLimit = 0, - std::string *ErrMsg = nullptr, bool *ExecutionFailed = nullptr); + /// \note On Microsoft Windows systems, users will need to either call + /// \see Wait until the process finished execution or win32 CloseHandle() API + /// on ProcessInfo.ProcessHandle to avoid memory leaks. + ProcessInfo ExecuteNoWait(StringRef Program, const char **Args, + const char **Env = nullptr, + ArrayRef<Optional<StringRef>> Redirects = {}, + unsigned MemoryLimit = 0, + std::string *ErrMsg = nullptr, + bool *ExecutionFailed = nullptr); /// Return true if the given arguments fit within system-specific /// argument length limits. - bool commandLineFitsWithinSystemLimits(StringRef Program, ArrayRef<const char*> Args); + bool commandLineFitsWithinSystemLimits(StringRef Program, + ArrayRef<const char *> Args); /// File encoding options when writing contents that a non-UTF8 tool will /// read (on Windows systems). For UNIX, we always use UTF-8. diff --git a/include/llvm/Support/ReverseIteration.h b/include/llvm/Support/ReverseIteration.h index cb97b60f06dd..5e0238d81c4c 100644 --- a/include/llvm/Support/ReverseIteration.h +++ b/include/llvm/Support/ReverseIteration.h @@ -2,16 +2,18 @@ #define LLVM_SUPPORT_REVERSEITERATION_H #include "llvm/Config/abi-breaking.h" +#include "llvm/Support/PointerLikeTypeTraits.h" namespace llvm { -#if LLVM_ENABLE_ABI_BREAKING_CHECKS -template <class T = void> struct ReverseIterate { static bool value; }; + +template<class T = void *> +bool shouldReverseIterate() { #if LLVM_ENABLE_REVERSE_ITERATION -template <class T> bool ReverseIterate<T>::value = true; + return detail::IsPointerLike<T>::value; #else -template <class T> bool ReverseIterate<T>::value = false; -#endif + return false; #endif } +} #endif diff --git a/include/llvm/Support/ScaledNumber.h b/include/llvm/Support/ScaledNumber.h index 910174732994..cfbdbc751617 100644 --- a/include/llvm/Support/ScaledNumber.h +++ b/include/llvm/Support/ScaledNumber.h @@ -504,13 +504,13 @@ private: static_assert(Width <= 64, "invalid integer width for digits"); private: - DigitsType Digits; - int16_t Scale; + DigitsType Digits = 0; + int16_t Scale = 0; public: - ScaledNumber() : Digits(0), Scale(0) {} + ScaledNumber() = default; - ScaledNumber(DigitsType Digits, int16_t Scale) + constexpr ScaledNumber(DigitsType Digits, int16_t Scale) : Digits(Digits), Scale(Scale) {} private: diff --git a/include/llvm/Support/SourceMgr.h b/include/llvm/Support/SourceMgr.h index 399f8dcd76fc..c08bf858760a 100644 --- a/include/llvm/Support/SourceMgr.h +++ b/include/llvm/Support/SourceMgr.h @@ -43,7 +43,8 @@ public: enum DiagKind { DK_Error, DK_Warning, - DK_Note + DK_Remark, + DK_Note, }; /// Clients that want to handle their own diagnostics in a custom way can diff --git a/include/llvm/Support/SpecialCaseList.h b/include/llvm/Support/SpecialCaseList.h index ce693c501312..fd62fc48047b 100644 --- a/include/llvm/Support/SpecialCaseList.h +++ b/include/llvm/Support/SpecialCaseList.h @@ -8,15 +8,19 @@ // // This is a utility class used to parse user-provided text files with // "special case lists" for code sanitizers. Such files are used to -// define "ABI list" for DataFlowSanitizer and blacklists for another sanitizers +// define an "ABI list" for DataFlowSanitizer and blacklists for sanitizers // like AddressSanitizer or UndefinedBehaviorSanitizer. // -// Empty lines and lines starting with "#" are ignored. All the rest lines -// should have the form: -// section:wildcard_expression[=category] +// Empty lines and lines starting with "#" are ignored. Sections are defined +// using a '[section_name]' header and can be used to specify sanitizers the +// entries below it apply to. Section names are regular expressions, and +// entries without a section header match all sections (e.g. an '[*]' header +// is assumed.) +// The remaining lines should have the form: +// prefix:wildcard_expression[=category] // If category is not specified, it is assumed to be empty string. -// Definitions of "section" and "category" are sanitizer-specific. For example, -// sanitizer blacklists support sections "src", "fun" and "global". +// Definitions of "prefix" and "category" are sanitizer-specific. For example, +// sanitizer blacklists support prefixes "src", "fun" and "global". // Wildcard expressions define, respectively, source files, functions or // globals which shouldn't be instrumented. // Examples of categories: @@ -26,6 +30,7 @@ // detection for certain globals or source files. // Full special case list file example: // --- +// [address] // # Blacklisted items: // fun:*_ZN4base6subtle* // global:*global_with_bad_access_or_initialization* @@ -34,14 +39,13 @@ // src:file_with_tricky_code.cc // src:ignore-global-initializers-issues.cc=init // +// [dataflow] // # Functions with pure functional semantics: // fun:cos=functional // fun:sin=functional // --- // Note that the wild card is in fact an llvm::Regex, but * is automatically // replaced with .* -// This is similar to the "ignore" feature of ThreadSanitizer. -// http://code.google.com/p/data-race-test/wiki/ThreadSanitizerIgnores // //===----------------------------------------------------------------------===// @@ -49,6 +53,9 @@ #define LLVM_SUPPORT_SPECIALCASELIST_H #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/TrigramIndex.h" #include <string> #include <vector> @@ -76,26 +83,70 @@ public: /// Returns true, if special case list contains a line /// \code - /// @Section:<E>=@Category + /// @Prefix:<E>=@Category /// \endcode - /// and @Query satisfies a wildcard expression <E>. - bool inSection(StringRef Section, StringRef Query, + /// where @Query satisfies wildcard expression <E> in a given @Section. + bool inSection(StringRef Section, StringRef Prefix, StringRef Query, StringRef Category = StringRef()) const; -private: + /// Returns the line number corresponding to the special case list entry if + /// the special case list contains a line + /// \code + /// @Prefix:<E>=@Category + /// \endcode + /// where @Query satisfies wildcard expression <E> in a given @Section. + /// Returns zero if there is no blacklist entry corresponding to this + /// expression. + unsigned inSectionBlame(StringRef Section, StringRef Prefix, StringRef Query, + StringRef Category = StringRef()) const; + +protected: + // Implementations of the create*() functions that can also be used by derived + // classes. + bool createInternal(const std::vector<std::string> &Paths, + std::string &Error); + bool createInternal(const MemoryBuffer *MB, std::string &Error); + + SpecialCaseList() = default; SpecialCaseList(SpecialCaseList const &) = delete; SpecialCaseList &operator=(SpecialCaseList const &) = delete; - struct Entry; - StringMap<StringMap<Entry>> Entries; - StringMap<StringMap<std::string>> Regexps; - bool IsCompiled; + /// Represents a set of regular expressions. Regular expressions which are + /// "literal" (i.e. no regex metacharacters) are stored in Strings. The + /// reason for doing so is efficiency; StringMap is much faster at matching + /// literal strings than Regex. + class Matcher { + public: + bool insert(std::string Regexp, unsigned LineNumber, std::string &REError); + // Returns the line number in the source file that this query matches to. + // Returns zero if no match is found. + unsigned match(StringRef Query) const; + + private: + StringMap<unsigned> Strings; + TrigramIndex Trigrams; + std::vector<std::pair<std::unique_ptr<Regex>, unsigned>> RegExes; + }; + + using SectionEntries = StringMap<StringMap<Matcher>>; + + struct Section { + Section(std::unique_ptr<Matcher> M) : SectionMatcher(std::move(M)){}; + + std::unique_ptr<Matcher> SectionMatcher; + SectionEntries Entries; + }; + + std::vector<Section> Sections; - SpecialCaseList(); /// Parses just-constructed SpecialCaseList entries from a memory buffer. - bool parse(const MemoryBuffer *MB, std::string &Error); - /// compile() should be called once, after parsing all the memory buffers. - void compile(); + bool parse(const MemoryBuffer *MB, StringMap<size_t> &SectionsMap, + std::string &Error); + + // Helper method for derived classes to search by Prefix, Query, and Category + // once they have already resolved a section entry. + unsigned inSectionBlame(const SectionEntries &Entries, StringRef Prefix, + StringRef Query, StringRef Category) const; }; } // namespace llvm diff --git a/include/llvm/Support/TarWriter.h b/include/llvm/Support/TarWriter.h index 44bdcaf2c465..639f61b53892 100644 --- a/include/llvm/Support/TarWriter.h +++ b/include/llvm/Support/TarWriter.h @@ -11,6 +11,7 @@ #define LLVM_SUPPORT_TAR_WRITER_H #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" @@ -26,6 +27,7 @@ private: TarWriter(int FD, StringRef BaseDir); raw_fd_ostream OS; std::string BaseDir; + StringSet<> Files; }; } diff --git a/include/llvm/Support/TargetParser.h b/include/llvm/Support/TargetParser.h index e13582f6a6d3..13b7befb8ce4 100644 --- a/include/llvm/Support/TargetParser.h +++ b/include/llvm/Support/TargetParser.h @@ -31,61 +31,61 @@ class StringRef; // back-end to TableGen to create these clean tables. namespace ARM { -// FPU names. -enum FPUKind { -#define ARM_FPU(NAME, KIND, VERSION, NEON_SUPPORT, RESTRICTION) KIND, -#include "ARMTargetParser.def" - FK_LAST +// FPU Version +enum class FPUVersion { + NONE, + VFPV2, + VFPV3, + VFPV3_FP16, + VFPV4, + VFPV5 }; -// FPU Version -enum FPUVersion { - FV_NONE = 0, - FV_VFPV2, - FV_VFPV3, - FV_VFPV3_FP16, - FV_VFPV4, - FV_VFPV5 +// An FPU name restricts the FPU in one of three ways: +enum class FPURestriction { + None = 0, ///< No restriction + D16, ///< Only 16 D registers + SP_D16 ///< Only single-precision instructions, with 16 D registers }; // An FPU name implies one of three levels of Neon support: -enum NeonSupportLevel { - NS_None = 0, ///< No Neon - NS_Neon, ///< Neon - NS_Crypto ///< Neon with Crypto +enum class NeonSupportLevel { + None = 0, ///< No Neon + Neon, ///< Neon + Crypto ///< Neon with Crypto }; -// An FPU name restricts the FPU in one of three ways: -enum FPURestriction { - FR_None = 0, ///< No restriction - FR_D16, ///< Only 16 D registers - FR_SP_D16 ///< Only single-precision instructions, with 16 D registers +// FPU names. +enum FPUKind { +#define ARM_FPU(NAME, KIND, VERSION, NEON_SUPPORT, RESTRICTION) KIND, +#include "ARMTargetParser.def" + FK_LAST }; // Arch names. -enum ArchKind { +enum class ArchKind { #define ARM_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, ARCH_BASE_EXT) ID, #include "ARMTargetParser.def" - AK_LAST }; // Arch extension modifiers for CPUs. enum ArchExtKind : unsigned { - AEK_INVALID = 0x0, - AEK_NONE = 0x1, - AEK_CRC = 0x2, - AEK_CRYPTO = 0x4, - AEK_FP = 0x8, - AEK_HWDIVTHUMB = 0x10, - AEK_HWDIVARM = 0x20, - AEK_MP = 0x40, - AEK_SIMD = 0x80, - AEK_SEC = 0x100, - AEK_VIRT = 0x200, - AEK_DSP = 0x400, - AEK_FP16 = 0x800, - AEK_RAS = 0x1000, - AEK_SVE = 0x2000, + AEK_INVALID = 0, + AEK_NONE = 1, + AEK_CRC = 1 << 1, + AEK_CRYPTO = 1 << 2, + AEK_FP = 1 << 3, + AEK_HWDIVTHUMB = 1 << 4, + AEK_HWDIVARM = 1 << 5, + AEK_MP = 1 << 6, + AEK_SIMD = 1 << 7, + AEK_SEC = 1 << 8, + AEK_VIRT = 1 << 9, + AEK_DSP = 1 << 10, + AEK_FP16 = 1 << 11, + AEK_RAS = 1 << 12, + AEK_SVE = 1 << 13, + AEK_DOTPROD = 1 << 14, // Unsupported extensions. AEK_OS = 0x8000000, AEK_IWMMXT = 0x10000000, @@ -95,22 +95,22 @@ enum ArchExtKind : unsigned { }; // ISA kinds. -enum ISAKind { IK_INVALID = 0, IK_ARM, IK_THUMB, IK_AARCH64 }; +enum class ISAKind { INVALID = 0, ARM, THUMB, AARCH64 }; // Endianness // FIXME: BE8 vs. BE32? -enum EndianKind { EK_INVALID = 0, EK_LITTLE, EK_BIG }; +enum class EndianKind { INVALID = 0, LITTLE, BIG }; // v6/v7/v8 Profile -enum ProfileKind { PK_INVALID = 0, PK_A, PK_R, PK_M }; +enum class ProfileKind { INVALID = 0, A, R, M }; StringRef getCanonicalArchName(StringRef Arch); // Information by ID StringRef getFPUName(unsigned FPUKind); -unsigned getFPUVersion(unsigned FPUKind); -unsigned getFPUNeonSupportLevel(unsigned FPUKind); -unsigned getFPURestriction(unsigned FPUKind); +FPUVersion getFPUVersion(unsigned FPUKind); +NeonSupportLevel getFPUNeonSupportLevel(unsigned FPUKind); +FPURestriction getFPURestriction(unsigned FPUKind); // FIXME: These should be moved to TargetTuple once it exists bool getFPUFeatures(unsigned FPUKind, std::vector<StringRef> &Features); @@ -118,28 +118,28 @@ bool getHWDivFeatures(unsigned HWDivKind, std::vector<StringRef> &Features); bool getExtensionFeatures(unsigned Extensions, std::vector<StringRef> &Features); -StringRef getArchName(unsigned ArchKind); -unsigned getArchAttr(unsigned ArchKind); -StringRef getCPUAttr(unsigned ArchKind); -StringRef getSubArch(unsigned ArchKind); +StringRef getArchName(ArchKind AK); +unsigned getArchAttr(ArchKind AK); +StringRef getCPUAttr(ArchKind AK); +StringRef getSubArch(ArchKind AK); StringRef getArchExtName(unsigned ArchExtKind); StringRef getArchExtFeature(StringRef ArchExt); StringRef getHWDivName(unsigned HWDivKind); // Information by Name -unsigned getDefaultFPU(StringRef CPU, unsigned ArchKind); -unsigned getDefaultExtensions(StringRef CPU, unsigned ArchKind); +unsigned getDefaultFPU(StringRef CPU, ArchKind AK); +unsigned getDefaultExtensions(StringRef CPU, ArchKind AK); StringRef getDefaultCPU(StringRef Arch); // Parser unsigned parseHWDiv(StringRef HWDiv); unsigned parseFPU(StringRef FPU); -unsigned parseArch(StringRef Arch); +ArchKind parseArch(StringRef Arch); unsigned parseArchExt(StringRef ArchExt); -unsigned parseCPUArch(StringRef CPU); -unsigned parseArchISA(StringRef Arch); -unsigned parseArchEndian(StringRef Arch); -unsigned parseArchProfile(StringRef Arch); +ArchKind parseCPUArch(StringRef CPU); +ISAKind parseArchISA(StringRef Arch); +EndianKind parseArchEndian(StringRef Arch); +ProfileKind parseArchProfile(StringRef Arch); unsigned parseArchVersion(StringRef Arch); StringRef computeDefaultTargetABI(const Triple &TT, StringRef CPU); @@ -153,62 +153,108 @@ namespace AArch64 { enum class ArchKind { #define AARCH64_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, ARCH_BASE_EXT) ID, #include "AArch64TargetParser.def" - AK_LAST }; // Arch extension modifiers for CPUs. enum ArchExtKind : unsigned { - AEK_INVALID = 0x0, - AEK_NONE = 0x1, - AEK_CRC = 0x2, - AEK_CRYPTO = 0x4, - AEK_FP = 0x8, - AEK_SIMD = 0x10, - AEK_FP16 = 0x20, - AEK_PROFILE = 0x40, - AEK_RAS = 0x80, - AEK_LSE = 0x100, - AEK_SVE = 0x200 + AEK_INVALID = 0, + AEK_NONE = 1, + AEK_CRC = 1 << 1, + AEK_CRYPTO = 1 << 2, + AEK_FP = 1 << 3, + AEK_SIMD = 1 << 4, + AEK_FP16 = 1 << 5, + AEK_PROFILE = 1 << 6, + AEK_RAS = 1 << 7, + AEK_LSE = 1 << 8, + AEK_SVE = 1 << 9, + AEK_DOTPROD = 1 << 10, + AEK_RCPC = 1 << 11, + AEK_RDM = 1 << 12 }; StringRef getCanonicalArchName(StringRef Arch); // Information by ID StringRef getFPUName(unsigned FPUKind); -unsigned getFPUVersion(unsigned FPUKind); -unsigned getFPUNeonSupportLevel(unsigned FPUKind); -unsigned getFPURestriction(unsigned FPUKind); +ARM::FPUVersion getFPUVersion(unsigned FPUKind); +ARM::NeonSupportLevel getFPUNeonSupportLevel(unsigned FPUKind); +ARM::FPURestriction getFPURestriction(unsigned FPUKind); // FIXME: These should be moved to TargetTuple once it exists bool getFPUFeatures(unsigned FPUKind, std::vector<StringRef> &Features); bool getExtensionFeatures(unsigned Extensions, std::vector<StringRef> &Features); -bool getArchFeatures(unsigned ArchKind, std::vector<StringRef> &Features); +bool getArchFeatures(ArchKind AK, std::vector<StringRef> &Features); -StringRef getArchName(unsigned ArchKind); -unsigned getArchAttr(unsigned ArchKind); -StringRef getCPUAttr(unsigned ArchKind); -StringRef getSubArch(unsigned ArchKind); +StringRef getArchName(ArchKind AK); +unsigned getArchAttr(ArchKind AK); +StringRef getCPUAttr(ArchKind AK); +StringRef getSubArch(ArchKind AK); StringRef getArchExtName(unsigned ArchExtKind); StringRef getArchExtFeature(StringRef ArchExt); unsigned checkArchVersion(StringRef Arch); // Information by Name -unsigned getDefaultFPU(StringRef CPU, unsigned ArchKind); -unsigned getDefaultExtensions(StringRef CPU, unsigned ArchKind); +unsigned getDefaultFPU(StringRef CPU, ArchKind AK); +unsigned getDefaultExtensions(StringRef CPU, ArchKind AK); StringRef getDefaultCPU(StringRef Arch); // Parser unsigned parseFPU(StringRef FPU); -unsigned parseArch(StringRef Arch); +AArch64::ArchKind parseArch(StringRef Arch); unsigned parseArchExt(StringRef ArchExt); -unsigned parseCPUArch(StringRef CPU); -unsigned parseArchISA(StringRef Arch); -unsigned parseArchEndian(StringRef Arch); -unsigned parseArchProfile(StringRef Arch); +ArchKind parseCPUArch(StringRef CPU); +ARM::ISAKind parseArchISA(StringRef Arch); +ARM::EndianKind parseArchEndian(StringRef Arch); +ARM::ProfileKind parseArchProfile(StringRef Arch); unsigned parseArchVersion(StringRef Arch); } // namespace AArch64 + +namespace X86 { + +// This should be kept in sync with libcc/compiler-rt as its included by clang +// as a proxy for what's in libgcc/compiler-rt. +enum ProcessorVendors : unsigned { + VENDOR_DUMMY, +#define X86_VENDOR(ENUM, STRING) \ + ENUM, +#include "llvm/Support/X86TargetParser.def" + VENDOR_OTHER +}; + +// This should be kept in sync with libcc/compiler-rt as its included by clang +// as a proxy for what's in libgcc/compiler-rt. +enum ProcessorTypes : unsigned { + CPU_TYPE_DUMMY, +#define X86_CPU_TYPE(ARCHNAME, ENUM) \ + ENUM, +#include "llvm/Support/X86TargetParser.def" + CPU_TYPE_MAX +}; + +// This should be kept in sync with libcc/compiler-rt as its included by clang +// as a proxy for what's in libgcc/compiler-rt. +enum ProcessorSubtypes : unsigned { + CPU_SUBTYPE_DUMMY, +#define X86_CPU_SUBTYPE(ARCHNAME, ENUM) \ + ENUM, +#include "llvm/Support/X86TargetParser.def" + CPU_SUBTYPE_MAX +}; + +// This should be kept in sync with libcc/compiler-rt as it should be used +// by clang as a proxy for what's in libgcc/compiler-rt. +enum ProcessorFeatures { +#define X86_FEATURE(VAL, ENUM) \ + ENUM = VAL, +#include "llvm/Support/X86TargetParser.def" + +}; + +} // namespace X86 + } // namespace llvm #endif diff --git a/include/llvm/Support/TargetRegistry.h b/include/llvm/Support/TargetRegistry.h index 8454b27b6f04..bd096e2f74f6 100644 --- a/include/llvm/Support/TargetRegistry.h +++ b/include/llvm/Support/TargetRegistry.h @@ -67,15 +67,21 @@ MCStreamer *createAsmStreamer(MCContext &Ctx, MCAsmBackend *TAB, bool ShowInst); /// Takes ownership of \p TAB and \p CE. -MCStreamer *createELFStreamer(MCContext &Ctx, MCAsmBackend &TAB, - raw_pwrite_stream &OS, MCCodeEmitter *CE, +MCStreamer *createELFStreamer(MCContext &Ctx, + std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&CE, bool RelaxAll); -MCStreamer *createMachOStreamer(MCContext &Ctx, MCAsmBackend &TAB, - raw_pwrite_stream &OS, MCCodeEmitter *CE, +MCStreamer *createMachOStreamer(MCContext &Ctx, + std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&CE, bool RelaxAll, bool DWARFMustBeAtTheEnd, bool LabelSections = false); -MCStreamer *createWasmStreamer(MCContext &Ctx, MCAsmBackend &TAB, - raw_pwrite_stream &OS, MCCodeEmitter *CE, +MCStreamer *createWasmStreamer(MCContext &Ctx, + std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&CE, bool RelaxAll); MCRelocationInfo *createMCRelocationInfo(const Triple &TT, MCContext &Ctx); @@ -101,19 +107,16 @@ public: using MCAsmInfoCtorFnTy = MCAsmInfo *(*)(const MCRegisterInfo &MRI, const Triple &TT); - using MCAdjustCodeGenOptsFnTy = void (*)(const Triple &TT, Reloc::Model RM, - CodeModel::Model &CM); - using MCInstrInfoCtorFnTy = MCInstrInfo *(*)(); using MCInstrAnalysisCtorFnTy = MCInstrAnalysis *(*)(const MCInstrInfo *Info); using MCRegInfoCtorFnTy = MCRegisterInfo *(*)(const Triple &TT); using MCSubtargetInfoCtorFnTy = MCSubtargetInfo *(*)(const Triple &TT, StringRef CPU, StringRef Features); - using TargetMachineCtorTy = TargetMachine *(*)( - const Target &T, const Triple &TT, StringRef CPU, StringRef Features, - const TargetOptions &Options, Optional<Reloc::Model> RM, - CodeModel::Model CM, CodeGenOpt::Level OL); + using TargetMachineCtorTy = TargetMachine + *(*)(const Target &T, const Triple &TT, StringRef CPU, StringRef Features, + const TargetOptions &Options, Optional<Reloc::Model> RM, + Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT); // If it weren't for layering issues (this header is in llvm/Support, but // depends on MC?) this should take the Streamer by value rather than rvalue // reference. @@ -137,26 +140,26 @@ public: using MCCodeEmitterCtorTy = MCCodeEmitter *(*)(const MCInstrInfo &II, const MCRegisterInfo &MRI, MCContext &Ctx); - using ELFStreamerCtorTy = MCStreamer *(*)(const Triple &T, MCContext &Ctx, - MCAsmBackend &TAB, - raw_pwrite_stream &OS, - MCCodeEmitter *Emitter, - bool RelaxAll); - using MachOStreamerCtorTy = MCStreamer *(*)(MCContext &Ctx, MCAsmBackend &TAB, - raw_pwrite_stream &OS, - MCCodeEmitter *Emitter, - bool RelaxAll, - bool DWARFMustBeAtTheEnd); - using COFFStreamerCtorTy = MCStreamer *(*)(MCContext &Ctx, MCAsmBackend &TAB, - raw_pwrite_stream &OS, - MCCodeEmitter *Emitter, - bool RelaxAll, - bool IncrementalLinkerCompatible); - using WasmStreamerCtorTy = MCStreamer *(*)(const Triple &T, MCContext &Ctx, - MCAsmBackend &TAB, - raw_pwrite_stream &OS, - MCCodeEmitter *Emitter, - bool RelaxAll); + using ELFStreamerCtorTy = + MCStreamer *(*)(const Triple &T, MCContext &Ctx, + std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll); + using MachOStreamerCtorTy = + MCStreamer *(*)(MCContext &Ctx, std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll, + bool DWARFMustBeAtTheEnd); + using COFFStreamerCtorTy = + MCStreamer *(*)(MCContext &Ctx, std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll, + bool IncrementalLinkerCompatible); + using WasmStreamerCtorTy = + MCStreamer *(*)(const Triple &T, MCContext &Ctx, + std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll); using NullTargetStreamerCtorTy = MCTargetStreamer *(*)(MCStreamer &S); using AsmTargetStreamerCtorTy = MCTargetStreamer *(*)( MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrint, @@ -184,6 +187,10 @@ private: /// ShortDesc - A short description of the target. const char *ShortDesc; + /// BackendName - The name of the backend implementation. This must match the + /// name of the 'def X : Target ...' in TableGen. + const char *BackendName; + /// HasJIT - Whether this target supports the JIT. bool HasJIT; @@ -191,8 +198,6 @@ private: /// registered. MCAsmInfoCtorFnTy MCAsmInfoCtorFn; - MCAdjustCodeGenOptsFnTy MCAdjustCodeGenOptsFn; - /// MCInstrInfoCtorFn - Constructor function for this target's MCInstrInfo, /// if registered. MCInstrInfoCtorFnTy MCInstrInfoCtorFn; @@ -278,6 +283,9 @@ public: /// getShortDescription - Get a short description of the target. const char *getShortDescription() const { return ShortDesc; } + /// getBackendName - Get the backend name. + const char *getBackendName() const { return BackendName; } + /// @} /// @name Feature Predicates /// @{ @@ -312,12 +320,6 @@ public: return MCAsmInfoCtorFn(MRI, Triple(TheTriple)); } - void adjustCodeGenOpts(const Triple &TT, Reloc::Model RM, - CodeModel::Model &CM) const { - if (MCAdjustCodeGenOptsFn) - MCAdjustCodeGenOptsFn(TT, RM, CM); - } - /// createMCInstrInfo - Create a MCInstrInfo implementation. /// MCInstrInfo *createMCInstrInfo() const { @@ -365,15 +367,17 @@ public: /// feature set; it should always be provided. Generally this should be /// either the target triple from the module, or the target triple of the /// host if that does not exist. - TargetMachine * - createTargetMachine(StringRef TT, StringRef CPU, StringRef Features, - const TargetOptions &Options, Optional<Reloc::Model> RM, - CodeModel::Model CM = CodeModel::Default, - CodeGenOpt::Level OL = CodeGenOpt::Default) const { + TargetMachine *createTargetMachine(StringRef TT, StringRef CPU, + StringRef Features, + const TargetOptions &Options, + Optional<Reloc::Model> RM, + Optional<CodeModel::Model> CM = None, + CodeGenOpt::Level OL = CodeGenOpt::Default, + bool JIT = false) const { if (!TargetMachineCtorFn) return nullptr; return TargetMachineCtorFn(*this, Triple(TT), CPU, Features, Options, RM, - CM, OL); + CM, OL, JIT); } /// createMCAsmBackend - Create a target specific assembly parser. @@ -444,8 +448,9 @@ public: /// \param Emitter The target independent assembler object.Takes ownership. /// \param RelaxAll Relax all fixups? MCStreamer *createMCObjectStreamer(const Triple &T, MCContext &Ctx, - MCAsmBackend &TAB, raw_pwrite_stream &OS, - MCCodeEmitter *Emitter, + std::unique_ptr<MCAsmBackend> &&TAB, + raw_pwrite_stream &OS, + std::unique_ptr<MCCodeEmitter> &&Emitter, const MCSubtargetInfo &STI, bool RelaxAll, bool IncrementalLinkerCompatible, bool DWARFMustBeAtTheEnd) const { @@ -455,28 +460,32 @@ public: llvm_unreachable("Unknown object format"); case Triple::COFF: assert(T.isOSWindows() && "only Windows COFF is supported"); - S = COFFStreamerCtorFn(Ctx, TAB, OS, Emitter, RelaxAll, - IncrementalLinkerCompatible); + S = COFFStreamerCtorFn(Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll, IncrementalLinkerCompatible); break; case Triple::MachO: if (MachOStreamerCtorFn) - S = MachOStreamerCtorFn(Ctx, TAB, OS, Emitter, RelaxAll, - DWARFMustBeAtTheEnd); + S = MachOStreamerCtorFn(Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll, DWARFMustBeAtTheEnd); else - S = createMachOStreamer(Ctx, TAB, OS, Emitter, RelaxAll, - DWARFMustBeAtTheEnd); + S = createMachOStreamer(Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll, DWARFMustBeAtTheEnd); break; case Triple::ELF: if (ELFStreamerCtorFn) - S = ELFStreamerCtorFn(T, Ctx, TAB, OS, Emitter, RelaxAll); + S = ELFStreamerCtorFn(T, Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll); else - S = createELFStreamer(Ctx, TAB, OS, Emitter, RelaxAll); + S = createELFStreamer(Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll); break; case Triple::Wasm: if (WasmStreamerCtorFn) - S = WasmStreamerCtorFn(T, Ctx, TAB, OS, Emitter, RelaxAll); + S = WasmStreamerCtorFn(T, Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll); else - S = createWasmStreamer(Ctx, TAB, OS, Emitter, RelaxAll); + S = createWasmStreamer(Ctx, std::move(TAB), OS, std::move(Emitter), + RelaxAll); break; } if (ObjectTargetStreamerCtorFn) @@ -599,7 +608,7 @@ struct TargetRegistry { /// printRegisteredTargetsForVersion - Print the registered targets /// appropriately for inclusion in a tool's version output. - static void printRegisteredTargetsForVersion(); + static void printRegisteredTargetsForVersion(raw_ostream &OS); /// @name Registry Access /// @{ @@ -643,10 +652,15 @@ struct TargetRegistry { /// @param Name - The target name. This should be a static string. /// @param ShortDesc - A short target description. This should be a static /// string. + /// @param BackendName - The name of the backend. This should be a static + /// string that is the same for all targets that share a backend + /// implementation and must match the name used in the 'def X : Target ...' in + /// TableGen. /// @param ArchMatchFn - The arch match checking function for this target. /// @param HasJIT - Whether the target supports JIT code /// generation. static void RegisterTarget(Target &T, const char *Name, const char *ShortDesc, + const char *BackendName, Target::ArchMatchFnTy ArchMatchFn, bool HasJIT = false); @@ -663,11 +677,6 @@ struct TargetRegistry { T.MCAsmInfoCtorFn = Fn; } - static void registerMCAdjustCodeGenOpts(Target &T, - Target::MCAdjustCodeGenOptsFnTy Fn) { - T.MCAdjustCodeGenOptsFn = Fn; - } - /// RegisterMCInstrInfo - Register a MCInstrInfo implementation for the /// given target. /// @@ -881,13 +890,15 @@ struct TargetRegistry { /// } /// extern "C" void LLVMInitializeFooTargetInfo() { /// RegisterTarget<Triple::foo> X(getTheFooTarget(), "foo", "Foo -/// description"); +/// description", "Foo" /* Backend Name */); /// } template <Triple::ArchType TargetArchType = Triple::UnknownArch, bool HasJIT = false> struct RegisterTarget { - RegisterTarget(Target &T, const char *Name, const char *Desc) { - TargetRegistry::RegisterTarget(T, Name, Desc, &getArchMatch, HasJIT); + RegisterTarget(Target &T, const char *Name, const char *Desc, + const char *BackendName) { + TargetRegistry::RegisterTarget(T, Name, Desc, BackendName, &getArchMatch, + HasJIT); } static bool getArchMatch(Triple::ArchType Arch) { @@ -929,12 +940,6 @@ struct RegisterMCAsmInfoFn { } }; -struct RegisterMCAdjustCodeGenOptsFn { - RegisterMCAdjustCodeGenOptsFn(Target &T, Target::MCAdjustCodeGenOptsFnTy Fn) { - TargetRegistry::registerMCAdjustCodeGenOpts(T, Fn); - } -}; - /// RegisterMCInstrInfo - Helper template for registering a target instruction /// info implementation. This invokes the static "Create" method on the class /// to actually do the construction. Usage: @@ -1080,12 +1085,11 @@ template <class TargetMachineImpl> struct RegisterTargetMachine { } private: - static TargetMachine *Allocator(const Target &T, const Triple &TT, - StringRef CPU, StringRef FS, - const TargetOptions &Options, - Optional<Reloc::Model> RM, - CodeModel::Model CM, CodeGenOpt::Level OL) { - return new TargetMachineImpl(T, TT, CPU, FS, Options, RM, CM, OL); + static TargetMachine * + Allocator(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, + const TargetOptions &Options, Optional<Reloc::Model> RM, + Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT) { + return new TargetMachineImpl(T, TT, CPU, FS, Options, RM, CM, OL, JIT); } }; diff --git a/include/llvm/Support/ThreadPool.h b/include/llvm/Support/ThreadPool.h index 9ada946c6dae..fb8255900510 100644 --- a/include/llvm/Support/ThreadPool.h +++ b/include/llvm/Support/ThreadPool.h @@ -38,8 +38,8 @@ public: using TaskTy = std::function<void()>; using PackagedTaskTy = std::packaged_task<void()>; - /// Construct a pool with the number of core available on the system (or - /// whatever the value returned by std::thread::hardware_concurrency() is). + /// Construct a pool with the number of threads found by + /// hardware_concurrency(). ThreadPool(); /// Construct a pool of \p ThreadCount threads diff --git a/include/llvm/Support/Threading.h b/include/llvm/Support/Threading.h index 03963a24c107..6d813bccb93f 100644 --- a/include/llvm/Support/Threading.h +++ b/include/llvm/Support/Threading.h @@ -131,6 +131,14 @@ void llvm_execute_on_thread(void (*UserFn)(void *), void *UserData, /// Returns 1 when LLVM is configured with LLVM_ENABLE_THREADS=OFF unsigned heavyweight_hardware_concurrency(); + /// Get the number of threads that the current program can execute + /// concurrently. On some systems std::thread::hardware_concurrency() returns + /// the total number of cores, without taking affinity into consideration. + /// Returns 1 when LLVM is configured with LLVM_ENABLE_THREADS=OFF. + /// Fallback to std::thread::hardware_concurrency() if sched_getaffinity is + /// not available. + unsigned hardware_concurrency(); + /// \brief Return the current thread id, as used in various OS system calls. /// Note that not all platforms guarantee that the value returned will be /// unique across the entire system, so portable code should not assume diff --git a/include/llvm/Support/ToolOutputFile.h b/include/llvm/Support/ToolOutputFile.h index 1be26c2cb58b..b41ca5a6edaa 100644 --- a/include/llvm/Support/ToolOutputFile.h +++ b/include/llvm/Support/ToolOutputFile.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the tool_output_file class. +// This file defines the ToolOutputFile class. // //===----------------------------------------------------------------------===// @@ -21,9 +21,9 @@ namespace llvm { /// This class contains a raw_fd_ostream and adds a few extra features commonly /// needed for compiler-like tool output files: /// - The file is automatically deleted if the process is killed. -/// - The file is automatically deleted when the tool_output_file +/// - The file is automatically deleted when the ToolOutputFile /// object is destroyed unless the client calls keep(). -class tool_output_file { +class ToolOutputFile { /// This class is declared before the raw_fd_ostream so that it is constructed /// before the raw_fd_ostream is constructed and destructed after the /// raw_fd_ostream is destructed. It installs cleanups in its constructor and @@ -45,10 +45,10 @@ class tool_output_file { public: /// This constructor's arguments are passed to to raw_fd_ostream's /// constructor. - tool_output_file(StringRef Filename, std::error_code &EC, - sys::fs::OpenFlags Flags); + ToolOutputFile(StringRef Filename, std::error_code &EC, + sys::fs::OpenFlags Flags); - tool_output_file(StringRef Filename, int FD); + ToolOutputFile(StringRef Filename, int FD); /// Return the contained raw_fd_ostream. raw_fd_ostream &os() { return OS; } diff --git a/include/llvm/Support/X86TargetParser.def b/include/llvm/Support/X86TargetParser.def new file mode 100644 index 000000000000..5c8c576b1027 --- /dev/null +++ b/include/llvm/Support/X86TargetParser.def @@ -0,0 +1,155 @@ +//===- X86TargetParser.def - X86 target parsing defines ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides defines to build up the X86 target parser's logic. +// +//===----------------------------------------------------------------------===// + +// NOTE: NO INCLUDE GUARD DESIRED! + +#ifndef X86_VENDOR +#define X86_VENDOR(ENUM, STR) +#endif +X86_VENDOR(VENDOR_INTEL, "intel") +X86_VENDOR(VENDOR_AMD, "amd") +#undef X86_VENDOR + +// This macro is used to implement CPU types that have an alias. As of now +// there is only ever one alias. +#ifndef X86_CPU_TYPE_COMPAT_WITH_ALIAS +#define X86_CPU_TYPE_COMPAT_WITH_ALIAS(ARCHNAME, ENUM, STR, ALIAS) X86_CPU_TYPE_COMPAT(ARCHNAME, ENUM, STR) +#endif + +// This macro is used for cpu types present in compiler-rt/libgcc. +#ifndef X86_CPU_TYPE_COMPAT +#define X86_CPU_TYPE_COMPAT(ARCHNAME, ENUM, STR) X86_CPU_TYPE(ARCHNAME, ENUM) +#endif + +#ifndef X86_CPU_TYPE +#define X86_CPU_TYPE(ARCHNAME, ENUM) +#endif +X86_CPU_TYPE_COMPAT_WITH_ALIAS("bonnell", INTEL_BONNELL, "bonnell", "atom") +X86_CPU_TYPE_COMPAT ("core2", INTEL_CORE2, "core2") +X86_CPU_TYPE_COMPAT ("nehalem", INTEL_COREI7, "corei7") +X86_CPU_TYPE_COMPAT_WITH_ALIAS("amdfam10", AMDFAM10H, "amdfam10h", "amdfam10") +X86_CPU_TYPE_COMPAT_WITH_ALIAS("bdver1", AMDFAM15H, "amdfam15h", "amdfam15") +X86_CPU_TYPE_COMPAT_WITH_ALIAS("silvermont", INTEL_SILVERMONT, "silvermont", "slm") +X86_CPU_TYPE_COMPAT ("knl", INTEL_KNL, "knl") +X86_CPU_TYPE_COMPAT ("btver1", AMD_BTVER1, "btver1") +X86_CPU_TYPE_COMPAT ("btver2", AMD_BTVER2, "btver2") +X86_CPU_TYPE_COMPAT ("znver1", AMDFAM17H, "amdfam17h") +X86_CPU_TYPE_COMPAT ("knm", INTEL_KNM, "knm") +// Entries below this are not in libgcc/compiler-rt. +X86_CPU_TYPE ("i386", INTEL_i386) +X86_CPU_TYPE ("i486", INTEL_i486) +X86_CPU_TYPE ("pentium", INTEL_PENTIUM) +X86_CPU_TYPE ("pentium-mmx", INTEL_PENTIUM_MMX) +X86_CPU_TYPE ("pentiumpro", INTEL_PENTIUM_PRO) +X86_CPU_TYPE ("pentium2", INTEL_PENTIUM_II) +X86_CPU_TYPE ("pentium3", INTEL_PENTIUM_III) +X86_CPU_TYPE ("pentium4", INTEL_PENTIUM_IV) +X86_CPU_TYPE ("pentium-m", INTEL_PENTIUM_M) +X86_CPU_TYPE ("yonah", INTEL_CORE_DUO) +X86_CPU_TYPE ("nocona", INTEL_NOCONA) +X86_CPU_TYPE ("prescott", INTEL_PRESCOTT) +X86_CPU_TYPE ("i486", AMD_i486) +X86_CPU_TYPE ("pentium", AMDPENTIUM) +X86_CPU_TYPE ("athlon", AMD_ATHLON) +X86_CPU_TYPE ("athlon-xp", AMD_ATHLON_XP) +X86_CPU_TYPE ("k8", AMD_K8) +X86_CPU_TYPE ("k8-sse3", AMD_K8SSE3) +X86_CPU_TYPE ("goldmont", INTEL_GOLDMONT) +#undef X86_CPU_TYPE_COMPAT_WITH_ALIAS +#undef X86_CPU_TYPE_COMPAT +#undef X86_CPU_TYPE + +// This macro is used for cpu subtypes present in compiler-rt/libgcc. +#ifndef X86_CPU_SUBTYPE_COMPAT +#define X86_CPU_SUBTYPE_COMPAT(ARCHNAME, ENUM, STR) X86_CPU_SUBTYPE(ARCHNAME, ENUM) +#endif + +#ifndef X86_CPU_SUBTYPE +#define X86_CPU_SUBTYPE(ARCHNAME, ENUM) +#endif + +X86_CPU_SUBTYPE_COMPAT("nehalem", INTEL_COREI7_NEHALEM, "nehalem") +X86_CPU_SUBTYPE_COMPAT("westmere", INTEL_COREI7_WESTMERE, "westmere") +X86_CPU_SUBTYPE_COMPAT("sandybridge", INTEL_COREI7_SANDYBRIDGE, "sandybridge") +X86_CPU_SUBTYPE_COMPAT("amdfam10", AMDFAM10H_BARCELONA, "barcelona") +X86_CPU_SUBTYPE_COMPAT("amdfam10", AMDFAM10H_SHANGHAI, "shanghai") +X86_CPU_SUBTYPE_COMPAT("amdfam10", AMDFAM10H_ISTANBUL, "istanbul") +X86_CPU_SUBTYPE_COMPAT("bdver1", AMDFAM15H_BDVER1, "bdver1") +X86_CPU_SUBTYPE_COMPAT("bdver2", AMDFAM15H_BDVER2, "bdver2") +X86_CPU_SUBTYPE_COMPAT("bdver3", AMDFAM15H_BDVER3, "bdver3") +X86_CPU_SUBTYPE_COMPAT("bdver4", AMDFAM15H_BDVER4, "bdver4") +X86_CPU_SUBTYPE_COMPAT("znver1", AMDFAM17H_ZNVER1, "znver1") +X86_CPU_SUBTYPE_COMPAT("ivybridge", INTEL_COREI7_IVYBRIDGE, "ivybridge") +X86_CPU_SUBTYPE_COMPAT("haswell", INTEL_COREI7_HASWELL, "haswell") +X86_CPU_SUBTYPE_COMPAT("broadwell", INTEL_COREI7_BROADWELL, "broadwell") +X86_CPU_SUBTYPE_COMPAT("skylake", INTEL_COREI7_SKYLAKE, "skylake") +X86_CPU_SUBTYPE_COMPAT("skylake-avx512", INTEL_COREI7_SKYLAKE_AVX512, "skylake-avx512") +X86_CPU_SUBTYPE_COMPAT("cannonlake", INTEL_COREI7_CANNONLAKE, "cannonlake") +// Entries below this are not in libgcc/compiler-rt. +X86_CPU_SUBTYPE ("core2", INTEL_CORE2_65) +X86_CPU_SUBTYPE ("penryn", INTEL_CORE2_45) +X86_CPU_SUBTYPE ("k6", AMDPENTIUM_K6) +X86_CPU_SUBTYPE ("k6-2", AMDPENTIUM_K62) +X86_CPU_SUBTYPE ("k6-3", AMDPENTIUM_K63) +X86_CPU_SUBTYPE ("geode", AMDPENTIUM_GEODE) +#undef X86_CPU_SUBTYPE_COMPAT +#undef X86_CPU_SUBTYPE + + +// This macro is used for cpu types present in compiler-rt/libgcc. +#ifndef X86_FEATURE_COMPAT +#define X86_FEATURE_COMPAT(VAL, ENUM, STR) X86_FEATURE(VAL, ENUM) +#endif + +#ifndef X86_FEATURE +#define X86_FEATURE(VAL, ENUM) +#endif +X86_FEATURE_COMPAT( 0, FEATURE_CMOV, "cmov") +X86_FEATURE_COMPAT( 1, FEATURE_MMX, "mmx") +X86_FEATURE_COMPAT( 2, FEATURE_POPCNT, "popcnt") +X86_FEATURE_COMPAT( 3, FEATURE_SSE, "sse") +X86_FEATURE_COMPAT( 4, FEATURE_SSE2, "sse2") +X86_FEATURE_COMPAT( 5, FEATURE_SSE3, "sse3") +X86_FEATURE_COMPAT( 6, FEATURE_SSSE3, "ssse3") +X86_FEATURE_COMPAT( 7, FEATURE_SSE4_1, "sse4.1") +X86_FEATURE_COMPAT( 8, FEATURE_SSE4_2, "sse4.2") +X86_FEATURE_COMPAT( 9, FEATURE_AVX, "avx") +X86_FEATURE_COMPAT(10, FEATURE_AVX2, "avx2") +X86_FEATURE_COMPAT(11, FEATURE_SSE4_A, "sse4a") +X86_FEATURE_COMPAT(12, FEATURE_FMA4, "fma4") +X86_FEATURE_COMPAT(13, FEATURE_XOP, "xop") +X86_FEATURE_COMPAT(14, FEATURE_FMA, "fma") +X86_FEATURE_COMPAT(15, FEATURE_AVX512F, "avx512f") +X86_FEATURE_COMPAT(16, FEATURE_BMI, "bmi") +X86_FEATURE_COMPAT(17, FEATURE_BMI2, "bmi2") +X86_FEATURE_COMPAT(18, FEATURE_AES, "aes") +X86_FEATURE_COMPAT(19, FEATURE_PCLMUL, "pclmul") +X86_FEATURE_COMPAT(20, FEATURE_AVX512VL, "avx512vl") +X86_FEATURE_COMPAT(21, FEATURE_AVX512BW, "avx512bw") +X86_FEATURE_COMPAT(22, FEATURE_AVX512DQ, "avx512dq") +X86_FEATURE_COMPAT(23, FEATURE_AVX512CD, "avx512cd") +X86_FEATURE_COMPAT(24, FEATURE_AVX512ER, "avx512er") +X86_FEATURE_COMPAT(25, FEATURE_AVX512PF, "avx512pf") +X86_FEATURE_COMPAT(26, FEATURE_AVX512VBMI, "avx512vbmi") +X86_FEATURE_COMPAT(27, FEATURE_AVX512IFMA, "avx512ifma") +X86_FEATURE_COMPAT(28, FEATURE_AVX5124VNNIW, "avx5124vnniw") +X86_FEATURE_COMPAT(29, FEATURE_AVX5124FMAPS, "avx5124fmaps") +X86_FEATURE_COMPAT(30, FEATURE_AVX512VPOPCNTDQ, "avx512vpopcntdq") +// Features below here are not in libgcc/compiler-rt. +X86_FEATURE (32, FEATURE_MOVBE) +X86_FEATURE (33, FEATURE_ADX) +X86_FEATURE (34, FEATURE_EM64T) +X86_FEATURE (35, FEATURE_CLFLUSHOPT) +X86_FEATURE (36, FEATURE_SHA) +#undef X86_FEATURE_COMPAT +#undef X86_FEATURE diff --git a/include/llvm/Support/YAMLParser.h b/include/llvm/Support/YAMLParser.h index 549da3ccad51..c907a99ddb59 100644 --- a/include/llvm/Support/YAMLParser.h +++ b/include/llvm/Support/YAMLParser.h @@ -291,9 +291,11 @@ public: Node *getValue(); void skip() override { - getKey()->skip(); - if (Node *Val = getValue()) - Val->skip(); + if (Node *Key = getKey()) { + Key->skip(); + if (Node *Val = getValue()) + Val->skip(); + } } static bool classof(const Node *N) { @@ -572,13 +574,15 @@ public: document_iterator() = default; document_iterator(std::unique_ptr<Document> &D) : Doc(&D) {} - bool operator==(const document_iterator &Other) { + bool operator==(const document_iterator &Other) const { if (isAtEnd() || Other.isAtEnd()) return isAtEnd() && Other.isAtEnd(); return Doc == Other.Doc; } - bool operator!=(const document_iterator &Other) { return !(*this == Other); } + bool operator!=(const document_iterator &Other) const { + return !(*this == Other); + } document_iterator operator++() { assert(Doc && "incrementing iterator past the end."); diff --git a/include/llvm/Support/YAMLTraits.h b/include/llvm/Support/YAMLTraits.h index 71fdf47f1979..83b097a199d6 100644 --- a/include/llvm/Support/YAMLTraits.h +++ b/include/llvm/Support/YAMLTraits.h @@ -12,6 +12,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -117,6 +118,11 @@ struct ScalarBitSetTraits { // static void bitset(IO &io, T &value); }; +/// Describe which type of quotes should be used when quoting is necessary. +/// Some non-printable characters need to be double-quoted, while some others +/// are fine with simple-quoting, and some don't need any quoting. +enum class QuotingType { None, Single, Double }; + /// This class should be specialized by type that requires custom conversion /// to/from a yaml scalar. For example: /// @@ -131,7 +137,7 @@ struct ScalarBitSetTraits { /// // return empty string on success, or error string /// return StringRef(); /// } -/// static bool mustQuote(StringRef) { return true; } +/// static QuotingType mustQuote(StringRef) { return QuotingType::Single; } /// }; template<typename T> struct ScalarTraits { @@ -145,7 +151,7 @@ struct ScalarTraits { //static StringRef input(StringRef scalar, void *ctxt, T &value); // // Function to determine if the value should be quoted. - //static bool mustQuote(StringRef); + //static QuotingType mustQuote(StringRef); }; /// This class should be specialized by type that requires custom conversion @@ -270,7 +276,7 @@ struct has_ScalarTraits { using Signature_input = StringRef (*)(StringRef, void*, T&); using Signature_output = void (*)(const T&, void*, raw_ostream&); - using Signature_mustQuote = bool (*)(StringRef); + using Signature_mustQuote = QuotingType (*)(StringRef); template <typename U> static char test(SameType<Signature_input, &U::input> *, @@ -495,28 +501,66 @@ inline bool isBool(StringRef S) { S.equals("false") || S.equals("False") || S.equals("FALSE"); } -inline bool needsQuotes(StringRef S) { +// 5.1. Character Set +// The allowed character range explicitly excludes the C0 control block #x0-#x1F +// (except for TAB #x9, LF #xA, and CR #xD which are allowed), DEL #x7F, the C1 +// control block #x80-#x9F (except for NEL #x85 which is allowed), the surrogate +// block #xD800-#xDFFF, #xFFFE, and #xFFFF. +inline QuotingType needsQuotes(StringRef S) { if (S.empty()) - return true; + return QuotingType::Single; if (isspace(S.front()) || isspace(S.back())) - return true; + return QuotingType::Single; if (S.front() == ',') - return true; - - static const char ScalarSafeChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-/^., \t"; - if (S.find_first_not_of(ScalarSafeChars) != StringRef::npos) - return true; - + return QuotingType::Single; if (isNull(S)) - return true; + return QuotingType::Single; if (isBool(S)) - return true; + return QuotingType::Single; if (isNumeric(S)) - return true; + return QuotingType::Single; + + QuotingType MaxQuotingNeeded = QuotingType::None; + for (unsigned char C : S) { + // Alphanum is safe. + if (isAlnum(C)) + continue; + + switch (C) { + // Safe scalar characters. + case '_': + case '-': + case '/': + case '^': + case '.': + case ',': + case ' ': + // TAB (0x9), LF (0xA), CR (0xD) and NEL (0x85) are allowed. + case 0x9: + case 0xA: + case 0xD: + case 0x85: + continue; + // DEL (0x7F) are excluded from the allowed character range. + case 0x7F: + return QuotingType::Double; + default: { + // C0 control block (0x0 - 0x1F) is excluded from the allowed character + // range. + if (C <= 0x1F) + return QuotingType::Double; + // C1 control block (0x80 - 0x9F) is excluded from the allowed character + // range. + if (C >= 0x80 && C <= 0x9F) + return QuotingType::Double; + + // The character is not safe, at least simple quoting needed. + MaxQuotingNeeded = QuotingType::Single; + } + } + } - return false; + return MaxQuotingNeeded; } template <typename T, typename Context> @@ -581,7 +625,7 @@ public: virtual bool bitSetMatch(const char*, bool) = 0; virtual void endBitSetScalar() = 0; - virtual void scalarString(StringRef &, bool) = 0; + virtual void scalarString(StringRef &, QuotingType) = 0; virtual void blockScalarString(StringRef &) = 0; virtual void setError(const Twine &) = 0; @@ -911,91 +955,91 @@ template<> struct ScalarTraits<bool> { static void output(const bool &, void* , raw_ostream &); static StringRef input(StringRef, void *, bool &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<StringRef> { static void output(const StringRef &, void *, raw_ostream &); static StringRef input(StringRef, void *, StringRef &); - static bool mustQuote(StringRef S) { return needsQuotes(S); } + static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; template<> struct ScalarTraits<std::string> { static void output(const std::string &, void *, raw_ostream &); static StringRef input(StringRef, void *, std::string &); - static bool mustQuote(StringRef S) { return needsQuotes(S); } + static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } }; template<> struct ScalarTraits<uint8_t> { static void output(const uint8_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint8_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<uint16_t> { static void output(const uint16_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint16_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<uint32_t> { static void output(const uint32_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint32_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<uint64_t> { static void output(const uint64_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, uint64_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<int8_t> { static void output(const int8_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int8_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<int16_t> { static void output(const int16_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int16_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<int32_t> { static void output(const int32_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int32_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<int64_t> { static void output(const int64_t &, void *, raw_ostream &); static StringRef input(StringRef, void *, int64_t &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<float> { static void output(const float &, void *, raw_ostream &); static StringRef input(StringRef, void *, float &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<double> { static void output(const double &, void *, raw_ostream &); static StringRef input(StringRef, void *, double &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; // For endian types, we just use the existing ScalarTraits for the underlying @@ -1019,7 +1063,7 @@ struct ScalarTraits<support::detail::packed_endian_specific_integral< return R; } - static bool mustQuote(StringRef Str) { + static QuotingType mustQuote(StringRef Str) { return ScalarTraits<value_type>::mustQuote(Str); } }; @@ -1148,7 +1192,7 @@ private: bool beginBitSetScalar(bool &) override; bool bitSetMatch(const char *, bool ) override; void endBitSetScalar() override; - void scalarString(StringRef &, bool) override; + void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; void setError(const Twine &message) override; bool canElideEmptySequence() override; @@ -1293,7 +1337,7 @@ public: bool beginBitSetScalar(bool &) override; bool bitSetMatch(const char *, bool ) override; void endBitSetScalar() override; - void scalarString(StringRef &, bool) override; + void scalarString(StringRef &, QuotingType) override; void blockScalarString(StringRef &) override; void setError(const Twine &message) override; bool canElideEmptySequence() override; @@ -1371,28 +1415,28 @@ template<> struct ScalarTraits<Hex8> { static void output(const Hex8 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex8 &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<Hex16> { static void output(const Hex16 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex16 &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<Hex32> { static void output(const Hex32 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex32 &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template<> struct ScalarTraits<Hex64> { static void output(const Hex64 &, void *, raw_ostream &); static StringRef input(StringRef, void *, Hex64 &); - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; // Define non-member operator>> so that Input can stream in a document list. @@ -1681,7 +1725,7 @@ template <typename T> struct StdMapStringCustomMappingTraitsImpl { template <> struct ScalarTraits<Type> { \ static void output(const Type &Value, void *ctx, raw_ostream &Out); \ static StringRef input(StringRef Scalar, void *ctxt, Type &Value); \ - static bool mustQuote(StringRef) { return MustQuote; } \ + static QuotingType mustQuote(StringRef) { return MustQuote; } \ }; \ } \ } diff --git a/include/llvm/Support/raw_ostream.h b/include/llvm/Support/raw_ostream.h index e644a5bda5ef..d11f5a837796 100644 --- a/include/llvm/Support/raw_ostream.h +++ b/include/llvm/Support/raw_ostream.h @@ -213,6 +213,10 @@ public: /// Output \p N in hexadecimal, without any prefix or padding. raw_ostream &write_hex(unsigned long long N); + /// Output a formatted UUID with dash separators. + using uuid_t = uint8_t[16]; + raw_ostream &write_uuid(const uuid_t UUID); + /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't /// satisfy std::isprint into an escape sequence. raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false); @@ -358,9 +362,7 @@ class raw_fd_ostream : public raw_pwrite_stream { int FD; bool ShouldClose; - /// Error This flag is true if an error of any kind has been detected. - /// - bool Error; + std::error_code EC; uint64_t pos; @@ -379,7 +381,7 @@ class raw_fd_ostream : public raw_pwrite_stream { size_t preferred_buffer_size() const override; /// Set the flag indicating that an output error has been encountered. - void error_detected() { Error = true; } + void error_detected(std::error_code EC) { this->EC = EC; } public: /// Open the specified file for writing. If an error occurs, information @@ -388,15 +390,14 @@ public: /// \p Flags allows optional flags to control how the file will be opened. /// /// As a special case, if Filename is "-", then the stream will use - /// STDOUT_FILENO instead of opening a file. Note that it will still consider - /// itself to own the file descriptor. In particular, it will close the - /// file descriptor when it is done (this is necessary to detect - /// output errors). + /// STDOUT_FILENO instead of opening a file. This will not close the stdout + /// descriptor. raw_fd_ostream(StringRef Filename, std::error_code &EC, sys::fs::OpenFlags Flags); /// FD is the file descriptor that this writes to. If ShouldClose is true, - /// this closes the file when the stream is destroyed. + /// this closes the file when the stream is destroyed. If FD is for stdout or + /// stderr, it will not be closed. raw_fd_ostream(int fd, bool shouldClose, bool unbuffered=false); ~raw_fd_ostream() override; @@ -421,13 +422,13 @@ public: bool has_colors() const override; + std::error_code error() const { return EC; } + /// Return the value of the flag in this raw_fd_ostream indicating whether an /// output error has been encountered. /// This doesn't implicitly flush any pending output. Also, it doesn't /// guarantee to detect all errors unless the stream has been closed. - bool has_error() const { - return Error; - } + bool has_error() const { return bool(EC); } /// Set the flag read by has_error() to false. If the error flag is set at the /// time when this raw_ostream's destructor is called, report_fatal_error is @@ -438,9 +439,7 @@ public: /// Unless explicitly silenced." /// - from The Zen of Python, by Tim Peters /// - void clear_error() { - Error = false; - } + void clear_error() { EC = std::error_code(); } }; /// This returns a reference to a raw_ostream for standard output. Use it like: |
