diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm16/include/llvm/Support | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm16/include/llvm/Support')
186 files changed, 47857 insertions, 0 deletions
diff --git a/contrib/libs/llvm16/include/llvm/Support/AArch64TargetParser.h b/contrib/libs/llvm16/include/llvm/Support/AArch64TargetParser.h new file mode 100644 index 00000000000..f2001849483 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/AArch64TargetParser.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/AArch64TargetParser.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of +/// `llvm/TargetParser/AArch64TargetParser.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/AArch64TargetParser.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/AMDGPUMetadata.h b/contrib/libs/llvm16/include/llvm/Support/AMDGPUMetadata.h new file mode 100644 index 00000000000..5c76a988d03 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/AMDGPUMetadata.h @@ -0,0 +1,540 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- AMDGPUMetadata.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// AMDGPU metadata definitions and in-memory representations. +/// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_AMDGPUMETADATA_H +#define LLVM_SUPPORT_AMDGPUMETADATA_H + +#include "llvm/ADT/StringRef.h" +#include <cstdint> +#include <string> +#include <system_error> +#include <vector> + +namespace llvm { +namespace AMDGPU { + +//===----------------------------------------------------------------------===// +// HSA metadata. +//===----------------------------------------------------------------------===// +namespace HSAMD { + +/// HSA metadata major version for code object V2. +constexpr uint32_t VersionMajorV2 = 1; +/// HSA metadata minor version for code object V2. +constexpr uint32_t VersionMinorV2 = 0; + +/// HSA metadata major version for code object V3. +constexpr uint32_t VersionMajorV3 = 1; +/// HSA metadata minor version for code object V3. +constexpr uint32_t VersionMinorV3 = 0; + +/// HSA metadata major version for code object V4. +constexpr uint32_t VersionMajorV4 = 1; +/// HSA metadata minor version for code object V4. +constexpr uint32_t VersionMinorV4 = 1; + +/// HSA metadata major version for code object V5. +constexpr uint32_t VersionMajorV5 = 1; +/// HSA metadata minor version for code object V5. +constexpr uint32_t VersionMinorV5 = 2; + +/// HSA metadata beginning assembler directive. +constexpr char AssemblerDirectiveBegin[] = ".amd_amdgpu_hsa_metadata"; +/// HSA metadata ending assembler directive. +constexpr char AssemblerDirectiveEnd[] = ".end_amd_amdgpu_hsa_metadata"; + +/// Access qualifiers. +enum class AccessQualifier : uint8_t { + Default = 0, + ReadOnly = 1, + WriteOnly = 2, + ReadWrite = 3, + Unknown = 0xff +}; + +/// Address space qualifiers. +enum class AddressSpaceQualifier : uint8_t { + Private = 0, + Global = 1, + Constant = 2, + Local = 3, + Generic = 4, + Region = 5, + Unknown = 0xff +}; + +/// Value kinds. +enum class ValueKind : uint8_t { + ByValue = 0, + GlobalBuffer = 1, + DynamicSharedPointer = 2, + Sampler = 3, + Image = 4, + Pipe = 5, + Queue = 6, + HiddenGlobalOffsetX = 7, + HiddenGlobalOffsetY = 8, + HiddenGlobalOffsetZ = 9, + HiddenNone = 10, + HiddenPrintfBuffer = 11, + HiddenDefaultQueue = 12, + HiddenCompletionAction = 13, + HiddenMultiGridSyncArg = 14, + HiddenHostcallBuffer = 15, + Unknown = 0xff +}; + +/// Value types. This is deprecated and only remains for compatibility parsing +/// of old metadata. +enum class ValueType : uint8_t { + Struct = 0, + I8 = 1, + U8 = 2, + I16 = 3, + U16 = 4, + F16 = 5, + I32 = 6, + U32 = 7, + F32 = 8, + I64 = 9, + U64 = 10, + F64 = 11, + Unknown = 0xff +}; + +//===----------------------------------------------------------------------===// +// Kernel Metadata. +//===----------------------------------------------------------------------===// +namespace Kernel { + +//===----------------------------------------------------------------------===// +// Kernel Attributes Metadata. +//===----------------------------------------------------------------------===// +namespace Attrs { + +namespace Key { +/// Key for Kernel::Attr::Metadata::mReqdWorkGroupSize. +constexpr char ReqdWorkGroupSize[] = "ReqdWorkGroupSize"; +/// Key for Kernel::Attr::Metadata::mWorkGroupSizeHint. +constexpr char WorkGroupSizeHint[] = "WorkGroupSizeHint"; +/// Key for Kernel::Attr::Metadata::mVecTypeHint. +constexpr char VecTypeHint[] = "VecTypeHint"; +/// Key for Kernel::Attr::Metadata::mRuntimeHandle. +constexpr char RuntimeHandle[] = "RuntimeHandle"; +} // end namespace Key + +/// In-memory representation of kernel attributes metadata. +struct Metadata final { + /// 'reqd_work_group_size' attribute. Optional. + std::vector<uint32_t> mReqdWorkGroupSize = std::vector<uint32_t>(); + /// 'work_group_size_hint' attribute. Optional. + std::vector<uint32_t> mWorkGroupSizeHint = std::vector<uint32_t>(); + /// 'vec_type_hint' attribute. Optional. + std::string mVecTypeHint = std::string(); + /// External symbol created by runtime to store the kernel address + /// for enqueued blocks. + std::string mRuntimeHandle = std::string(); + + /// Default constructor. + Metadata() = default; + + /// \returns True if kernel attributes metadata is empty, false otherwise. + bool empty() const { + return !notEmpty(); + } + + /// \returns True if kernel attributes metadata is not empty, false otherwise. + bool notEmpty() const { + return !mReqdWorkGroupSize.empty() || !mWorkGroupSizeHint.empty() || + !mVecTypeHint.empty() || !mRuntimeHandle.empty(); + } +}; + +} // end namespace Attrs + +//===----------------------------------------------------------------------===// +// Kernel Argument Metadata. +//===----------------------------------------------------------------------===// +namespace Arg { + +namespace Key { +/// Key for Kernel::Arg::Metadata::mName. +constexpr char Name[] = "Name"; +/// Key for Kernel::Arg::Metadata::mTypeName. +constexpr char TypeName[] = "TypeName"; +/// Key for Kernel::Arg::Metadata::mSize. +constexpr char Size[] = "Size"; +/// Key for Kernel::Arg::Metadata::mOffset. +constexpr char Offset[] = "Offset"; +/// Key for Kernel::Arg::Metadata::mAlign. +constexpr char Align[] = "Align"; +/// Key for Kernel::Arg::Metadata::mValueKind. +constexpr char ValueKind[] = "ValueKind"; +/// Key for Kernel::Arg::Metadata::mValueType. (deprecated) +constexpr char ValueType[] = "ValueType"; +/// Key for Kernel::Arg::Metadata::mPointeeAlign. +constexpr char PointeeAlign[] = "PointeeAlign"; +/// Key for Kernel::Arg::Metadata::mAddrSpaceQual. +constexpr char AddrSpaceQual[] = "AddrSpaceQual"; +/// Key for Kernel::Arg::Metadata::mAccQual. +constexpr char AccQual[] = "AccQual"; +/// Key for Kernel::Arg::Metadata::mActualAccQual. +constexpr char ActualAccQual[] = "ActualAccQual"; +/// Key for Kernel::Arg::Metadata::mIsConst. +constexpr char IsConst[] = "IsConst"; +/// Key for Kernel::Arg::Metadata::mIsRestrict. +constexpr char IsRestrict[] = "IsRestrict"; +/// Key for Kernel::Arg::Metadata::mIsVolatile. +constexpr char IsVolatile[] = "IsVolatile"; +/// Key for Kernel::Arg::Metadata::mIsPipe. +constexpr char IsPipe[] = "IsPipe"; +} // end namespace Key + +/// In-memory representation of kernel argument metadata. +struct Metadata final { + /// Name. Optional. + std::string mName = std::string(); + /// Type name. Optional. + std::string mTypeName = std::string(); + /// Size in bytes. Required. + uint32_t mSize = 0; + /// Offset in bytes. Required for code object v3, unused for code object v2. + uint32_t mOffset = 0; + /// Alignment in bytes. Required. + uint32_t mAlign = 0; + /// Value kind. Required. + ValueKind mValueKind = ValueKind::Unknown; + /// Pointee alignment in bytes. Optional. + uint32_t mPointeeAlign = 0; + /// Address space qualifier. Optional. + AddressSpaceQualifier mAddrSpaceQual = AddressSpaceQualifier::Unknown; + /// Access qualifier. Optional. + AccessQualifier mAccQual = AccessQualifier::Unknown; + /// Actual access qualifier. Optional. + AccessQualifier mActualAccQual = AccessQualifier::Unknown; + /// True if 'const' qualifier is specified. Optional. + bool mIsConst = false; + /// True if 'restrict' qualifier is specified. Optional. + bool mIsRestrict = false; + /// True if 'volatile' qualifier is specified. Optional. + bool mIsVolatile = false; + /// True if 'pipe' qualifier is specified. Optional. + bool mIsPipe = false; + + /// Default constructor. + Metadata() = default; +}; + +} // end namespace Arg + +//===----------------------------------------------------------------------===// +// Kernel Code Properties Metadata. +//===----------------------------------------------------------------------===// +namespace CodeProps { + +namespace Key { +/// Key for Kernel::CodeProps::Metadata::mKernargSegmentSize. +constexpr char KernargSegmentSize[] = "KernargSegmentSize"; +/// Key for Kernel::CodeProps::Metadata::mGroupSegmentFixedSize. +constexpr char GroupSegmentFixedSize[] = "GroupSegmentFixedSize"; +/// Key for Kernel::CodeProps::Metadata::mPrivateSegmentFixedSize. +constexpr char PrivateSegmentFixedSize[] = "PrivateSegmentFixedSize"; +/// Key for Kernel::CodeProps::Metadata::mKernargSegmentAlign. +constexpr char KernargSegmentAlign[] = "KernargSegmentAlign"; +/// Key for Kernel::CodeProps::Metadata::mWavefrontSize. +constexpr char WavefrontSize[] = "WavefrontSize"; +/// Key for Kernel::CodeProps::Metadata::mNumSGPRs. +constexpr char NumSGPRs[] = "NumSGPRs"; +/// Key for Kernel::CodeProps::Metadata::mNumVGPRs. +constexpr char NumVGPRs[] = "NumVGPRs"; +/// Key for Kernel::CodeProps::Metadata::mMaxFlatWorkGroupSize. +constexpr char MaxFlatWorkGroupSize[] = "MaxFlatWorkGroupSize"; +/// Key for Kernel::CodeProps::Metadata::mIsDynamicCallStack. +constexpr char IsDynamicCallStack[] = "IsDynamicCallStack"; +/// Key for Kernel::CodeProps::Metadata::mIsXNACKEnabled. +constexpr char IsXNACKEnabled[] = "IsXNACKEnabled"; +/// Key for Kernel::CodeProps::Metadata::mNumSpilledSGPRs. +constexpr char NumSpilledSGPRs[] = "NumSpilledSGPRs"; +/// Key for Kernel::CodeProps::Metadata::mNumSpilledVGPRs. +constexpr char NumSpilledVGPRs[] = "NumSpilledVGPRs"; +} // end namespace Key + +/// In-memory representation of kernel code properties metadata. +struct Metadata final { + /// Size in bytes of the kernarg segment memory. Kernarg segment memory + /// holds the values of the arguments to the kernel. Required. + uint64_t mKernargSegmentSize = 0; + /// 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. Required. + uint32_t mGroupSegmentFixedSize = 0; + /// Size in bytes of the private segment memory required by a workitem. + /// Private segment memory includes arg, spill and private segments. Required. + uint32_t mPrivateSegmentFixedSize = 0; + /// Maximum byte alignment of variables used by the kernel in the + /// kernarg memory segment. Required. + uint32_t mKernargSegmentAlign = 0; + /// Wavefront size. Required. + uint32_t mWavefrontSize = 0; + /// Total number of SGPRs used by a wavefront. Optional. + uint16_t mNumSGPRs = 0; + /// Total number of VGPRs used by a workitem. Optional. + uint16_t mNumVGPRs = 0; + /// Maximum flat work-group size supported by the kernel. Optional. + uint32_t mMaxFlatWorkGroupSize = 0; + /// True if the generated machine code is using a dynamically sized + /// call stack. Optional. + bool mIsDynamicCallStack = false; + /// True if the generated machine code is capable of supporting XNACK. + /// Optional. + bool mIsXNACKEnabled = false; + /// Number of SGPRs spilled by a wavefront. Optional. + uint16_t mNumSpilledSGPRs = 0; + /// Number of VGPRs spilled by a workitem. Optional. + uint16_t mNumSpilledVGPRs = 0; + + /// Default constructor. + Metadata() = default; + + /// \returns True if kernel code properties metadata is empty, false + /// otherwise. + bool empty() const { + return !notEmpty(); + } + + /// \returns True if kernel code properties metadata is not empty, false + /// otherwise. + bool notEmpty() const { + return true; + } +}; + +} // end namespace CodeProps + +//===----------------------------------------------------------------------===// +// Kernel Debug Properties Metadata. +//===----------------------------------------------------------------------===// +namespace DebugProps { + +namespace Key { +/// Key for Kernel::DebugProps::Metadata::mDebuggerABIVersion. +constexpr char DebuggerABIVersion[] = "DebuggerABIVersion"; +/// Key for Kernel::DebugProps::Metadata::mReservedNumVGPRs. +constexpr char ReservedNumVGPRs[] = "ReservedNumVGPRs"; +/// Key for Kernel::DebugProps::Metadata::mReservedFirstVGPR. +constexpr char ReservedFirstVGPR[] = "ReservedFirstVGPR"; +/// Key for Kernel::DebugProps::Metadata::mPrivateSegmentBufferSGPR. +constexpr char PrivateSegmentBufferSGPR[] = "PrivateSegmentBufferSGPR"; +/// Key for +/// Kernel::DebugProps::Metadata::mWavefrontPrivateSegmentOffsetSGPR. +constexpr char WavefrontPrivateSegmentOffsetSGPR[] = + "WavefrontPrivateSegmentOffsetSGPR"; +} // end namespace Key + +/// In-memory representation of kernel debug properties metadata. +struct Metadata final { + /// Debugger ABI version. Optional. + std::vector<uint32_t> mDebuggerABIVersion = std::vector<uint32_t>(); + /// Consecutive number of VGPRs reserved for debugger use. Must be 0 if + /// mDebuggerABIVersion is not set. Optional. + uint16_t mReservedNumVGPRs = 0; + /// First fixed VGPR reserved. Must be uint16_t(-1) if + /// mDebuggerABIVersion is not set or mReservedFirstVGPR is 0. Optional. + uint16_t mReservedFirstVGPR = uint16_t(-1); + /// Fixed SGPR of the first of 4 SGPRs used to hold the scratch V# used + /// for the entire kernel execution. Must be uint16_t(-1) if + /// mDebuggerABIVersion is not set or SGPR not used or not known. Optional. + uint16_t mPrivateSegmentBufferSGPR = uint16_t(-1); + /// Fixed SGPR used to hold the wave scratch offset for the entire + /// kernel execution. Must be uint16_t(-1) if mDebuggerABIVersion is not set + /// or SGPR is not used or not known. Optional. + uint16_t mWavefrontPrivateSegmentOffsetSGPR = uint16_t(-1); + + /// Default constructor. + Metadata() = default; + + /// \returns True if kernel debug properties metadata is empty, false + /// otherwise. + bool empty() const { + return !notEmpty(); + } + + /// \returns True if kernel debug properties metadata is not empty, false + /// otherwise. + bool notEmpty() const { + return !mDebuggerABIVersion.empty(); + } +}; + +} // end namespace DebugProps + +namespace Key { +/// Key for Kernel::Metadata::mName. +constexpr char Name[] = "Name"; +/// Key for Kernel::Metadata::mSymbolName. +constexpr char SymbolName[] = "SymbolName"; +/// Key for Kernel::Metadata::mLanguage. +constexpr char Language[] = "Language"; +/// Key for Kernel::Metadata::mLanguageVersion. +constexpr char LanguageVersion[] = "LanguageVersion"; +/// Key for Kernel::Metadata::mAttrs. +constexpr char Attrs[] = "Attrs"; +/// Key for Kernel::Metadata::mArgs. +constexpr char Args[] = "Args"; +/// Key for Kernel::Metadata::mCodeProps. +constexpr char CodeProps[] = "CodeProps"; +/// Key for Kernel::Metadata::mDebugProps. +constexpr char DebugProps[] = "DebugProps"; +} // end namespace Key + +/// In-memory representation of kernel metadata. +struct Metadata final { + /// Kernel source name. Required. + std::string mName = std::string(); + /// Kernel descriptor name. Required. + std::string mSymbolName = std::string(); + /// Language. Optional. + std::string mLanguage = std::string(); + /// Language version. Optional. + std::vector<uint32_t> mLanguageVersion = std::vector<uint32_t>(); + /// Attributes metadata. Optional. + Attrs::Metadata mAttrs = Attrs::Metadata(); + /// Arguments metadata. Optional. + std::vector<Arg::Metadata> mArgs = std::vector<Arg::Metadata>(); + /// Code properties metadata. Optional. + CodeProps::Metadata mCodeProps = CodeProps::Metadata(); + /// Debug properties metadata. Optional. + DebugProps::Metadata mDebugProps = DebugProps::Metadata(); + + /// Default constructor. + Metadata() = default; +}; + +} // end namespace Kernel + +namespace Key { +/// Key for HSA::Metadata::mVersion. +constexpr char Version[] = "Version"; +/// Key for HSA::Metadata::mPrintf. +constexpr char Printf[] = "Printf"; +/// Key for HSA::Metadata::mKernels. +constexpr char Kernels[] = "Kernels"; +} // end namespace Key + +/// In-memory representation of HSA metadata. +struct Metadata final { + /// HSA metadata version. Required. + std::vector<uint32_t> mVersion = std::vector<uint32_t>(); + /// Printf metadata. Optional. + std::vector<std::string> mPrintf = std::vector<std::string>(); + /// Kernels metadata. Required. + std::vector<Kernel::Metadata> mKernels = std::vector<Kernel::Metadata>(); + + /// Default constructor. + Metadata() = default; +}; + +/// Converts \p String to \p HSAMetadata. +std::error_code fromString(StringRef String, Metadata &HSAMetadata); + +/// Converts \p HSAMetadata to \p String. +std::error_code toString(Metadata HSAMetadata, std::string &String); + +//===----------------------------------------------------------------------===// +// HSA metadata for v3 code object. +//===----------------------------------------------------------------------===// +namespace V3 { +/// HSA metadata major version. +constexpr uint32_t VersionMajor = 1; +/// HSA metadata minor version. +constexpr uint32_t VersionMinor = 0; + +/// HSA metadata beginning assembler directive. +constexpr char AssemblerDirectiveBegin[] = ".amdgpu_metadata"; +/// HSA metadata ending assembler directive. +constexpr char AssemblerDirectiveEnd[] = ".end_amdgpu_metadata"; +} // end namespace V3 + +} // end namespace HSAMD + +//===----------------------------------------------------------------------===// +// PAL metadata. +//===----------------------------------------------------------------------===// +namespace PALMD { + +/// PAL metadata (old linear format) assembler directive. +constexpr char AssemblerDirective[] = ".amd_amdgpu_pal_metadata"; + +/// PAL metadata (new MsgPack format) beginning assembler directive. +constexpr char AssemblerDirectiveBegin[] = ".amdgpu_pal_metadata"; + +/// PAL metadata (new MsgPack format) ending assembler directive. +constexpr char AssemblerDirectiveEnd[] = ".end_amdgpu_pal_metadata"; + +/// PAL metadata keys. +enum Key : uint32_t { + R_2E12_COMPUTE_PGM_RSRC1 = 0x2e12, + R_2D4A_SPI_SHADER_PGM_RSRC1_LS = 0x2d4a, + R_2D0A_SPI_SHADER_PGM_RSRC1_HS = 0x2d0a, + R_2CCA_SPI_SHADER_PGM_RSRC1_ES = 0x2cca, + R_2C8A_SPI_SHADER_PGM_RSRC1_GS = 0x2c8a, + R_2C4A_SPI_SHADER_PGM_RSRC1_VS = 0x2c4a, + R_2C0A_SPI_SHADER_PGM_RSRC1_PS = 0x2c0a, + R_2E00_COMPUTE_DISPATCH_INITIATOR = 0x2e00, + R_A1B3_SPI_PS_INPUT_ENA = 0xa1b3, + R_A1B4_SPI_PS_INPUT_ADDR = 0xa1b4, + R_A1B6_SPI_PS_IN_CONTROL = 0xa1b6, + R_A2D5_VGT_SHADER_STAGES_EN = 0xa2d5, + + 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 PALMD +} // end namespace AMDGPU +} // end namespace llvm + +#endif // LLVM_SUPPORT_AMDGPUMETADATA_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ARMAttributeParser.h b/contrib/libs/llvm16/include/llvm/Support/ARMAttributeParser.h new file mode 100644 index 00000000000..9c5c8884673 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ARMAttributeParser.h @@ -0,0 +1,94 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- ARMAttributeParser.h - ARM Attribute Information Printer -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ARMATTRIBUTEPARSER_H +#define LLVM_SUPPORT_ARMATTRIBUTEPARSER_H + +#include "ARMBuildAttributes.h" +#include "ELFAttributeParser.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +class ScopedPrinter; + +class ARMAttributeParser : public ELFAttributeParser { + struct DisplayHandler { + ARMBuildAttrs::AttrType attribute; + Error (ARMAttributeParser::*routine)(ARMBuildAttrs::AttrType); + }; + static const DisplayHandler displayRoutines[]; + + Error handler(uint64_t tag, bool &handled) override; + + Error stringAttribute(ARMBuildAttrs::AttrType tag); + + Error CPU_arch(ARMBuildAttrs::AttrType tag); + Error CPU_arch_profile(ARMBuildAttrs::AttrType tag); + Error ARM_ISA_use(ARMBuildAttrs::AttrType tag); + Error THUMB_ISA_use(ARMBuildAttrs::AttrType tag); + Error FP_arch(ARMBuildAttrs::AttrType tag); + Error WMMX_arch(ARMBuildAttrs::AttrType tag); + Error Advanced_SIMD_arch(ARMBuildAttrs::AttrType tag); + Error MVE_arch(ARMBuildAttrs::AttrType tag); + Error PCS_config(ARMBuildAttrs::AttrType tag); + Error ABI_PCS_R9_use(ARMBuildAttrs::AttrType tag); + Error ABI_PCS_RW_data(ARMBuildAttrs::AttrType tag); + Error ABI_PCS_RO_data(ARMBuildAttrs::AttrType tag); + Error ABI_PCS_GOT_use(ARMBuildAttrs::AttrType tag); + Error ABI_PCS_wchar_t(ARMBuildAttrs::AttrType tag); + Error ABI_FP_rounding(ARMBuildAttrs::AttrType tag); + Error ABI_FP_denormal(ARMBuildAttrs::AttrType tag); + Error ABI_FP_exceptions(ARMBuildAttrs::AttrType tag); + Error ABI_FP_user_exceptions(ARMBuildAttrs::AttrType tag); + Error ABI_FP_number_model(ARMBuildAttrs::AttrType tag); + Error ABI_align_needed(ARMBuildAttrs::AttrType tag); + Error ABI_align_preserved(ARMBuildAttrs::AttrType tag); + Error ABI_enum_size(ARMBuildAttrs::AttrType tag); + Error ABI_HardFP_use(ARMBuildAttrs::AttrType tag); + Error ABI_VFP_args(ARMBuildAttrs::AttrType tag); + Error ABI_WMMX_args(ARMBuildAttrs::AttrType tag); + Error ABI_optimization_goals(ARMBuildAttrs::AttrType tag); + Error ABI_FP_optimization_goals(ARMBuildAttrs::AttrType tag); + Error compatibility(ARMBuildAttrs::AttrType tag); + Error CPU_unaligned_access(ARMBuildAttrs::AttrType tag); + Error FP_HP_extension(ARMBuildAttrs::AttrType tag); + Error ABI_FP_16bit_format(ARMBuildAttrs::AttrType tag); + Error MPextension_use(ARMBuildAttrs::AttrType tag); + Error DIV_use(ARMBuildAttrs::AttrType tag); + Error DSP_extension(ARMBuildAttrs::AttrType tag); + Error T2EE_use(ARMBuildAttrs::AttrType tag); + Error Virtualization_use(ARMBuildAttrs::AttrType tag); + Error PAC_extension(ARMBuildAttrs::AttrType tag); + Error BTI_extension(ARMBuildAttrs::AttrType tag); + Error PACRET_use(ARMBuildAttrs::AttrType tag); + Error BTI_use(ARMBuildAttrs::AttrType tag); + Error nodefaults(ARMBuildAttrs::AttrType tag); + Error also_compatible_with(ARMBuildAttrs::AttrType tag); + +public: + ARMAttributeParser(ScopedPrinter *sw) + : ELFAttributeParser(sw, ARMBuildAttrs::getARMAttributeTags(), "aeabi") {} + ARMAttributeParser() + : ELFAttributeParser(ARMBuildAttrs::getARMAttributeTags(), "aeabi") {} +}; +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ARMBuildAttributes.h b/contrib/libs/llvm16/include/llvm/Support/ARMBuildAttributes.h new file mode 100644 index 00000000000..b0b793bf2c7 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ARMBuildAttributes.h @@ -0,0 +1,280 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- ARMBuildAttributes.h - ARM Build Attributes -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains enumerations and support routines for ARM build attributes +// as defined in ARM ABI addenda document (ABI release 2.08). +// +// ELF for the ARM Architecture r2.09 - November 30, 2012 +// +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/IHI0044E_aaelf.pdf +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ARMBUILDATTRIBUTES_H +#define LLVM_SUPPORT_ARMBUILDATTRIBUTES_H + +#include "llvm/Support/ELFAttributes.h" + +namespace llvm { +namespace ARMBuildAttrs { + +const TagNameMap &getARMAttributeTags(); + +enum SpecialAttr { + // This is for the .cpu asm attr. It translates into one or more + // AttrType (below) entries in the .ARM.attributes section in the ELF. + SEL_CPU +}; + +enum AttrType : unsigned { + // Rest correspond to ELF/.ARM.attributes + File = 1, + CPU_raw_name = 4, + CPU_name = 5, + CPU_arch = 6, + CPU_arch_profile = 7, + ARM_ISA_use = 8, + THUMB_ISA_use = 9, + FP_arch = 10, + WMMX_arch = 11, + Advanced_SIMD_arch = 12, + PCS_config = 13, + ABI_PCS_R9_use = 14, + ABI_PCS_RW_data = 15, + ABI_PCS_RO_data = 16, + ABI_PCS_GOT_use = 17, + ABI_PCS_wchar_t = 18, + ABI_FP_rounding = 19, + ABI_FP_denormal = 20, + ABI_FP_exceptions = 21, + ABI_FP_user_exceptions = 22, + ABI_FP_number_model = 23, + ABI_align_needed = 24, + ABI_align_preserved = 25, + ABI_enum_size = 26, + ABI_HardFP_use = 27, + ABI_VFP_args = 28, + ABI_WMMX_args = 29, + ABI_optimization_goals = 30, + ABI_FP_optimization_goals = 31, + compatibility = 32, + CPU_unaligned_access = 34, + FP_HP_extension = 36, + ABI_FP_16bit_format = 38, + MPextension_use = 42, // recoded from 70 (ABI r2.08) + DIV_use = 44, + DSP_extension = 46, + MVE_arch = 48, + PAC_extension = 50, + BTI_extension = 52, + also_compatible_with = 65, + conformance = 67, + Virtualization_use = 68, + BTI_use = 74, + PACRET_use = 76, + + /// Legacy Tags + Section = 2, // deprecated (ABI r2.09) + Symbol = 3, // deprecated (ABI r2.09) + ABI_align8_needed = 24, // renamed to ABI_align_needed (ABI r2.09) + ABI_align8_preserved = 25, // renamed to ABI_align_preserved (ABI r2.09) + nodefaults = 64, // deprecated (ABI r2.09) + T2EE_use = 66, // deprecated (ABI r2.09) + MPextension_use_old = 70 // recoded to MPextension_use (ABI r2.08) +}; + +// Legal Values for CPU_arch, (=6), uleb128 +enum CPUArch { + Pre_v4 = 0, + v4 = 1, // e.g. SA110 + v4T = 2, // e.g. ARM7TDMI + v5T = 3, // e.g. ARM9TDMI + v5TE = 4, // e.g. ARM946E_S + v5TEJ = 5, // e.g. ARM926EJ_S + v6 = 6, // e.g. ARM1136J_S + v6KZ = 7, // e.g. ARM1176JZ_S + v6T2 = 8, // e.g. ARM1156T2_S + v6K = 9, // e.g. ARM1176JZ_S + v7 = 10, // e.g. Cortex A8, Cortex M3 + v6_M = 11, // e.g. Cortex M1 + v6S_M = 12, // v6_M with the System extensions + v7E_M = 13, // v7_M with DSP extensions + v8_A = 14, // v8_A AArch32 + v8_R = 15, // e.g. Cortex R52 + v8_M_Base = 16, // v8_M_Base AArch32 + v8_M_Main = 17, // v8_M_Main AArch32 + v8_1_M_Main = 21, // v8_1_M_Main AArch32 + v9_A = 22, // v9_A AArch32 +}; + +enum CPUArchProfile { // (=7), uleb128 + Not_Applicable = 0, // pre v7, or cross-profile code + ApplicationProfile = (0x41), // 'A' (e.g. for Cortex A8) + RealTimeProfile = (0x52), // 'R' (e.g. for Cortex R4) + MicroControllerProfile = (0x4D), // 'M' (e.g. for Cortex M3) + SystemProfile = (0x53) // 'S' Application or real-time profile +}; + +// The following have a lot of common use cases +enum { + Not_Allowed = 0, + Allowed = 1, + + // Tag_ARM_ISA_use (=8), uleb128 + + // Tag_THUMB_ISA_use, (=9), uleb128 + AllowThumb32 = 2, // 32-bit Thumb (implies 16-bit instructions) + AllowThumbDerived = 3, // Thumb allowed, derived from arch/profile + + // Tag_FP_arch (=10), uleb128 (formerly Tag_VFP_arch = 10) + AllowFPv2 = 2, // v2 FP ISA permitted (implies use of the v1 FP ISA) + AllowFPv3A = 3, // v3 FP ISA permitted (implies use of the v2 FP ISA) + AllowFPv3B = 4, // v3 FP ISA permitted, but only D0-D15, S0-S31 + AllowFPv4A = 5, // v4 FP ISA permitted (implies use of v3 FP ISA) + AllowFPv4B = 6, // v4 FP ISA was permitted, but only D0-D15, S0-S31 + AllowFPARMv8A = 7, // Use of the ARM v8-A FP ISA was permitted + AllowFPARMv8B = 8, // Use of the ARM v8-A FP ISA was permitted, but only + // D0-D15, S0-S31 + + // Tag_WMMX_arch, (=11), uleb128 + AllowWMMXv1 = 1, // The user permitted this entity to use WMMX v1 + AllowWMMXv2 = 2, // The user permitted this entity to use WMMX v2 + + // Tag_Advanced_SIMD_arch, (=12), uleb128 + AllowNeon = 1, // SIMDv1 was permitted + AllowNeon2 = 2, // SIMDv2 was permitted (Half-precision FP, MAC operations) + AllowNeonARMv8 = 3, // ARM v8-A SIMD was permitted + AllowNeonARMv8_1a = 4,// ARM v8.1-A SIMD was permitted (RDMA) + + // Tag_MVE_arch, (=48), uleb128 + AllowMVEInteger = 1, // integer-only MVE was permitted + AllowMVEIntegerAndFloat = 2, // both integer and floating point MVE were permitted + + // Tag_ABI_PCS_R9_use, (=14), uleb128 + R9IsGPR = 0, // R9 used as v6 (just another callee-saved register) + R9IsSB = 1, // R9 used as a global static base rgister + R9IsTLSPointer = 2, // R9 used as a thread local storage pointer + R9Reserved = 3, // R9 not used by code associated with attributed entity + + // Tag_ABI_PCS_RW_data, (=15), uleb128 + AddressRWPCRel = 1, // Address RW static data PC-relative + AddressRWSBRel = 2, // Address RW static data SB-relative + AddressRWNone = 3, // No RW static data permitted + + // Tag_ABI_PCS_RO_data, (=14), uleb128 + AddressROPCRel = 1, // Address RO static data PC-relative + AddressRONone = 2, // No RO static data permitted + + // Tag_ABI_PCS_GOT_use, (=17), uleb128 + AddressDirect = 1, // Address imported data directly + AddressGOT = 2, // Address imported data indirectly (via GOT) + + // Tag_ABI_PCS_wchar_t, (=18), uleb128 + WCharProhibited = 0, // wchar_t is not used + WCharWidth2Bytes = 2, // sizeof(wchar_t) == 2 + WCharWidth4Bytes = 4, // sizeof(wchar_t) == 4 + + // Tag_ABI_align_needed, (=24), uleb128 + Align8Byte = 1, + Align4Byte = 2, + AlignReserved = 3, + + // Tag_ABI_align_needed, (=25), uleb128 + AlignNotPreserved = 0, + AlignPreserve8Byte = 1, + AlignPreserveAll = 2, + + // Tag_ABI_FP_denormal, (=20), uleb128 + PositiveZero = 0, + IEEEDenormals = 1, + PreserveFPSign = 2, // sign when flushed-to-zero is preserved + + // Tag_ABI_FP_number_model, (=23), uleb128 + AllowIEEENormal = 1, + AllowRTABI = 2, // numbers, infinities, and one quiet NaN (see [RTABI]) + AllowIEEE754 = 3, // this code to use all the IEEE 754-defined FP encodings + + // Tag_ABI_enum_size, (=26), uleb128 + EnumProhibited = 0, // The user prohibited the use of enums when building + // this entity. + EnumSmallest = 1, // Enum is smallest container big enough to hold all + // values. + Enum32Bit = 2, // Enum is at least 32 bits. + Enum32BitABI = 3, // Every enumeration visible across an ABI-complying + // interface contains a value needing 32 bits to encode + // it; other enums can be containerized. + + // Tag_ABI_HardFP_use, (=27), uleb128 + HardFPImplied = 0, // FP use should be implied by Tag_FP_arch + HardFPSinglePrecision = 1, // Single-precision only + + // Tag_ABI_VFP_args, (=28), uleb128 + BaseAAPCS = 0, + HardFPAAPCS = 1, + ToolChainFPPCS = 2, + CompatibleFPAAPCS = 3, + + // Tag_FP_HP_extension, (=36), uleb128 + AllowHPFP = 1, // Allow use of Half Precision FP + + // Tag_FP_16bit_format, (=38), uleb128 + FP16FormatIEEE = 1, + FP16VFP3 = 2, + + // Tag_MPextension_use, (=42), uleb128 + AllowMP = 1, // Allow use of MP extensions + + // Tag_DIV_use, (=44), uleb128 + // Note: AllowDIVExt must be emitted if and only if the permission to use + // hardware divide cannot be conveyed using AllowDIVIfExists or DisallowDIV + AllowDIVIfExists = 0, // Allow hardware divide if available in arch, or no + // info exists. + DisallowDIV = 1, // Hardware divide explicitly disallowed. + AllowDIVExt = 2, // Allow hardware divide as optional architecture + // extension above the base arch specified by + // Tag_CPU_arch and Tag_CPU_arch_profile. + + // Tag_Virtualization_use, (=68), uleb128 + AllowTZ = 1, + AllowVirtualization = 2, + AllowTZVirtualization = 3, + + // Tag_PAC_extension, (=50), uleb128 + DisallowPAC = 0, + AllowPACInNOPSpace = 1, + AllowPAC = 2, + + // Tag_BTI_extension, (=52), uleb128 + DisallowBTI = 0, + AllowBTIInNOPSpace = 1, + AllowBTI = 2, + + // Tag_BTI_use, (=74), uleb128 + BTINotUsed = 0, + BTIUsed = 1, + + // Tag_PACRET_use, (=76), uleb128 + PACRETNotUsed = 0, + PACRETUsed = 1 +}; + +} // namespace ARMBuildAttrs +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ARMTargetParser.h b/contrib/libs/llvm16/include/llvm/Support/ARMTargetParser.h new file mode 100644 index 00000000000..22a4ca85cd6 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ARMTargetParser.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ARMTargetParser.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of +/// `llvm/TargetParser/ARMTargetParser.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/ARMTargetParser.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ARMTargetParserCommon.h b/contrib/libs/llvm16/include/llvm/Support/ARMTargetParserCommon.h new file mode 100644 index 00000000000..62502ee8b15 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ARMTargetParserCommon.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ARMTargetParserCommon.def ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of +/// `llvm/TargetParser/ARMTargetParserCommon.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/ARMTargetParserCommon.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ARMWinEH.h b/contrib/libs/llvm16/include/llvm/Support/ARMWinEH.h new file mode 100644 index 00000000000..c21d6870b00 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ARMWinEH.h @@ -0,0 +1,532 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ARMWinEH.h - Windows on ARM EH Constants ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ARMWINEH_H +#define LLVM_SUPPORT_ARMWINEH_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace ARM { +namespace WinEH { +enum class RuntimeFunctionFlag { + RFF_Unpacked, /// unpacked entry + RFF_Packed, /// packed entry + RFF_PackedFragment, /// packed entry representing a fragment + RFF_Reserved, /// reserved +}; + +enum class ReturnType { + RT_POP, /// return via pop {pc} (L flag must be set) + RT_B, /// 16-bit branch + RT_BW, /// 32-bit branch + RT_NoEpilogue, /// no epilogue (fragment) +}; + +/// RuntimeFunction - An entry in the table of procedure data (.pdata) +/// +/// This is ARM specific, but the Function Start RVA, Flag and +/// ExceptionInformationRVA fields work identically for ARM64. +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +---------------------------------------------------------------+ +/// | Function Start RVA | +/// +-------------------+-+-+-+-----+-+---+---------------------+---+ +/// | Stack Adjust |C|L|R| Reg |H|Ret| Function Length |Flg| +/// +-------------------+-+-+-+-----+-+---+---------------------+---+ +/// +/// Flag : 2-bit field with the following meanings: +/// - 00 = packed unwind data not used; reamining bits point to .xdata record +/// - 01 = packed unwind data +/// - 10 = packed unwind data, function assumed to have no prologue; useful +/// for function fragments that are discontiguous with the start of the +/// function +/// - 11 = reserved +/// Function Length : 11-bit field providing the length of the entire function +/// in bytes, divided by 2; if the function is greater than +/// 4KB, a full .xdata record must be used instead +/// Ret : 2-bit field indicating how the function returns +/// - 00 = return via pop {pc} (the L bit must be set) +/// - 01 = return via 16-bit branch +/// - 10 = return via 32-bit branch +/// - 11 = no epilogue; useful for function fragments that may only contain a +/// prologue but the epilogue is elsewhere +/// H : 1-bit flag indicating whether the function "homes" the integer parameter +/// registers (r0-r3), allocating 16-bytes on the stack +/// Reg : 3-bit field indicating the index of the last saved non-volatile +/// register. If the R bit is set to 0, then only integer registers are +/// saved (r4-rN, where N is 4 + Reg). If the R bit is set to 1, then +/// only floating-point registers are being saved (d8-dN, where N is +/// 8 + Reg). The special case of the R bit being set to 1 and Reg equal +/// to 7 indicates that no registers are saved. +/// R : 1-bit flag indicating whether the non-volatile registers are integer or +/// floating-point. 0 indicates integer, 1 indicates floating-point. The +/// special case of the R-flag being set and Reg being set to 7 indicates +/// that no non-volatile registers are saved. +/// L : 1-bit flag indicating whether the function saves/restores the link +/// register (LR) +/// C : 1-bit flag indicating whether the function includes extra instructions +/// to setup a frame chain for fast walking. If this flag is set, r11 is +/// implicitly added to the list of saved non-volatile integer registers. +/// Stack Adjust : 10-bit field indicating the number of bytes of stack that are +/// allocated for this function. Only values between 0x000 and +/// 0x3f3 can be directly encoded. If the value is 0x3f4 or +/// greater, then the low 4 bits have special meaning as follows: +/// - Bit 0-1 +/// indicate the number of words' of adjustment (1-4), minus 1 +/// - Bit 2 +/// indicates if the prologue combined adjustment into push +/// - Bit 3 +/// indicates if the epilogue combined adjustment into pop +/// +/// RESTRICTIONS: +/// - IF C is SET: +/// + L flag must be set since frame chaining requires r11 and lr +/// + r11 must NOT be included in the set of registers described by Reg +/// - IF Ret is 0: +/// + L flag must be set + +// NOTE: RuntimeFunction is meant to be a simple class that provides raw access +// to all fields in the structure. The accessor methods reflect the names of +// the bitfields that they correspond to. Although some obvious simplifications +// are possible via merging of methods, it would prevent the use of this class +// to fully inspect the contents of the data structure which is particularly +// useful for scenarios such as llvm-readobj to aid in testing. + +class RuntimeFunction { +public: + const support::ulittle32_t BeginAddress; + const support::ulittle32_t UnwindData; + + RuntimeFunction(const support::ulittle32_t *Data) + : BeginAddress(Data[0]), UnwindData(Data[1]) {} + + RuntimeFunction(const support::ulittle32_t BeginAddress, + const support::ulittle32_t UnwindData) + : BeginAddress(BeginAddress), UnwindData(UnwindData) {} + + RuntimeFunctionFlag Flag() const { + return RuntimeFunctionFlag(UnwindData & 0x3); + } + + uint32_t ExceptionInformationRVA() const { + assert(Flag() == RuntimeFunctionFlag::RFF_Unpacked && + "unpacked form required for this operation"); + return (UnwindData & ~0x3); + } + + uint32_t PackedUnwindData() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return (UnwindData & ~0x3); + } + uint32_t FunctionLength() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return (((UnwindData & 0x00001ffc) >> 2) << 1); + } + ReturnType Ret() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + assert(((UnwindData & 0x00006000) || L()) && "L must be set to 1"); + return ReturnType((UnwindData & 0x00006000) >> 13); + } + bool H() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x00008000) >> 15); + } + uint8_t Reg() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x00070000) >> 16); + } + bool R() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x00080000) >> 19); + } + bool L() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x00100000) >> 20); + } + bool C() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + assert(((~UnwindData & 0x00200000) || L()) && + "L flag must be set, chaining requires r11 and LR"); + assert(((~UnwindData & 0x00200000) || (Reg() < 7) || R()) && + "r11 must not be included in Reg; C implies r11"); + return ((UnwindData & 0x00200000) >> 21); + } + uint16_t StackAdjust() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0xffc00000) >> 22); + } +}; + +/// PrologueFolding - pseudo-flag derived from Stack Adjust indicating that the +/// prologue has stack adjustment combined into the push +inline bool PrologueFolding(const RuntimeFunction &RF) { + return RF.StackAdjust() >= 0x3f4 && (RF.StackAdjust() & 0x4); +} +/// Epilogue - pseudo-flag derived from Stack Adjust indicating that the +/// epilogue has stack adjustment combined into the pop +inline bool EpilogueFolding(const RuntimeFunction &RF) { + return RF.StackAdjust() >= 0x3f4 && (RF.StackAdjust() & 0x8); +} +/// StackAdjustment - calculated stack adjustment in words. The stack +/// adjustment should be determined via this function to account for the special +/// handling the special encoding when the value is >= 0x3f4. +inline uint16_t StackAdjustment(const RuntimeFunction &RF) { + uint16_t Adjustment = RF.StackAdjust(); + if (Adjustment >= 0x3f4) + return (Adjustment & 0x3) + 1; + return Adjustment; +} + +/// SavedRegisterMask - Utility function to calculate the set of saved general +/// purpose (r0-r15) and VFP (d0-d31) registers. +std::pair<uint16_t, uint32_t> SavedRegisterMask(const RuntimeFunction &RF, + bool Prologue = true); + +/// RuntimeFunctionARM64 - An entry in the table of procedure data (.pdata) +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +---------------------------------------------------------------+ +/// | Function Start RVA | +/// +-----------------+---+-+-------+-----+---------------------+---+ +/// | Frame Size |CR |H| RegI |RegF | Function Length |Flg| +/// +-----------------+---+-+-------+-----+---------------------+---+ +/// +/// See https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling +/// for the full reference for this struct. + +class RuntimeFunctionARM64 { +public: + const support::ulittle32_t BeginAddress; + const support::ulittle32_t UnwindData; + + RuntimeFunctionARM64(const support::ulittle32_t *Data) + : BeginAddress(Data[0]), UnwindData(Data[1]) {} + + RuntimeFunctionARM64(const support::ulittle32_t BeginAddress, + const support::ulittle32_t UnwindData) + : BeginAddress(BeginAddress), UnwindData(UnwindData) {} + + RuntimeFunctionFlag Flag() const { + return RuntimeFunctionFlag(UnwindData & 0x3); + } + + uint32_t ExceptionInformationRVA() const { + assert(Flag() == RuntimeFunctionFlag::RFF_Unpacked && + "unpacked form required for this operation"); + return (UnwindData & ~0x3); + } + + uint32_t PackedUnwindData() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return (UnwindData & ~0x3); + } + uint32_t FunctionLength() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return (((UnwindData & 0x00001ffc) >> 2) << 2); + } + uint8_t RegF() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x0000e000) >> 13); + } + uint8_t RegI() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x000f0000) >> 16); + } + bool H() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x00100000) >> 20); + } + uint8_t CR() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0x600000) >> 21); + } + uint16_t FrameSize() const { + assert((Flag() == RuntimeFunctionFlag::RFF_Packed || + Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "packed form required for this operation"); + return ((UnwindData & 0xff800000) >> 23); + } +}; + +/// ExceptionDataRecord - An entry in the table of exception data (.xdata) +/// +/// The format on ARM is: +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +-------+---------+-+-+-+---+-----------------------------------+ +/// | C Wrd | Epi Cnt |F|E|X|Ver| Function Length | +/// +-------+--------+'-'-'-'---'---+-------------------------------+ +/// | Reserved |Ex. Code Words| (Extended Epilogue Count) | +/// +-------+--------+--------------+-------------------------------+ +/// +/// The format on ARM64 is: +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +---------+---------+-+-+---+-----------------------------------+ +/// | C Wrd | Epi Cnt |E|X|Ver| Function Length | +/// +---------+------+--'-'-'---'---+-------------------------------+ +/// | Reserved |Ex. Code Words| (Extended Epilogue Count) | +/// +-------+--------+--------------+-------------------------------+ +/// +/// Function Length : 18-bit field indicating the total length of the function +/// in bytes divided by 2. If a function is larger than +/// 512KB, then multiple pdata and xdata records must be used. +/// Vers : 2-bit field describing the version of the remaining structure. Only +/// version 0 is currently defined (values 1-3 are not permitted). +/// X : 1-bit field indicating the presence of exception data +/// E : 1-bit field indicating that the single epilogue is packed into the +/// header +/// F : 1-bit field indicating that the record describes a function fragment +/// (implies that no prologue is present, and prologue processing should be +/// skipped) (ARM only) +/// Epilogue Count : 5-bit field that differs in meaning based on the E field. +/// +/// If E is set, then this field specifies the index of the +/// first unwind code describing the (only) epilogue. +/// +/// Otherwise, this field indicates the number of exception +/// scopes. If more than 31 scopes exist, then this field and +/// the Code Words field must both be set to 0 to indicate that +/// an extension word is required. +/// Code Words : 4-bit (5-bit on ARM64) field that specifies the number of +/// 32-bit words needed to contain all the unwind codes. If more +/// than 15 words (31 words on ARM64) are required, then this field +/// and the Epilogue Count field must both be set to 0 to indicate +/// that an extension word is required. +/// Extended Epilogue Count, Extended Code Words : +/// Valid only if Epilog Count and Code Words are both +/// set to 0. Provides an 8-bit extended code word +/// count and 16-bits for epilogue count +/// +/// The epilogue scope format on ARM is: +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +----------------+------+---+---+-------------------------------+ +/// | Ep Start Idx | Cond |Res| Epilogue Start Offset | +/// +----------------+------+---+-----------------------------------+ +/// +/// The epilogue scope format on ARM64 is: +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +-------------------+-------+---+-------------------------------+ +/// | Ep Start Idx | Res | Epilogue Start Offset | +/// +-------------------+-------+-----------------------------------+ +/// +/// If the E bit is unset in the header, the header is followed by a series of +/// epilogue scopes, which are sorted by their offset. +/// +/// Epilogue Start Offset: 18-bit field encoding the offset of epilogue relative +/// to the start of the function in bytes divided by two +/// Res : 2-bit field reserved for future expansion (must be set to 0) +/// Condition : (ARM only) 4-bit field providing the condition under which the +/// epilogue is executed. Unconditional epilogues should set this +/// field to 0xe. Epilogues must be entirely conditional or +/// unconditional, and in Thumb-2 mode. The epilogue begins with +/// the first instruction after the IT opcode. +/// Epilogue Start Index : 8-bit field indicating the byte index of the first +/// unwind code describing the epilogue +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +---------------+---------------+---------------+---------------+ +/// | Unwind Code 3 | Unwind Code 2 | Unwind Code 1 | Unwind Code 0 | +/// +---------------+---------------+---------------+---------------+ +/// +/// Following the epilogue scopes, the byte code describing the unwinding +/// follows. This is padded to align up to word alignment. Bytes are stored in +/// little endian. +/// +/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 +/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +/// +---------------------------------------------------------------+ +/// | Exception Handler RVA (requires X = 1) | +/// +---------------------------------------------------------------+ +/// | (possibly followed by data required for exception handler) | +/// +---------------------------------------------------------------+ +/// +/// If the X bit is set in the header, the unwind byte code is followed by the +/// exception handler information. This constants of one Exception Handler RVA +/// which is the address to the exception handler, followed immediately by the +/// variable length data associated with the exception handler. +/// + +struct EpilogueScope { + const support::ulittle32_t ES; + + EpilogueScope(const support::ulittle32_t Data) : ES(Data) {} + // Same for both ARM and AArch64. + uint32_t EpilogueStartOffset() const { + return (ES & 0x0003ffff); + } + + // Different implementations for ARM and AArch64. + uint8_t ResARM() const { + return ((ES & 0x000c0000) >> 18); + } + + uint8_t ResAArch64() const { + return ((ES & 0x000f0000) >> 18); + } + + // Condition is only applicable to ARM. + uint8_t Condition() const { + return ((ES & 0x00f00000) >> 20); + } + + // Different implementations for ARM and AArch64. + uint8_t EpilogueStartIndexARM() const { + return ((ES & 0xff000000) >> 24); + } + + uint16_t EpilogueStartIndexAArch64() const { + return ((ES & 0xffc00000) >> 22); + } +}; + +struct ExceptionDataRecord; +inline size_t HeaderWords(const ExceptionDataRecord &XR); + +struct ExceptionDataRecord { + const support::ulittle32_t *Data; + bool isAArch64; + + ExceptionDataRecord(const support::ulittle32_t *Data, bool isAArch64) : + Data(Data), isAArch64(isAArch64) {} + + uint32_t FunctionLength() const { + return (Data[0] & 0x0003ffff); + } + + uint32_t FunctionLengthInBytesARM() const { + return FunctionLength() << 1; + } + + uint32_t FunctionLengthInBytesAArch64() const { + return FunctionLength() << 2; + } + + uint8_t Vers() const { + return (Data[0] & 0x000C0000) >> 18; + } + + bool X() const { + return ((Data[0] & 0x00100000) >> 20); + } + + bool E() const { + return ((Data[0] & 0x00200000) >> 21); + } + + bool F() const { + assert(!isAArch64 && "Fragments are only supported on ARMv7 WinEH"); + return ((Data[0] & 0x00400000) >> 22); + } + + uint16_t EpilogueCount() const { + if (HeaderWords(*this) == 1) { + if (isAArch64) + return (Data[0] & 0x07C00000) >> 22; + return (Data[0] & 0x0f800000) >> 23; + } + return Data[1] & 0x0000ffff; + } + + uint8_t CodeWords() const { + if (HeaderWords(*this) == 1) { + if (isAArch64) + return (Data[0] & 0xf8000000) >> 27; + return (Data[0] & 0xf0000000) >> 28; + } + return (Data[1] & 0x00ff0000) >> 16; + } + + ArrayRef<support::ulittle32_t> EpilogueScopes() const { + assert(E() == 0 && "epilogue scopes are only present when the E bit is 0"); + size_t Offset = HeaderWords(*this); + return ArrayRef(&Data[Offset], EpilogueCount()); + } + + ArrayRef<uint8_t> UnwindByteCode() const { + const size_t Offset = HeaderWords(*this) + + (E() ? 0 : EpilogueCount()); + const uint8_t *ByteCode = + reinterpret_cast<const uint8_t *>(&Data[Offset]); + return ArrayRef(ByteCode, CodeWords() * sizeof(uint32_t)); + } + + uint32_t ExceptionHandlerRVA() const { + assert(X() && "Exception Handler RVA is only valid if the X bit is set"); + return Data[HeaderWords(*this) + (E() ? 0 : EpilogueCount()) + CodeWords()]; + } + + uint32_t ExceptionHandlerParameter() const { + assert(X() && "Exception Handler RVA is only valid if the X bit is set"); + return Data[HeaderWords(*this) + (E() ? 0 : EpilogueCount()) + CodeWords() + + 1]; + } +}; + +inline size_t HeaderWords(const ExceptionDataRecord &XR) { + if (XR.isAArch64) + return (XR.Data[0] & 0xffc00000) ? 1 : 2; + return (XR.Data[0] & 0xff800000) ? 1 : 2; +} +} +} +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/AlignOf.h b/contrib/libs/llvm16/include/llvm/Support/AlignOf.h new file mode 100644 index 00000000000..1521bcebb30 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/AlignOf.h @@ -0,0 +1,45 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- AlignOf.h - Portable calculation of type alignment -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the AlignedCharArrayUnion class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ALIGNOF_H +#define LLVM_SUPPORT_ALIGNOF_H + +#include <type_traits> + +namespace llvm { + +/// A suitably aligned and sized character array member which can hold elements +/// of any type. +/// +/// This template is equivalent to std::aligned_union_t<1, ...>, but we cannot +/// use it due to a bug in the MSVC x86 compiler: +/// https://github.com/microsoft/STL/issues/1533 +/// Using `alignas` here works around the bug. +template <typename T, typename... Ts> struct AlignedCharArrayUnion { + using AlignedUnion = std::aligned_union_t<1, T, Ts...>; + alignas(alignof(AlignedUnion)) char buffer[sizeof(AlignedUnion)]; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_ALIGNOF_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Alignment.h b/contrib/libs/llvm16/include/llvm/Support/Alignment.h new file mode 100644 index 00000000000..e2db67159f8 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Alignment.h @@ -0,0 +1,338 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Alignment.h - Useful alignment functions ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains types to represent alignments. +// They are instrumented to guarantee some invariants are preserved and prevent +// invalid manipulations. +// +// - Align represents an alignment in bytes, it is always set and always a valid +// power of two, its minimum value is 1 which means no alignment requirements. +// +// - MaybeAlign is an optional type, it may be undefined or set. When it's set +// you can get the underlying Align type by using the getValue() method. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ALIGNMENT_H_ +#define LLVM_SUPPORT_ALIGNMENT_H_ + +#include "llvm/Support/MathExtras.h" +#include <cassert> +#include <optional> +#ifndef NDEBUG +#include <string> +#endif // NDEBUG + +namespace llvm { + +#define ALIGN_CHECK_ISPOSITIVE(decl) \ + assert(decl > 0 && (#decl " should be defined")) + +/// This struct is a compact representation of a valid (non-zero power of two) +/// alignment. +/// It is suitable for use as static global constants. +struct Align { +private: + uint8_t ShiftValue = 0; /// The log2 of the required alignment. + /// ShiftValue is less than 64 by construction. + + friend struct MaybeAlign; + friend unsigned Log2(Align); + friend bool operator==(Align Lhs, Align Rhs); + friend bool operator!=(Align Lhs, Align Rhs); + friend bool operator<=(Align Lhs, Align Rhs); + friend bool operator>=(Align Lhs, Align Rhs); + friend bool operator<(Align Lhs, Align Rhs); + friend bool operator>(Align Lhs, Align Rhs); + friend unsigned encode(struct MaybeAlign A); + friend struct MaybeAlign decodeMaybeAlign(unsigned Value); + + /// A trivial type to allow construction of constexpr Align. + /// This is currently needed to workaround a bug in GCC 5.3 which prevents + /// definition of constexpr assign operators. + /// https://stackoverflow.com/questions/46756288/explicitly-defaulted-function-cannot-be-declared-as-constexpr-because-the-implic + /// FIXME: Remove this, make all assign operators constexpr and introduce user + /// defined literals when we don't have to support GCC 5.3 anymore. + /// https://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain + struct LogValue { + uint8_t Log; + }; + +public: + /// Default is byte-aligned. + constexpr Align() = default; + /// Do not perform checks in case of copy/move construct/assign, because the + /// checks have been performed when building `Other`. + constexpr Align(const Align &Other) = default; + constexpr Align(Align &&Other) = default; + Align &operator=(const Align &Other) = default; + Align &operator=(Align &&Other) = default; + + explicit Align(uint64_t Value) { + assert(Value > 0 && "Value must not be 0"); + assert(llvm::isPowerOf2_64(Value) && "Alignment is not a power of 2"); + ShiftValue = Log2_64(Value); + assert(ShiftValue < 64 && "Broken invariant"); + } + + /// This is a hole in the type system and should not be abused. + /// Needed to interact with C for instance. + uint64_t value() const { return uint64_t(1) << ShiftValue; } + + // Returns the previous alignment. + Align previous() const { + assert(ShiftValue != 0 && "Undefined operation"); + Align Out; + Out.ShiftValue = ShiftValue - 1; + return Out; + } + + /// Allow constructions of constexpr Align. + template <size_t kValue> constexpr static Align Constant() { + return LogValue{static_cast<uint8_t>(CTLog2<kValue>())}; + } + + /// Allow constructions of constexpr Align from types. + /// Compile time equivalent to Align(alignof(T)). + template <typename T> constexpr static Align Of() { + return Constant<std::alignment_of<T>::value>(); + } + + /// Constexpr constructor from LogValue type. + constexpr Align(LogValue CA) : ShiftValue(CA.Log) {} +}; + +/// Treats the value 0 as a 1, so Align is always at least 1. +inline Align assumeAligned(uint64_t Value) { + return Value ? Align(Value) : Align(); +} + +/// This struct is a compact representation of a valid (power of two) or +/// undefined (0) alignment. +struct MaybeAlign : public std::optional<Align> { +private: + using UP = std::optional<Align>; + +public: + /// Default is undefined. + MaybeAlign() = default; + /// Do not perform checks in case of copy/move construct/assign, because the + /// checks have been performed when building `Other`. + MaybeAlign(const MaybeAlign &Other) = default; + MaybeAlign &operator=(const MaybeAlign &Other) = default; + MaybeAlign(MaybeAlign &&Other) = default; + MaybeAlign &operator=(MaybeAlign &&Other) = default; + + constexpr MaybeAlign(std::nullopt_t None) : UP(None) {} + constexpr MaybeAlign(Align Value) : UP(Value) {} + explicit MaybeAlign(uint64_t Value) { + assert((Value == 0 || llvm::isPowerOf2_64(Value)) && + "Alignment is neither 0 nor a power of 2"); + if (Value) + emplace(Value); + } + + /// For convenience, returns a valid alignment or 1 if undefined. + Align valueOrOne() const { return value_or(Align()); } +}; + +/// Checks that SizeInBytes is a multiple of the alignment. +inline bool isAligned(Align Lhs, uint64_t SizeInBytes) { + return SizeInBytes % Lhs.value() == 0; +} + +/// Checks that Addr is a multiple of the alignment. +inline bool isAddrAligned(Align Lhs, const void *Addr) { + return isAligned(Lhs, reinterpret_cast<uintptr_t>(Addr)); +} + +/// Returns a multiple of A needed to store `Size` bytes. +inline uint64_t alignTo(uint64_t Size, Align A) { + const uint64_t Value = A.value(); + // The following line is equivalent to `(Size + Value - 1) / Value * Value`. + + // The division followed by a multiplication can be thought of as a right + // shift followed by a left shift which zeros out the extra bits produced in + // the bump; `~(Value - 1)` is a mask where all those bits being zeroed out + // are just zero. + + // Most compilers can generate this code but the pattern may be missed when + // multiple functions gets inlined. + return (Size + Value - 1) & ~(Value - 1U); +} + +/// If non-zero \p Skew is specified, the return value will be a minimal integer +/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for +/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p +/// Skew mod \p A'. +/// +/// Examples: +/// \code +/// alignTo(5, Align(8), 7) = 7 +/// alignTo(17, Align(8), 1) = 17 +/// alignTo(~0LL, Align(8), 3) = 3 +/// \endcode +inline uint64_t alignTo(uint64_t Size, Align A, uint64_t Skew) { + const uint64_t Value = A.value(); + Skew %= Value; + return alignTo(Size - Skew, A) + Skew; +} + +/// Aligns `Addr` to `Alignment` bytes, rounding up. +inline uintptr_t alignAddr(const void *Addr, Align Alignment) { + uintptr_t ArithAddr = reinterpret_cast<uintptr_t>(Addr); + assert(static_cast<uintptr_t>(ArithAddr + Alignment.value() - 1) >= + ArithAddr && + "Overflow"); + return alignTo(ArithAddr, Alignment); +} + +/// Returns the offset to the next integer (mod 2**64) that is greater than +/// or equal to \p Value and is a multiple of \p Align. +inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) { + return alignTo(Value, Alignment) - Value; +} + +/// Returns the necessary adjustment for aligning `Addr` to `Alignment` +/// bytes, rounding up. +inline uint64_t offsetToAlignedAddr(const void *Addr, Align Alignment) { + return offsetToAlignment(reinterpret_cast<uintptr_t>(Addr), Alignment); +} + +/// Returns the log2 of the alignment. +inline unsigned Log2(Align A) { return A.ShiftValue; } + +/// Returns the alignment that satisfies both alignments. +/// Same semantic as MinAlign. +inline Align commonAlignment(Align A, uint64_t Offset) { + return Align(MinAlign(A.value(), Offset)); +} + +/// Returns a representation of the alignment that encodes undefined as 0. +inline unsigned encode(MaybeAlign A) { return A ? A->ShiftValue + 1 : 0; } + +/// Dual operation of the encode function above. +inline MaybeAlign decodeMaybeAlign(unsigned Value) { + if (Value == 0) + return MaybeAlign(); + Align Out; + Out.ShiftValue = Value - 1; + return Out; +} + +/// Returns a representation of the alignment, the encoded value is positive by +/// definition. +inline unsigned encode(Align A) { return encode(MaybeAlign(A)); } + +/// Comparisons between Align and scalars. Rhs must be positive. +inline bool operator==(Align Lhs, uint64_t Rhs) { + ALIGN_CHECK_ISPOSITIVE(Rhs); + return Lhs.value() == Rhs; +} +inline bool operator!=(Align Lhs, uint64_t Rhs) { + ALIGN_CHECK_ISPOSITIVE(Rhs); + return Lhs.value() != Rhs; +} +inline bool operator<=(Align Lhs, uint64_t Rhs) { + ALIGN_CHECK_ISPOSITIVE(Rhs); + return Lhs.value() <= Rhs; +} +inline bool operator>=(Align Lhs, uint64_t Rhs) { + ALIGN_CHECK_ISPOSITIVE(Rhs); + return Lhs.value() >= Rhs; +} +inline bool operator<(Align Lhs, uint64_t Rhs) { + ALIGN_CHECK_ISPOSITIVE(Rhs); + return Lhs.value() < Rhs; +} +inline bool operator>(Align Lhs, uint64_t Rhs) { + ALIGN_CHECK_ISPOSITIVE(Rhs); + return Lhs.value() > Rhs; +} + +/// Comparisons operators between Align. +inline bool operator==(Align Lhs, Align Rhs) { + return Lhs.ShiftValue == Rhs.ShiftValue; +} +inline bool operator!=(Align Lhs, Align Rhs) { + return Lhs.ShiftValue != Rhs.ShiftValue; +} +inline bool operator<=(Align Lhs, Align Rhs) { + return Lhs.ShiftValue <= Rhs.ShiftValue; +} +inline bool operator>=(Align Lhs, Align Rhs) { + return Lhs.ShiftValue >= Rhs.ShiftValue; +} +inline bool operator<(Align Lhs, Align Rhs) { + return Lhs.ShiftValue < Rhs.ShiftValue; +} +inline bool operator>(Align Lhs, Align Rhs) { + return Lhs.ShiftValue > Rhs.ShiftValue; +} + +// Don't allow relational comparisons with MaybeAlign. +bool operator<=(Align Lhs, MaybeAlign Rhs) = delete; +bool operator>=(Align Lhs, MaybeAlign Rhs) = delete; +bool operator<(Align Lhs, MaybeAlign Rhs) = delete; +bool operator>(Align Lhs, MaybeAlign Rhs) = delete; + +bool operator<=(MaybeAlign Lhs, Align Rhs) = delete; +bool operator>=(MaybeAlign Lhs, Align Rhs) = delete; +bool operator<(MaybeAlign Lhs, Align Rhs) = delete; +bool operator>(MaybeAlign Lhs, Align Rhs) = delete; + +bool operator<=(MaybeAlign Lhs, MaybeAlign Rhs) = delete; +bool operator>=(MaybeAlign Lhs, MaybeAlign Rhs) = delete; +bool operator<(MaybeAlign Lhs, MaybeAlign Rhs) = delete; +bool operator>(MaybeAlign Lhs, MaybeAlign Rhs) = delete; + +// Allow equality comparisons between Align and MaybeAlign. +inline bool operator==(MaybeAlign Lhs, Align Rhs) { return Lhs && *Lhs == Rhs; } +inline bool operator!=(MaybeAlign Lhs, Align Rhs) { return !(Lhs == Rhs); } +inline bool operator==(Align Lhs, MaybeAlign Rhs) { return Rhs == Lhs; } +inline bool operator!=(Align Lhs, MaybeAlign Rhs) { return !(Rhs == Lhs); } +// Allow equality comparisons with MaybeAlign. +inline bool operator==(MaybeAlign Lhs, MaybeAlign Rhs) { + return (Lhs && Rhs && (*Lhs == *Rhs)) || (!Lhs && !Rhs); +} +inline bool operator!=(MaybeAlign Lhs, MaybeAlign Rhs) { return !(Lhs == Rhs); } +// Allow equality comparisons with std::nullopt. +inline bool operator==(MaybeAlign Lhs, std::nullopt_t) { return !bool(Lhs); } +inline bool operator!=(MaybeAlign Lhs, std::nullopt_t) { return bool(Lhs); } +inline bool operator==(std::nullopt_t, MaybeAlign Rhs) { return !bool(Rhs); } +inline bool operator!=(std::nullopt_t, MaybeAlign Rhs) { return bool(Rhs); } + +#ifndef NDEBUG +// For usage in LLVM_DEBUG macros. +inline std::string DebugStr(const Align &A) { + return std::to_string(A.value()); +} +// For usage in LLVM_DEBUG macros. +inline std::string DebugStr(const MaybeAlign &MA) { + if (MA) + return std::to_string(MA->value()); + return "None"; +} +#endif // NDEBUG + +#undef ALIGN_CHECK_ISPOSITIVE + +} // namespace llvm + +#endif // LLVM_SUPPORT_ALIGNMENT_H_ + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Allocator.h b/contrib/libs/llvm16/include/llvm/Support/Allocator.h new file mode 100644 index 00000000000..ed2920c8b9e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Allocator.h @@ -0,0 +1,465 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- Allocator.h - Simple memory allocation abstraction -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines the BumpPtrAllocator interface. BumpPtrAllocator conforms +/// to the LLVM "Allocator" concept and is similar to MallocAllocator, but +/// objects cannot be deallocated. Their lifetime is tied to the lifetime of the +/// allocator. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ALLOCATOR_H +#define LLVM_SUPPORT_ALLOCATOR_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/AllocatorBase.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <optional> +#include <utility> + +namespace llvm { + +namespace detail { + +// We call out to an external function to actually print the message as the +// printing code uses Allocator.h in its implementation. +void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t BytesAllocated, + size_t TotalMemory); + +} // end namespace detail + +/// Allocate memory in an ever growing pool, as if by bump-pointer. +/// +/// This isn't strictly a bump-pointer allocator as it uses backing slabs of +/// memory rather than relying on a boundless contiguous heap. However, it has +/// bump-pointer semantics in that it is a monotonically growing pool of memory +/// where every allocation is found by merely allocating the next N bytes in +/// the slab, or the next N bytes in the next slab. +/// +/// Note that this also has a threshold for forcing allocations above a certain +/// size into their own slab. +/// +/// The BumpPtrAllocatorImpl template defaults to using a MallocAllocator +/// object, which wraps malloc, to allocate memory, but it can be changed to +/// use a custom allocator. +/// +/// The GrowthDelay specifies after how many allocated slabs the allocator +/// increases the size of the slabs. +template <typename AllocatorT = MallocAllocator, size_t SlabSize = 4096, + size_t SizeThreshold = SlabSize, size_t GrowthDelay = 128> +class BumpPtrAllocatorImpl + : public AllocatorBase<BumpPtrAllocatorImpl<AllocatorT, SlabSize, + SizeThreshold, GrowthDelay>>, + private detail::AllocatorHolder<AllocatorT> { + using AllocTy = detail::AllocatorHolder<AllocatorT>; + +public: + static_assert(SizeThreshold <= SlabSize, + "The SizeThreshold must be at most the SlabSize to ensure " + "that objects larger than a slab go into their own memory " + "allocation."); + static_assert(GrowthDelay > 0, + "GrowthDelay must be at least 1 which already increases the" + "slab size after each allocated slab."); + + BumpPtrAllocatorImpl() = default; + + template <typename T> + BumpPtrAllocatorImpl(T &&Allocator) + : AllocTy(std::forward<T &&>(Allocator)) {} + + // Manually implement a move constructor as we must clear the old allocator's + // slabs as a matter of correctness. + BumpPtrAllocatorImpl(BumpPtrAllocatorImpl &&Old) + : AllocTy(std::move(Old.getAllocator())), CurPtr(Old.CurPtr), + End(Old.End), Slabs(std::move(Old.Slabs)), + CustomSizedSlabs(std::move(Old.CustomSizedSlabs)), + BytesAllocated(Old.BytesAllocated), RedZoneSize(Old.RedZoneSize) { + Old.CurPtr = Old.End = nullptr; + Old.BytesAllocated = 0; + Old.Slabs.clear(); + Old.CustomSizedSlabs.clear(); + } + + ~BumpPtrAllocatorImpl() { + DeallocateSlabs(Slabs.begin(), Slabs.end()); + DeallocateCustomSizedSlabs(); + } + + BumpPtrAllocatorImpl &operator=(BumpPtrAllocatorImpl &&RHS) { + DeallocateSlabs(Slabs.begin(), Slabs.end()); + DeallocateCustomSizedSlabs(); + + CurPtr = RHS.CurPtr; + End = RHS.End; + BytesAllocated = RHS.BytesAllocated; + RedZoneSize = RHS.RedZoneSize; + Slabs = std::move(RHS.Slabs); + CustomSizedSlabs = std::move(RHS.CustomSizedSlabs); + AllocTy::operator=(std::move(RHS.getAllocator())); + + RHS.CurPtr = RHS.End = nullptr; + RHS.BytesAllocated = 0; + RHS.Slabs.clear(); + RHS.CustomSizedSlabs.clear(); + return *this; + } + + /// Deallocate all but the current slab and reset the current pointer + /// to the beginning of it, freeing all memory allocated so far. + void Reset() { + // Deallocate all but the first slab, and deallocate all custom-sized slabs. + DeallocateCustomSizedSlabs(); + CustomSizedSlabs.clear(); + + if (Slabs.empty()) + return; + + // Reset the state. + BytesAllocated = 0; + CurPtr = (char *)Slabs.front(); + End = CurPtr + SlabSize; + + __asan_poison_memory_region(*Slabs.begin(), computeSlabSize(0)); + DeallocateSlabs(std::next(Slabs.begin()), Slabs.end()); + Slabs.erase(std::next(Slabs.begin()), Slabs.end()); + } + + /// Allocate space at the specified alignment. + // This method is *not* marked noalias, because + // SpecificBumpPtrAllocator::DestroyAll() loops over all allocations, and + // that loop is not based on the Allocate() return value. + // + // Allocate(0, N) is valid, it returns a non-null pointer (which should not + // be dereferenced). + LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, Align Alignment) { + // Keep track of how many bytes we've allocated. + BytesAllocated += Size; + + size_t Adjustment = offsetToAlignedAddr(CurPtr, Alignment); + assert(Adjustment + Size >= Size && "Adjustment + Size must not overflow"); + + size_t SizeToAllocate = Size; +#if LLVM_ADDRESS_SANITIZER_BUILD + // Add trailing bytes as a "red zone" under ASan. + SizeToAllocate += RedZoneSize; +#endif + + // Check if we have enough space. + if (Adjustment + SizeToAllocate <= size_t(End - CurPtr) + // We can't return nullptr even for a zero-sized allocation! + && CurPtr != nullptr) { + char *AlignedPtr = CurPtr + Adjustment; + CurPtr = AlignedPtr + SizeToAllocate; + // Update the allocation point of this memory block in MemorySanitizer. + // Without this, MemorySanitizer messages for values originated from here + // will point to the allocation of the entire slab. + __msan_allocated_memory(AlignedPtr, Size); + // Similarly, tell ASan about this space. + __asan_unpoison_memory_region(AlignedPtr, Size); + return AlignedPtr; + } + + // If Size is really big, allocate a separate slab for it. + size_t PaddedSize = SizeToAllocate + Alignment.value() - 1; + if (PaddedSize > SizeThreshold) { + void *NewSlab = + this->getAllocator().Allocate(PaddedSize, alignof(std::max_align_t)); + // We own the new slab and don't want anyone reading anyting other than + // pieces returned from this method. So poison the whole slab. + __asan_poison_memory_region(NewSlab, PaddedSize); + CustomSizedSlabs.push_back(std::make_pair(NewSlab, PaddedSize)); + + uintptr_t AlignedAddr = alignAddr(NewSlab, Alignment); + assert(AlignedAddr + Size <= (uintptr_t)NewSlab + PaddedSize); + char *AlignedPtr = (char*)AlignedAddr; + __msan_allocated_memory(AlignedPtr, Size); + __asan_unpoison_memory_region(AlignedPtr, Size); + return AlignedPtr; + } + + // Otherwise, start a new slab and try again. + StartNewSlab(); + uintptr_t AlignedAddr = alignAddr(CurPtr, Alignment); + assert(AlignedAddr + SizeToAllocate <= (uintptr_t)End && + "Unable to allocate memory!"); + char *AlignedPtr = (char*)AlignedAddr; + CurPtr = AlignedPtr + SizeToAllocate; + __msan_allocated_memory(AlignedPtr, Size); + __asan_unpoison_memory_region(AlignedPtr, Size); + return AlignedPtr; + } + + inline LLVM_ATTRIBUTE_RETURNS_NONNULL void * + Allocate(size_t Size, size_t Alignment) { + assert(Alignment > 0 && "0-byte alignment is not allowed. Use 1 instead."); + return Allocate(Size, Align(Alignment)); + } + + // 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, size_t /*Alignment*/) { + __asan_poison_memory_region(Ptr, Size); + } + + // Pull in base class overloads. + using AllocatorBase<BumpPtrAllocatorImpl>::Deallocate; + + size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); } + + /// \return An index uniquely and reproducibly identifying + /// an input pointer \p Ptr in the given allocator. + /// The returned value is negative iff the object is inside a custom-size + /// slab. + /// Returns an empty optional if the pointer is not found in the allocator. + std::optional<int64_t> identifyObject(const void *Ptr) { + const char *P = static_cast<const char *>(Ptr); + int64_t InSlabIdx = 0; + for (size_t Idx = 0, E = Slabs.size(); Idx < E; Idx++) { + const char *S = static_cast<const char *>(Slabs[Idx]); + if (P >= S && P < S + computeSlabSize(Idx)) + return InSlabIdx + static_cast<int64_t>(P - S); + InSlabIdx += static_cast<int64_t>(computeSlabSize(Idx)); + } + + // Use negative index to denote custom sized slabs. + int64_t InCustomSizedSlabIdx = -1; + for (size_t Idx = 0, E = CustomSizedSlabs.size(); Idx < E; Idx++) { + const char *S = static_cast<const char *>(CustomSizedSlabs[Idx].first); + size_t Size = CustomSizedSlabs[Idx].second; + if (P >= S && P < S + Size) + return InCustomSizedSlabIdx - static_cast<int64_t>(P - S); + InCustomSizedSlabIdx -= static_cast<int64_t>(Size); + } + return std::nullopt; + } + + /// A wrapper around identifyObject that additionally asserts that + /// the object is indeed within the allocator. + /// \return An index uniquely and reproducibly identifying + /// an input pointer \p Ptr in the given allocator. + int64_t identifyKnownObject(const void *Ptr) { + std::optional<int64_t> Out = identifyObject(Ptr); + assert(Out && "Wrong allocator used"); + return *Out; + } + + /// A wrapper around identifyKnownObject. Accepts type information + /// about the object and produces a smaller identifier by relying on + /// the alignment information. Note that sub-classes may have different + /// alignment, so the most base class should be passed as template parameter + /// in order to obtain correct results. For that reason automatic template + /// parameter deduction is disabled. + /// \return An index uniquely and reproducibly identifying + /// an input pointer \p Ptr in the given allocator. This identifier is + /// different from the ones produced by identifyObject and + /// identifyAlignedObject. + template <typename T> + int64_t identifyKnownAlignedObject(const void *Ptr) { + int64_t Out = identifyKnownObject(Ptr); + assert(Out % alignof(T) == 0 && "Wrong alignment information"); + return Out / alignof(T); + } + + size_t getTotalMemory() const { + size_t TotalMemory = 0; + for (auto I = Slabs.begin(), E = Slabs.end(); I != E; ++I) + TotalMemory += computeSlabSize(std::distance(Slabs.begin(), I)); + for (const auto &PtrAndSize : CustomSizedSlabs) + TotalMemory += PtrAndSize.second; + return TotalMemory; + } + + size_t getBytesAllocated() const { return BytesAllocated; } + + void setRedZoneSize(size_t NewSize) { + RedZoneSize = NewSize; + } + + void PrintStats() const { + detail::printBumpPtrAllocatorStats(Slabs.size(), BytesAllocated, + getTotalMemory()); + } + +private: + /// The current pointer into the current slab. + /// + /// This points to the next free byte in the slab. + char *CurPtr = nullptr; + + /// The end of the current slab. + char *End = nullptr; + + /// The slabs allocated so far. + SmallVector<void *, 4> Slabs; + + /// Custom-sized slabs allocated for too-large allocation requests. + SmallVector<std::pair<void *, size_t>, 0> CustomSizedSlabs; + + /// How many bytes we've allocated. + /// + /// Used so that we can compute how much space was wasted. + size_t BytesAllocated = 0; + + /// The number of bytes to put between allocations when running under + /// a sanitizer. + size_t RedZoneSize = 1; + + static size_t computeSlabSize(unsigned SlabIdx) { + // Scale the actual allocated slab size based on the number of slabs + // allocated. Every GrowthDelay slabs allocated, we double + // the allocated size to reduce allocation frequency, but saturate at + // multiplying the slab size by 2^30. + return SlabSize * + ((size_t)1 << std::min<size_t>(30, SlabIdx / GrowthDelay)); + } + + /// Allocate a new slab and move the bump pointers over into the new + /// slab, modifying CurPtr and End. + void StartNewSlab() { + size_t AllocatedSlabSize = computeSlabSize(Slabs.size()); + + void *NewSlab = this->getAllocator().Allocate(AllocatedSlabSize, + alignof(std::max_align_t)); + // We own the new slab and don't want anyone reading anything other than + // pieces returned from this method. So poison the whole slab. + __asan_poison_memory_region(NewSlab, AllocatedSlabSize); + + Slabs.push_back(NewSlab); + CurPtr = (char *)(NewSlab); + End = ((char *)NewSlab) + AllocatedSlabSize; + } + + /// Deallocate a sequence of slabs. + void DeallocateSlabs(SmallVectorImpl<void *>::iterator I, + SmallVectorImpl<void *>::iterator E) { + for (; I != E; ++I) { + size_t AllocatedSlabSize = + computeSlabSize(std::distance(Slabs.begin(), I)); + this->getAllocator().Deallocate(*I, AllocatedSlabSize, + alignof(std::max_align_t)); + } + } + + /// Deallocate all memory for custom sized slabs. + void DeallocateCustomSizedSlabs() { + for (auto &PtrAndSize : CustomSizedSlabs) { + void *Ptr = PtrAndSize.first; + size_t Size = PtrAndSize.second; + this->getAllocator().Deallocate(Ptr, Size, alignof(std::max_align_t)); + } + } + + template <typename T> friend class SpecificBumpPtrAllocator; +}; + +/// The standard BumpPtrAllocator which just uses the default template +/// parameters. +typedef BumpPtrAllocatorImpl<> BumpPtrAllocator; + +/// A BumpPtrAllocator that allows only elements of a specific type to be +/// allocated. +/// +/// This allows calling the destructor in DestroyAll() and when the allocator is +/// destroyed. +template <typename T> class SpecificBumpPtrAllocator { + BumpPtrAllocator Allocator; + +public: + SpecificBumpPtrAllocator() { + // Because SpecificBumpPtrAllocator walks the memory to call destructors, + // it can't have red zones between allocations. + Allocator.setRedZoneSize(0); + } + SpecificBumpPtrAllocator(SpecificBumpPtrAllocator &&Old) + : Allocator(std::move(Old.Allocator)) {} + ~SpecificBumpPtrAllocator() { DestroyAll(); } + + SpecificBumpPtrAllocator &operator=(SpecificBumpPtrAllocator &&RHS) { + Allocator = std::move(RHS.Allocator); + return *this; + } + + /// Call the destructor of each allocated object and deallocate all but the + /// current slab and reset the current pointer to the beginning of it, freeing + /// all memory allocated so far. + void DestroyAll() { + auto DestroyElements = [](char *Begin, char *End) { + assert(Begin == (char *)alignAddr(Begin, Align::Of<T>())); + for (char *Ptr = Begin; Ptr + sizeof(T) <= End; Ptr += sizeof(T)) + reinterpret_cast<T *>(Ptr)->~T(); + }; + + for (auto I = Allocator.Slabs.begin(), E = Allocator.Slabs.end(); I != E; + ++I) { + size_t AllocatedSlabSize = BumpPtrAllocator::computeSlabSize( + std::distance(Allocator.Slabs.begin(), I)); + char *Begin = (char *)alignAddr(*I, Align::Of<T>()); + char *End = *I == Allocator.Slabs.back() ? Allocator.CurPtr + : (char *)*I + AllocatedSlabSize; + + DestroyElements(Begin, End); + } + + for (auto &PtrAndSize : Allocator.CustomSizedSlabs) { + void *Ptr = PtrAndSize.first; + size_t Size = PtrAndSize.second; + DestroyElements((char *)alignAddr(Ptr, Align::Of<T>()), + (char *)Ptr + Size); + } + + Allocator.Reset(); + } + + /// Allocate space for an array of objects without constructing them. + T *Allocate(size_t num = 1) { return Allocator.Allocate<T>(num); } +}; + +} // end namespace llvm + +template <typename AllocatorT, size_t SlabSize, size_t SizeThreshold, + size_t GrowthDelay> +void * +operator new(size_t Size, + llvm::BumpPtrAllocatorImpl<AllocatorT, SlabSize, SizeThreshold, + GrowthDelay> &Allocator) { + return Allocator.Allocate(Size, std::min((size_t)llvm::NextPowerOf2(Size), + alignof(std::max_align_t))); +} + +template <typename AllocatorT, size_t SlabSize, size_t SizeThreshold, + size_t GrowthDelay> +void operator delete(void *, + llvm::BumpPtrAllocatorImpl<AllocatorT, SlabSize, + SizeThreshold, GrowthDelay> &) { +} + +#endif // LLVM_SUPPORT_ALLOCATOR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/AllocatorBase.h b/contrib/libs/llvm16/include/llvm/Support/AllocatorBase.h new file mode 100644 index 00000000000..94efa8a3993 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/AllocatorBase.h @@ -0,0 +1,137 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- AllocatorBase.h - Simple memory allocation abstraction ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines MallocAllocator. MallocAllocator conforms to the LLVM +/// "Allocator" concept which consists of an Allocate method accepting a size +/// and alignment, and a Deallocate accepting a pointer and size. Further, the +/// LLVM "Allocator" concept has overloads of Allocate and Deallocate for +/// setting size and alignment based on the final type. These overloads are +/// typically provided by a base class template \c AllocatorBase. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ALLOCATORBASE_H +#define LLVM_SUPPORT_ALLOCATORBASE_H + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MemAlloc.h" +#include <type_traits> + +namespace llvm { + +/// CRTP base class providing obvious overloads for the core \c +/// Allocate() methods of LLVM-style allocators. +/// +/// This base class both documents the full public interface exposed by all +/// LLVM-style allocators, and redirects all of the overloads to a single core +/// set of methods which the derived class must define. +template <typename DerivedT> class AllocatorBase { +public: + /// Allocate \a Size bytes of \a Alignment aligned memory. This method + /// must be implemented by \c DerivedT. + void *Allocate(size_t Size, size_t Alignment) { +#ifdef __clang__ + static_assert(static_cast<void *(AllocatorBase::*)(size_t, size_t)>( + &AllocatorBase::Allocate) != + static_cast<void *(DerivedT::*)(size_t, size_t)>( + &DerivedT::Allocate), + "Class derives from AllocatorBase without implementing the " + "core Allocate(size_t, size_t) overload!"); +#endif + return static_cast<DerivedT *>(this)->Allocate(Size, Alignment); + } + + /// Deallocate \a Ptr to \a Size bytes of memory allocated by this + /// allocator. + void Deallocate(const void *Ptr, size_t Size, size_t Alignment) { +#ifdef __clang__ + static_assert( + static_cast<void (AllocatorBase::*)(const void *, size_t, size_t)>( + &AllocatorBase::Deallocate) != + static_cast<void (DerivedT::*)(const void *, size_t, size_t)>( + &DerivedT::Deallocate), + "Class derives from AllocatorBase without implementing the " + "core Deallocate(void *) overload!"); +#endif + return static_cast<DerivedT *>(this)->Deallocate(Ptr, Size, Alignment); + } + + // The rest of these methods are helpers that redirect to one of the above + // core methods. + + /// Allocate space for a sequence of objects without constructing them. + template <typename T> T *Allocate(size_t Num = 1) { + return static_cast<T *>(Allocate(Num * sizeof(T), alignof(T))); + } + + /// Deallocate space for a sequence of objects without constructing them. + template <typename T> + std::enable_if_t<!std::is_same<std::remove_cv_t<T>, void>::value, void> + Deallocate(T *Ptr, size_t Num = 1) { + Deallocate(static_cast<const void *>(Ptr), Num * sizeof(T), alignof(T)); + } +}; + +class MallocAllocator : public AllocatorBase<MallocAllocator> { +public: + void Reset() {} + + LLVM_ATTRIBUTE_RETURNS_NONNULL void *Allocate(size_t Size, size_t Alignment) { + return allocate_buffer(Size, Alignment); + } + + // Pull in base class overloads. + using AllocatorBase<MallocAllocator>::Allocate; + + void Deallocate(const void *Ptr, size_t Size, size_t Alignment) { + deallocate_buffer(const_cast<void *>(Ptr), Size, Alignment); + } + + // Pull in base class overloads. + using AllocatorBase<MallocAllocator>::Deallocate; + + void PrintStats() const {} +}; + +namespace detail { + +template <typename Alloc> class AllocatorHolder : Alloc { +public: + AllocatorHolder() = default; + AllocatorHolder(const Alloc &A) : Alloc(A) {} + AllocatorHolder(Alloc &&A) : Alloc(static_cast<Alloc &&>(A)) {} + Alloc &getAllocator() { return *this; } + const Alloc &getAllocator() const { return *this; } +}; + +template <typename Alloc> class AllocatorHolder<Alloc &> { + Alloc &A; + +public: + AllocatorHolder(Alloc &A) : A(A) {} + Alloc &getAllocator() { return A; } + const Alloc &getAllocator() const { return A; } +}; + +} // namespace detail + +} // namespace llvm + +#endif // LLVM_SUPPORT_ALLOCATORBASE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ArrayRecycler.h b/contrib/libs/llvm16/include/llvm/Support/ArrayRecycler.h new file mode 100644 index 00000000000..b77cd88162f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ArrayRecycler.h @@ -0,0 +1,155 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==- llvm/Support/ArrayRecycler.h - Recycling of Arrays ---------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the ArrayRecycler class template which can recycle small +// arrays allocated from one of the allocators in Allocator.h +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ARRAYRECYCLER_H +#define LLVM_SUPPORT_ARRAYRECYCLER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/MathExtras.h" + +namespace llvm { + +/// Recycle small arrays allocated from a BumpPtrAllocator. +/// +/// Arrays are allocated in a small number of fixed sizes. For each supported +/// array size, the ArrayRecycler keeps a free list of available arrays. +/// +template <class T, size_t Align = alignof(T)> class ArrayRecycler { + // The free list for a given array size is a simple singly linked list. + // We can't use iplist or Recycler here since those classes can't be copied. + struct FreeList { + FreeList *Next; + }; + + static_assert(Align >= alignof(FreeList), "Object underaligned"); + static_assert(sizeof(T) >= sizeof(FreeList), "Objects are too small"); + + // Keep a free list for each array size. + SmallVector<FreeList*, 8> Bucket; + + // Remove an entry from the free list in Bucket[Idx] and return it. + // Return NULL if no entries are available. + T *pop(unsigned Idx) { + if (Idx >= Bucket.size()) + return nullptr; + FreeList *Entry = Bucket[Idx]; + if (!Entry) + return nullptr; + __asan_unpoison_memory_region(Entry, Capacity::get(Idx).getSize()); + Bucket[Idx] = Entry->Next; + __msan_allocated_memory(Entry, Capacity::get(Idx).getSize()); + return reinterpret_cast<T*>(Entry); + } + + // Add an entry to the free list at Bucket[Idx]. + void push(unsigned Idx, T *Ptr) { + assert(Ptr && "Cannot recycle NULL pointer"); + FreeList *Entry = reinterpret_cast<FreeList*>(Ptr); + if (Idx >= Bucket.size()) + Bucket.resize(size_t(Idx) + 1); + Entry->Next = Bucket[Idx]; + Bucket[Idx] = Entry; + __asan_poison_memory_region(Ptr, Capacity::get(Idx).getSize()); + } + +public: + /// The size of an allocated array is represented by a Capacity instance. + /// + /// This class is much smaller than a size_t, and it provides methods to work + /// with the set of legal array capacities. + class Capacity { + uint8_t Index; + explicit Capacity(uint8_t idx) : Index(idx) {} + + public: + Capacity() : Index(0) {} + + /// Get the capacity of an array that can hold at least N elements. + static Capacity get(size_t N) { + return Capacity(N ? Log2_64_Ceil(N) : 0); + } + + /// Get the number of elements in an array with this capacity. + size_t getSize() const { return size_t(1u) << Index; } + + /// Get the bucket number for this capacity. + unsigned getBucket() const { return Index; } + + /// Get the next larger capacity. Large capacities grow exponentially, so + /// this function can be used to reallocate incrementally growing vectors + /// in amortized linear time. + Capacity getNext() const { return Capacity(Index + 1); } + }; + + ~ArrayRecycler() { + // The client should always call clear() so recycled arrays can be returned + // to the allocator. + assert(Bucket.empty() && "Non-empty ArrayRecycler deleted!"); + } + + /// Release all the tracked allocations to the allocator. The recycler must + /// be free of any tracked allocations before being deleted. + template<class AllocatorType> + void clear(AllocatorType &Allocator) { + for (; !Bucket.empty(); Bucket.pop_back()) + while (T *Ptr = pop(Bucket.size() - 1)) + Allocator.Deallocate(Ptr); + } + + /// Special case for BumpPtrAllocator which has an empty Deallocate() + /// function. + /// + /// There is no need to traverse the free lists, pulling all the objects into + /// cache. + void clear(BumpPtrAllocator&) { + Bucket.clear(); + } + + /// Allocate an array of at least the requested capacity. + /// + /// Return an existing recycled array, or allocate one from Allocator if + /// none are available for recycling. + /// + template<class AllocatorType> + T *allocate(Capacity Cap, AllocatorType &Allocator) { + // Try to recycle an existing array. + if (T *Ptr = pop(Cap.getBucket())) + return Ptr; + // Nope, get more memory. + return static_cast<T*>(Allocator.Allocate(sizeof(T)*Cap.getSize(), Align)); + } + + /// Deallocate an array with the specified Capacity. + /// + /// Cap must be the same capacity that was given to allocate(). + /// + void deallocate(Capacity Cap, T *Ptr) { + push(Cap.getBucket(), Ptr); + } +}; + +} // end llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Atomic.h b/contrib/libs/llvm16/include/llvm/Support/Atomic.h new file mode 100644 index 00000000000..90cc634249b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Atomic.h @@ -0,0 +1,53 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Atomic.h - Atomic Operations -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys atomic operations. +// +// DO NOT USE IN NEW CODE! +// +// New code should always rely on the std::atomic facilities in C++11. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ATOMIC_H +#define LLVM_SUPPORT_ATOMIC_H + +#include "llvm/Support/DataTypes.h" + +// Windows will at times define MemoryFence. +#ifdef MemoryFence +#undef MemoryFence +#endif + +namespace llvm { + namespace sys { + void MemoryFence(); + +#ifdef _MSC_VER + typedef long cas_flag; +#else + typedef uint32_t cas_flag; +#endif + cas_flag CompareAndSwap(volatile cas_flag* ptr, + cas_flag new_value, + cas_flag old_value); + } +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/AtomicOrdering.h b/contrib/libs/llvm16/include/llvm/Support/AtomicOrdering.h new file mode 100644 index 00000000000..5c76a270e9d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/AtomicOrdering.h @@ -0,0 +1,173 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/AtomicOrdering.h ---Atomic Ordering---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Atomic ordering constants. +/// +/// These values are used by LLVM to represent atomic ordering for C++11's +/// memory model and more, as detailed in docs/Atomics.rst. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ATOMICORDERING_H +#define LLVM_SUPPORT_ATOMICORDERING_H + +#include <cstddef> + +namespace llvm { + +/// Atomic ordering for C11 / C++11's memory models. +/// +/// These values cannot change because they are shared with standard library +/// implementations as well as with other compilers. +enum class AtomicOrderingCABI { + relaxed = 0, + consume = 1, + acquire = 2, + release = 3, + acq_rel = 4, + seq_cst = 5, +}; + +bool operator<(AtomicOrderingCABI, AtomicOrderingCABI) = delete; +bool operator>(AtomicOrderingCABI, AtomicOrderingCABI) = delete; +bool operator<=(AtomicOrderingCABI, AtomicOrderingCABI) = delete; +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> inline bool isValidAtomicOrderingCABI(Int I) { + return (Int)AtomicOrderingCABI::relaxed <= I && + I <= (Int)AtomicOrderingCABI::seq_cst; +} + +/// Atomic ordering for LLVM's memory model. +/// +/// C++ defines ordering as a lattice. LLVM supplements this with NotAtomic and +/// Unordered, which are both below the C++ orders. +/// +/// not_atomic-->unordered-->relaxed-->release--------------->acq_rel-->seq_cst +/// \-->consume-->acquire--/ +enum class AtomicOrdering : unsigned { + NotAtomic = 0, + Unordered = 1, + Monotonic = 2, // Equivalent to C++'s relaxed. + // Consume = 3, // Not specified yet. + Acquire = 4, + Release = 5, + AcquireRelease = 6, + SequentiallyConsistent = 7, + LAST = SequentiallyConsistent +}; + +bool operator<(AtomicOrdering, AtomicOrdering) = delete; +bool operator>(AtomicOrdering, AtomicOrdering) = delete; +bool operator<=(AtomicOrdering, AtomicOrdering) = delete; +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> 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. +inline const char *toIRString(AtomicOrdering ao) { + static const char *names[8] = {"not_atomic", "unordered", "monotonic", + "consume", "acquire", "release", + "acq_rel", "seq_cst"}; + return names[static_cast<size_t>(ao)]; +} + +/// Returns true if ao is stronger than other as defined by the AtomicOrdering +/// lattice, which is based on C++'s definition. +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}, + /* Unordered */ { true, false, false, false, false, false, false, false}, + /* relaxed */ { true, true, false, false, false, false, false, false}, + /* consume */ { true, true, true, false, false, false, false, false}, + /* acquire */ { true, true, true, true, false, false, false, false}, + /* release */ { true, true, true, false, false, false, false, false}, + /* acq_rel */ { true, true, true, true, true, true, false, false}, + /* seq_cst */ { true, true, true, true, true, true, true, false}, + }; + return lookup[static_cast<size_t>(AO)][static_cast<size_t>(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}, + /* Unordered */ { true, true, false, false, false, false, false, false}, + /* relaxed */ { true, true, true, false, false, false, false, false}, + /* consume */ { true, true, true, true, false, false, false, false}, + /* acquire */ { true, true, true, true, true, false, false, false}, + /* release */ { true, true, true, false, false, true, false, false}, + /* acq_rel */ { true, true, true, true, true, true, true, false}, + /* seq_cst */ { true, true, true, true, true, true, true, true}, + }; + return lookup[static_cast<size_t>(AO)][static_cast<size_t>(Other)]; +} + +inline bool isStrongerThanUnordered(AtomicOrdering AO) { + return isStrongerThan(AO, AtomicOrdering::Unordered); +} + +inline bool isStrongerThanMonotonic(AtomicOrdering AO) { + return isStrongerThan(AO, AtomicOrdering::Monotonic); +} + +inline bool isAcquireOrStronger(AtomicOrdering AO) { + return isAtLeastOrStrongerThan(AO, AtomicOrdering::Acquire); +} + +inline bool isReleaseOrStronger(AtomicOrdering AO) { + return isAtLeastOrStrongerThan(AO, AtomicOrdering::Release); +} + +/// Return a single atomic ordering that is at least as strong as both the \p AO +/// and \p Other orderings for an atomic operation. +inline AtomicOrdering getMergedAtomicOrdering(AtomicOrdering AO, + AtomicOrdering Other) { + if ((AO == AtomicOrdering::Acquire && Other == AtomicOrdering::Release) || + (AO == AtomicOrdering::Release && Other == AtomicOrdering::Acquire)) + return AtomicOrdering::AcquireRelease; + return isStrongerThan(AO, Other) ? AO : Other; +} + +inline AtomicOrderingCABI toCABI(AtomicOrdering AO) { + static const AtomicOrderingCABI lookup[8] = { + /* NotAtomic */ AtomicOrderingCABI::relaxed, + /* Unordered */ AtomicOrderingCABI::relaxed, + /* relaxed */ AtomicOrderingCABI::relaxed, + /* consume */ AtomicOrderingCABI::consume, + /* acquire */ AtomicOrderingCABI::acquire, + /* release */ AtomicOrderingCABI::release, + /* acq_rel */ AtomicOrderingCABI::acq_rel, + /* seq_cst */ AtomicOrderingCABI::seq_cst, + }; + return lookup[static_cast<size_t>(AO)]; +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_ATOMICORDERING_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/AutoConvert.h b/contrib/libs/llvm16/include/llvm/Support/AutoConvert.h new file mode 100644 index 00000000000..94fa3f597f5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/AutoConvert.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- AutoConvert.h - Auto conversion between ASCII/EBCDIC -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains functions used for auto conversion between +// ASCII/EBCDIC codepages specific to z/OS. +// +//===----------------------------------------------------------------------===//i + +#ifndef LLVM_SUPPORT_AUTOCONVERT_H +#define LLVM_SUPPORT_AUTOCONVERT_H + +#ifdef __MVS__ +#define CCSID_IBM_1047 1047 +#define CCSID_UTF_8 1208 +#include <system_error> + +namespace llvm { + +/// \brief Disable the z/OS enhanced ASCII auto-conversion for the file +/// descriptor. +std::error_code disableAutoConversion(int FD); + +/// \brief Query the z/OS enhanced ASCII auto-conversion status of a file +/// descriptor and force the conversion if the file is not tagged with a +/// codepage. +std::error_code enableAutoConversion(int FD); + +/// \brief Set the tag information for a file descriptor. +std::error_code setFileTag(int FD, int CCSID, bool Text); + +} // namespace llvm + +#endif // __MVS__ + +#endif // LLVM_SUPPORT_AUTOCONVERT_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BCD.h b/contrib/libs/llvm16/include/llvm/Support/BCD.h new file mode 100644 index 00000000000..6da429546d4 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BCD.h @@ -0,0 +1,64 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/BCD.h - Binary-Coded Decimal utility functions -*- C++ -*-// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares some utility functions for encoding/decoding BCD values. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BCD_H +#define LLVM_SUPPORT_BCD_H + +#include <assert.h> +#include <cstddef> +#include <cstdint> + +namespace llvm { + +// Decode a packed BCD value. +// Maximum value of int64_t is 9,223,372,036,854,775,807. These are 18 usable +// decimal digits. Thus BCD numbers of up to 9 bytes can be converted. +// Please note that s390 supports BCD numbers up to a length of 16 bytes. +inline int64_t decodePackedBCD(const uint8_t *Ptr, size_t ByteLen, + bool IsSigned = true) { + assert(ByteLen >= 1 && ByteLen <= 9 && "Invalid BCD number"); + int64_t Value = 0; + size_t RunLen = ByteLen - static_cast<unsigned>(IsSigned); + for (size_t I = 0; I < RunLen; ++I) { + uint8_t DecodedByteValue = ((Ptr[I] >> 4) & 0x0f) * 10 + (Ptr[I] & 0x0f); + Value = (Value * 100) + DecodedByteValue; + } + if (IsSigned) { + uint8_t DecodedByteValue = (Ptr[ByteLen - 1] >> 4) & 0x0f; + uint8_t Sign = Ptr[ByteLen - 1] & 0x0f; + Value = (Value * 10) + DecodedByteValue; + if (Sign == 0x0d || Sign == 0x0b) + Value *= -1; + } + return Value; +} + +template <typename ResultT, typename ValT> +inline ResultT decodePackedBCD(const ValT Val, bool IsSigned = true) { + return static_cast<ResultT>(decodePackedBCD( + reinterpret_cast<const uint8_t *>(&Val), sizeof(ValT), IsSigned)); +} + +} // namespace llvm + +#endif // LLVM_SUPPORT_BCD_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BLAKE3.h b/contrib/libs/llvm16/include/llvm/Support/BLAKE3.h new file mode 100644 index 00000000000..eb38e12052f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BLAKE3.h @@ -0,0 +1,135 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==- BLAKE3.h - BLAKE3 C++ wrapper for LLVM ---------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a C++ wrapper of the BLAKE3 C interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BLAKE3_H +#define LLVM_SUPPORT_BLAKE3_H + +#include "llvm-c/blake3.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +/// The constant \p LLVM_BLAKE3_OUT_LEN provides the default output length, +/// 32 bytes, which is recommended for most callers. +/// +/// Outputs shorter than the default length of 32 bytes (256 bits) provide +/// less security. An N-bit BLAKE3 output is intended to provide N bits of +/// first and second preimage resistance and N/2 bits of collision +/// resistance, for any N up to 256. Longer outputs don't provide any +/// additional security. +/// +/// Shorter BLAKE3 outputs are prefixes of longer ones. Explicitly +/// requesting a short output is equivalent to truncating the default-length +/// output. +template <size_t NumBytes = LLVM_BLAKE3_OUT_LEN> +using BLAKE3Result = std::array<uint8_t, NumBytes>; + +/// A class that wraps the BLAKE3 algorithm. +class BLAKE3 { +public: + BLAKE3() { init(); } + + /// Reinitialize the internal state + void init() { llvm_blake3_hasher_init(&Hasher); } + + /// Digest more data. + void update(ArrayRef<uint8_t> Data) { + llvm_blake3_hasher_update(&Hasher, Data.data(), Data.size()); + } + + /// Digest more data. + void update(StringRef Str) { + llvm_blake3_hasher_update(&Hasher, Str.data(), Str.size()); + } + + /// Finalize the hasher and put the result in \p Result. + /// This doesn't modify the hasher itself, and it's possible to finalize again + /// after adding more input. + template <size_t NumBytes = LLVM_BLAKE3_OUT_LEN> + void final(BLAKE3Result<NumBytes> &Result) { + llvm_blake3_hasher_finalize(&Hasher, Result.data(), Result.size()); + } + + /// Finalize the hasher and return an output of any length, given in bytes. + /// This doesn't modify the hasher itself, and it's possible to finalize again + /// after adding more input. + template <size_t NumBytes = LLVM_BLAKE3_OUT_LEN> + BLAKE3Result<NumBytes> final() { + BLAKE3Result<NumBytes> Result; + llvm_blake3_hasher_finalize(&Hasher, Result.data(), Result.size()); + return Result; + } + + /// Return the current output for the digested data since the last call to + /// init(). + /// + /// Other hash functions distinguish between \p result() and \p final(), with + /// \p result() allowing more calls into \p update(), but there's no + // difference for the BLAKE3 hash function. + template <size_t NumBytes = LLVM_BLAKE3_OUT_LEN> + BLAKE3Result<NumBytes> result() { + return final<NumBytes>(); + } + + /// Returns a BLAKE3 hash for the given data. + template <size_t NumBytes = LLVM_BLAKE3_OUT_LEN> + static BLAKE3Result<NumBytes> hash(ArrayRef<uint8_t> Data) { + BLAKE3 Hasher; + Hasher.update(Data); + return Hasher.final<NumBytes>(); + } + +private: + llvm_blake3_hasher Hasher; +}; + +/// Like \p BLAKE3 but using a class-level template parameter for specifying the +/// hash size of the \p final() and \p result() functions. +/// +/// This is useful for using BLAKE3 as the hasher type for \p HashBuilder with +/// non-default hash sizes. +template <size_t NumBytes> class TruncatedBLAKE3 : public BLAKE3 { +public: + /// Finalize the hasher and put the result in \p Result. + /// This doesn't modify the hasher itself, and it's possible to finalize again + /// after adding more input. + void final(BLAKE3Result<NumBytes> &Result) { return BLAKE3::final(Result); } + + /// Finalize the hasher and return an output of any length, given in bytes. + /// This doesn't modify the hasher itself, and it's possible to finalize again + /// after adding more input. + BLAKE3Result<NumBytes> final() { return BLAKE3::final<NumBytes>(); } + + /// Return the current output for the digested data since the last call to + /// init(). + /// + /// Other hash functions distinguish between \p result() and \p final(), with + /// \p result() allowing more calls into \p update(), but there's no + // difference for the BLAKE3 hash function. + BLAKE3Result<NumBytes> result() { return BLAKE3::result<NumBytes>(); } +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Base64.h b/contrib/libs/llvm16/include/llvm/Support/Base64.h new file mode 100644 index 00000000000..7a3781249a9 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Base64.h @@ -0,0 +1,72 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- Base64.h - Base64 Encoder/Decoder ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides generic base64 encoder/decoder. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BASE64_H +#define LLVM_SUPPORT_BASE64_H + +#include "llvm/Support/Error.h" +#include <cstdint> +#include <string> +#include <vector> + +namespace llvm { + +template <class InputBytes> std::string encodeBase64(InputBytes const &Bytes) { + static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + std::string Buffer; + Buffer.resize(((Bytes.size() + 2) / 3) * 4); + + size_t i = 0, j = 0; + for (size_t n = Bytes.size() / 3 * 3; i < n; i += 3, j += 4) { + uint32_t x = ((unsigned char)Bytes[i] << 16) | + ((unsigned char)Bytes[i + 1] << 8) | + (unsigned char)Bytes[i + 2]; + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = Table[x & 63]; + } + if (i + 1 == Bytes.size()) { + uint32_t x = ((unsigned char)Bytes[i] << 16); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = '='; + Buffer[j + 3] = '='; + } else if (i + 2 == Bytes.size()) { + uint32_t x = + ((unsigned char)Bytes[i] << 16) | ((unsigned char)Bytes[i + 1] << 8); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = '='; + } + return Buffer; +} + +llvm::Error decodeBase64(llvm::StringRef Input, std::vector<char> &Output); + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryByteStream.h b/contrib/libs/llvm16/include/llvm/Support/BinaryByteStream.h new file mode 100644 index 00000000000..9f256329340 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryByteStream.h @@ -0,0 +1,281 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryByteStream.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// +// A BinaryStream which stores data in a single continguous memory buffer. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYBYTESTREAM_H +#define LLVM_SUPPORT_BINARYBYTESTREAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <cstdint> +#include <cstring> +#include <memory> + +namespace llvm { + +/// An implementation of BinaryStream which holds its entire data set +/// in a single contiguous buffer. BinaryByteStream guarantees that no read +/// operation will ever incur a copy. Note that BinaryByteStream does not +/// own the underlying buffer. +class BinaryByteStream : public BinaryStream { +public: + BinaryByteStream() = default; + BinaryByteStream(ArrayRef<uint8_t> Data, llvm::support::endianness Endian) + : Endian(Endian), Data(Data) {} + BinaryByteStream(StringRef Data, llvm::support::endianness Endian) + : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {} + + llvm::support::endianness getEndian() const override { return Endian; } + + Error readBytes(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffsetForRead(Offset, Size)) + return EC; + Buffer = Data.slice(Offset, Size); + return Error::success(); + } + + Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffsetForRead(Offset, 1)) + return EC; + Buffer = Data.slice(Offset); + return Error::success(); + } + + uint64_t getLength() override { return Data.size(); } + + ArrayRef<uint8_t> data() const { return Data; } + + StringRef str() const { + const char *CharData = reinterpret_cast<const char *>(Data.data()); + return StringRef(CharData, Data.size()); + } + +protected: + llvm::support::endianness Endian; + ArrayRef<uint8_t> Data; +}; + +/// An implementation of BinaryStream whose data is backed by an llvm +/// MemoryBuffer object. MemoryBufferByteStream owns the MemoryBuffer in +/// question. As with BinaryByteStream, reading from a MemoryBufferByteStream +/// will never cause a copy. +class MemoryBufferByteStream : public BinaryByteStream { +public: + MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer, + llvm::support::endianness Endian) + : BinaryByteStream(Buffer->getBuffer(), Endian), + MemBuffer(std::move(Buffer)) {} + + std::unique_ptr<MemoryBuffer> MemBuffer; +}; + +/// An implementation of BinaryStream which holds its entire data set +/// in a single contiguous buffer. As with BinaryByteStream, the mutable +/// version also guarantees that no read operation will ever incur a copy, +/// and similarly it does not own the underlying buffer. +class MutableBinaryByteStream : public WritableBinaryStream { +public: + MutableBinaryByteStream() = default; + MutableBinaryByteStream(MutableArrayRef<uint8_t> Data, + llvm::support::endianness Endian) + : Data(Data), ImmutableStream(Data, Endian) {} + + llvm::support::endianness getEndian() const override { + return ImmutableStream.getEndian(); + } + + Error readBytes(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) override { + return ImmutableStream.readBytes(Offset, Size, Buffer); + } + + Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) override { + return ImmutableStream.readLongestContiguousChunk(Offset, Buffer); + } + + uint64_t getLength() override { return ImmutableStream.getLength(); } + + Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override { + if (Buffer.empty()) + return Error::success(); + + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) + return EC; + + uint8_t *DataPtr = const_cast<uint8_t *>(Data.data()); + ::memcpy(DataPtr + Offset, Buffer.data(), Buffer.size()); + return Error::success(); + } + + Error commit() override { return Error::success(); } + + MutableArrayRef<uint8_t> data() const { return Data; } + +private: + MutableArrayRef<uint8_t> Data; + BinaryByteStream ImmutableStream; +}; + +/// 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(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) + return EC; + + Buffer = ArrayRef(Data).slice(Offset, Size); + return Error::success(); + } + + void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) { + Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end()); + } + + Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) override { + if (auto EC = checkOffsetForWrite(Offset, 1)) + return EC; + + Buffer = ArrayRef(Data).slice(Offset); + return Error::success(); + } + + uint64_t getLength() override { return Data.size(); } + + Error writeBytes(uint64_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); + + uint64_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(); } + + /// Return the properties of this stream. + BinaryStreamFlags getFlags() const override { return BSF_Write | BSF_Append; } + + MutableArrayRef<uint8_t> data() { return Data; } +}; + +/// An implementation of WritableBinaryStream backed by an llvm +/// FileOutputBuffer. +class FileBufferByteStream : public WritableBinaryStream { +private: + class StreamImpl : public MutableBinaryByteStream { + public: + StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer, + llvm::support::endianness Endian) + : MutableBinaryByteStream( + MutableArrayRef<uint8_t>(Buffer->getBufferStart(), + Buffer->getBufferEnd()), + Endian), + FileBuffer(std::move(Buffer)) {} + + Error commit() override { + if (FileBuffer->commit()) + return make_error<BinaryStreamError>( + stream_error_code::filesystem_error); + return Error::success(); + } + + /// Returns a pointer to the start of the buffer. + uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); } + + /// Returns a pointer to the end of the buffer. + uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); } + + private: + std::unique_ptr<FileOutputBuffer> FileBuffer; + }; + +public: + FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer, + llvm::support::endianness Endian) + : Impl(std::move(Buffer), Endian) {} + + llvm::support::endianness getEndian() const override { + return Impl.getEndian(); + } + + Error readBytes(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) override { + return Impl.readBytes(Offset, Size, Buffer); + } + + Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) override { + return Impl.readLongestContiguousChunk(Offset, Buffer); + } + + uint64_t getLength() override { return Impl.getLength(); } + + Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) override { + return Impl.writeBytes(Offset, Data); + } + + Error commit() override { return Impl.commit(); } + + /// Returns a pointer to the start of the buffer. + uint8_t *getBufferStart() const { return Impl.getBufferStart(); } + + /// Returns a pointer to the end of the buffer. + uint8_t *getBufferEnd() const { return Impl.getBufferEnd(); } + +private: + StreamImpl Impl; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_BINARYBYTESTREAM_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryItemStream.h b/contrib/libs/llvm16/include/llvm/Support/BinaryItemStream.h new file mode 100644 index 00000000000..acb3235a806 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryItemStream.h @@ -0,0 +1,117 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryItemStream.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYITEMSTREAM_H +#define LLVM_SUPPORT_BINARYITEMSTREAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/Error.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { + +template <typename T> struct BinaryItemTraits { + static size_t length(const T &Item) = delete; + static ArrayRef<uint8_t> bytes(const T &Item) = delete; +}; + +/// BinaryItemStream represents a sequence of objects stored in some kind of +/// external container but for which it is useful to view as a stream of +/// contiguous bytes. An example of this might be if you have a collection of +/// records and you serialize each one into a buffer, and store these serialized +/// records in a container. The pointers themselves are not laid out +/// contiguously in memory, but we may wish to read from or write to these +/// records as if they were. +template <typename T, typename Traits = BinaryItemTraits<T>> +class BinaryItemStream : public BinaryStream { +public: + explicit BinaryItemStream(llvm::support::endianness Endian) + : Endian(Endian) {} + + llvm::support::endianness getEndian() const override { return Endian; } + + Error readBytes(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) override { + auto ExpectedIndex = translateOffsetIndex(Offset); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + const auto &Item = Items[*ExpectedIndex]; + if (auto EC = checkOffsetForRead(Offset, Size)) + return EC; + if (Size > Traits::length(Item)) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + Buffer = Traits::bytes(Item).take_front(Size); + return Error::success(); + } + + Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) override { + auto ExpectedIndex = translateOffsetIndex(Offset); + if (!ExpectedIndex) + return ExpectedIndex.takeError(); + Buffer = Traits::bytes(Items[*ExpectedIndex]); + return Error::success(); + } + + void setItems(ArrayRef<T> ItemArray) { + Items = ItemArray; + computeItemOffsets(); + } + + uint64_t getLength() override { + return ItemEndOffsets.empty() ? 0 : ItemEndOffsets.back(); + } + +private: + void computeItemOffsets() { + ItemEndOffsets.clear(); + ItemEndOffsets.reserve(Items.size()); + uint64_t CurrentOffset = 0; + for (const auto &Item : Items) { + uint64_t Len = Traits::length(Item); + assert(Len > 0 && "no empty items"); + CurrentOffset += Len; + ItemEndOffsets.push_back(CurrentOffset); + } + } + + Expected<uint32_t> translateOffsetIndex(uint64_t Offset) { + // Make sure the offset is somewhere in our items array. + if (Offset >= getLength()) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + ++Offset; + auto Iter = llvm::lower_bound(ItemEndOffsets, Offset); + size_t Idx = std::distance(ItemEndOffsets.begin(), Iter); + assert(Idx < Items.size() && "binary search for offset failed"); + return Idx; + } + + llvm::support::endianness Endian; + ArrayRef<T> Items; + + // Sorted vector of offsets to accelerate lookup. + std::vector<uint64_t> ItemEndOffsets; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_BINARYITEMSTREAM_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryStream.h b/contrib/libs/llvm16/include/llvm/Support/BinaryStream.h new file mode 100644 index 00000000000..5882290ddd3 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryStream.h @@ -0,0 +1,112 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryStream.h - Base interface for a stream of data -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYSTREAM_H +#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" +#include <cstdint> + +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) +}; + +/// 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 +/// implementation. Since implementations may not necessarily store data in a +/// single contiguous buffer (or even in memory at all), in such cases a it may +/// be necessary for an implementation to cache such a buffer so that it can +/// return it. +class BinaryStream { +public: + virtual ~BinaryStream() = default; + + virtual llvm::support::endianness getEndian() const = 0; + + /// Given an offset into the stream and a number of bytes, attempt to + /// read the bytes and set the output ArrayRef to point to data owned by the + /// stream. + virtual Error readBytes(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) = 0; + + /// Given an offset into the stream, read as much as possible without + /// copying any data. + virtual Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) = 0; + + /// Return the number of bytes of data in this stream. + virtual uint64_t getLength() = 0; + + /// Return the properties of this stream. + virtual BinaryStreamFlags getFlags() const { return BSF_None; } + +protected: + Error checkOffsetForRead(uint64_t Offset, uint64_t DataSize) { + if (Offset > getLength()) + return make_error<BinaryStreamError>(stream_error_code::invalid_offset); + if (getLength() < DataSize + Offset) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + return Error::success(); + } +}; + +/// A BinaryStream which can be read from as well as written to. Note +/// that writing to a BinaryStream always necessitates copying from the input +/// buffer to the stream's backing store. Streams are assumed to be buffered +/// so that to be portable it is necessary to call commit() on the stream when +/// all data has been written. +class WritableBinaryStream : public BinaryStream { +public: + ~WritableBinaryStream() override = default; + + /// Attempt to write the given bytes into the stream at the desired + /// offset. This will always necessitate a copy. Cannot shrink or grow the + /// stream, only writes into existing allocated space. + virtual Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) = 0; + + /// For buffered streams, commits changes to the backing store. + virtual Error commit() = 0; + + /// Return the properties of this stream. + BinaryStreamFlags getFlags() const override { return BSF_Write; } + +protected: + Error checkOffsetForWrite(uint64_t Offset, uint64_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 + +#endif // LLVM_SUPPORT_BINARYSTREAM_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryStreamArray.h b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamArray.h new file mode 100644 index 00000000000..9bde33a81ed --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamArray.h @@ -0,0 +1,385 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryStreamArray.h - Array backed by an arbitrary stream *- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Lightweight arrays that are backed by an arbitrary BinaryStream. This file +/// provides two different array implementations. +/// +/// VarStreamArray - Arrays of variable length records. The user specifies +/// an Extractor type that can extract a record from a given offset and +/// return the number of bytes consumed by the record. +/// +/// FixedStreamArray - Arrays of fixed length records. This is similar in +/// spirit to ArrayRef<T>, but since it is backed by a BinaryStream, the +/// elements of the array need not be laid out in contiguous memory. +/// + +#ifndef LLVM_SUPPORT_BINARYSTREAMARRAY_H +#define LLVM_SUPPORT_BINARYSTREAMARRAY_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/iterator.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/Error.h" +#include <cassert> +#include <cstdint> + +namespace llvm { + +/// VarStreamArrayExtractor is intended to be specialized to provide customized +/// extraction logic. On input it receives a BinaryStreamRef pointing to the +/// beginning of the next record, but where the length of the record is not yet +/// known. Upon completion, it should return an appropriate Error instance if +/// a record could not be extracted, or if one could be extracted it should +/// return success and set Len to the number of bytes this record occupied in +/// the underlying stream, and it should fill out the fields of the value type +/// Item appropriately to represent the current record. +/// +/// You can specialize this template for your own custom value types to avoid +/// having to specify a second template argument to VarStreamArray (documented +/// below). +template <typename T> struct VarStreamArrayExtractor { + // Method intentionally deleted. You must provide an explicit specialization + // with the following method implemented. + Error operator()(BinaryStreamRef Stream, uint32_t &Len, + T &Item) const = delete; +}; + +/// VarStreamArray represents an array of variable length records backed by a +/// stream. This could be a contiguous sequence of bytes in memory, it could +/// be a file on disk, or it could be a PDB stream where bytes are stored as +/// discontiguous blocks in a file. Usually it is desirable to treat arrays +/// as contiguous blocks of memory, but doing so with large PDB files, for +/// example, could mean allocating huge amounts of memory just to allow +/// re-ordering of stream data to be contiguous before iterating over it. By +/// abstracting this out, we need not duplicate this memory, and we can +/// iterate over arrays in arbitrarily formatted streams. Elements are parsed +/// lazily on iteration, so there is no upfront cost associated with building +/// or copying a VarStreamArray, no matter how large it may be. +/// +/// You create a VarStreamArray by specifying a ValueType and an Extractor type. +/// If you do not specify an Extractor type, you are expected to specialize +/// VarStreamArrayExtractor<T> for your ValueType. +/// +/// By default an Extractor is default constructed in the class, but in some +/// cases you might find it useful for an Extractor to maintain state across +/// extractions. In this case you can provide your own Extractor through a +/// secondary constructor. The following examples show various ways of +/// creating a VarStreamArray. +/// +/// // Will use VarStreamArrayExtractor<MyType> as the extractor. +/// VarStreamArray<MyType> MyTypeArray; +/// +/// // Will use a default-constructed MyExtractor as the extractor. +/// VarStreamArray<MyType, MyExtractor> MyTypeArray2; +/// +/// // Will use the specific instance of MyExtractor provided. +/// // MyExtractor need not be default-constructible in this case. +/// MyExtractor E(SomeContext); +/// VarStreamArray<MyType, MyExtractor> MyTypeArray3(E); +/// + +template <typename ValueType, typename Extractor> class VarStreamArrayIterator; + +template <typename ValueType, + typename Extractor = VarStreamArrayExtractor<ValueType>> +class VarStreamArray { + friend class VarStreamArrayIterator<ValueType, Extractor>; + +public: + typedef VarStreamArrayIterator<ValueType, Extractor> Iterator; + + VarStreamArray() = default; + + explicit VarStreamArray(const Extractor &E) : E(E) {} + + explicit VarStreamArray(BinaryStreamRef Stream, uint32_t Skew = 0) + : Stream(Stream), Skew(Skew) {} + + VarStreamArray(BinaryStreamRef Stream, const Extractor &E, uint32_t Skew = 0) + : Stream(Stream), E(E), Skew(Skew) {} + + Iterator begin(bool *HadError = nullptr) const { + return Iterator(*this, E, Skew, nullptr); + } + + bool valid() const { return Stream.valid(); } + + bool isOffsetValid(uint32_t Offset) const { return at(Offset) != end(); } + + uint32_t skew() const { return Skew; } + Iterator end() const { return Iterator(E); } + + bool empty() const { return Stream.getLength() == 0; } + + VarStreamArray<ValueType, Extractor> substream(uint32_t Begin, + uint32_t End) const { + assert(Begin >= Skew); + // We should never cut off the beginning of the stream since it might be + // skewed, meaning the initial bytes are important. + BinaryStreamRef NewStream = Stream.slice(0, End); + return {NewStream, E, Begin}; + } + + /// given an offset into the array's underlying stream, return an + /// iterator to the record at that offset. This is considered unsafe + /// since the behavior is undefined if \p Offset does not refer to the + /// beginning of a valid record. + Iterator at(uint32_t Offset) const { + return Iterator(*this, E, Offset, nullptr); + } + + const Extractor &getExtractor() const { return E; } + Extractor &getExtractor() { return E; } + + BinaryStreamRef getUnderlyingStream() const { return Stream; } + void setUnderlyingStream(BinaryStreamRef NewStream, uint32_t NewSkew = 0) { + Stream = NewStream; + Skew = NewSkew; + } + + void drop_front() { Skew += begin()->length(); } + +private: + BinaryStreamRef Stream; + Extractor E; + uint32_t Skew = 0; +}; + +template <typename ValueType, typename Extractor> +class VarStreamArrayIterator + : public iterator_facade_base<VarStreamArrayIterator<ValueType, Extractor>, + std::forward_iterator_tag, const ValueType> { + typedef VarStreamArrayIterator<ValueType, Extractor> IterType; + typedef VarStreamArray<ValueType, Extractor> ArrayType; + +public: + VarStreamArrayIterator(const ArrayType &Array, const Extractor &E, + uint32_t Offset, bool *HadError) + : IterRef(Array.Stream.drop_front(Offset)), Extract(E), + Array(&Array), AbsOffset(Offset), HadError(HadError) { + if (IterRef.getLength() == 0) + moveToEnd(); + else { + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } + } + } + + VarStreamArrayIterator() = default; + explicit VarStreamArrayIterator(const Extractor &E) : Extract(E) {} + ~VarStreamArrayIterator() = default; + + bool operator==(const IterType &R) const { + if (Array && R.Array) { + // Both have a valid array, make sure they're same. + assert(Array == R.Array); + return IterRef == R.IterRef; + } + + // Both iterators are at the end. + if (!Array && !R.Array) + return true; + + // One is not at the end and one is. + return false; + } + + const ValueType &operator*() const { + assert(Array && !HasError); + return ThisValue; + } + + IterType &operator+=(unsigned N) { + for (unsigned I = 0; I < N; ++I) { + // We are done with the current record, discard it so that we are + // positioned at the next record. + AbsOffset += ThisLen; + IterRef = IterRef.drop_front(ThisLen); + if (IterRef.getLength() == 0) { + // There is nothing after the current record, we must make this an end + // iterator. + moveToEnd(); + } else { + // There is some data after the current record. + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } else if (ThisLen == 0) { + // An empty record? Make this an end iterator. + moveToEnd(); + } + } + } + return *this; + } + + uint32_t offset() const { return AbsOffset; } + uint32_t getRecordLength() const { return ThisLen; } + +private: + void moveToEnd() { + Array = nullptr; + ThisLen = 0; + } + void markError() { + moveToEnd(); + HasError = true; + if (HadError != nullptr) + *HadError = true; + } + + ValueType ThisValue; + BinaryStreamRef IterRef; + Extractor Extract; + const ArrayType *Array{nullptr}; + uint32_t ThisLen{0}; + uint32_t AbsOffset{0}; + bool HasError{false}; + bool *HadError{nullptr}; +}; + +template <typename T> class FixedStreamArrayIterator; + +/// FixedStreamArray is similar to VarStreamArray, except with each record +/// having a fixed-length. As with VarStreamArray, there is no upfront +/// cost associated with building or copying a FixedStreamArray, as the +/// memory for each element is not read from the backing stream until that +/// element is iterated. +template <typename T> class FixedStreamArray { + friend class FixedStreamArrayIterator<T>; + +public: + typedef FixedStreamArrayIterator<T> Iterator; + + FixedStreamArray() = default; + explicit FixedStreamArray(BinaryStreamRef Stream) : Stream(Stream) { + assert(Stream.getLength() % sizeof(T) == 0); + } + + bool operator==(const FixedStreamArray<T> &Other) const { + return Stream == Other.Stream; + } + + bool operator!=(const FixedStreamArray<T> &Other) const { + return !(*this == Other); + } + + FixedStreamArray(const FixedStreamArray &) = default; + FixedStreamArray &operator=(const FixedStreamArray &) = default; + + const T &operator[](uint32_t Index) const { + assert(Index < size()); + uint32_t Off = Index * sizeof(T); + ArrayRef<uint8_t> Data; + if (auto EC = Stream.readBytes(Off, sizeof(T), Data)) { + assert(false && "Unexpected failure reading from stream"); + // This should never happen since we asserted that the stream length was + // an exact multiple of the element size. + consumeError(std::move(EC)); + } + assert(isAddrAligned(Align::Of<T>(), Data.data())); + return *reinterpret_cast<const T *>(Data.data()); + } + + uint32_t size() const { return Stream.getLength() / sizeof(T); } + + bool empty() const { return size() == 0; } + + FixedStreamArrayIterator<T> begin() const { + return FixedStreamArrayIterator<T>(*this, 0); + } + + FixedStreamArrayIterator<T> end() const { + return FixedStreamArrayIterator<T>(*this, size()); + } + + const T &front() const { return *begin(); } + const T &back() const { + FixedStreamArrayIterator<T> I = end(); + return *(--I); + } + + BinaryStreamRef getUnderlyingStream() const { return Stream; } + +private: + BinaryStreamRef Stream; +}; + +template <typename T> +class FixedStreamArrayIterator + : public iterator_facade_base<FixedStreamArrayIterator<T>, + std::random_access_iterator_tag, const T> { + +public: + FixedStreamArrayIterator(const FixedStreamArray<T> &Array, uint32_t Index) + : Array(Array), Index(Index) {} + + FixedStreamArrayIterator(const FixedStreamArrayIterator<T> &Other) + : Array(Other.Array), Index(Other.Index) {} + FixedStreamArrayIterator<T> & + operator=(const FixedStreamArrayIterator<T> &Other) { + Array = Other.Array; + Index = Other.Index; + return *this; + } + + const T &operator*() const { return Array[Index]; } + const T &operator*() { return Array[Index]; } + + bool operator==(const FixedStreamArrayIterator<T> &R) const { + assert(Array == R.Array); + return (Index == R.Index) && (Array == R.Array); + } + + FixedStreamArrayIterator<T> &operator+=(std::ptrdiff_t N) { + Index += N; + return *this; + } + + FixedStreamArrayIterator<T> &operator-=(std::ptrdiff_t N) { + assert(std::ptrdiff_t(Index) >= N); + Index -= N; + return *this; + } + + std::ptrdiff_t operator-(const FixedStreamArrayIterator<T> &R) const { + assert(Array == R.Array); + assert(Index >= R.Index); + return Index - R.Index; + } + + bool operator<(const FixedStreamArrayIterator<T> &RHS) const { + assert(Array == RHS.Array); + return Index < RHS.Index; + } + +private: + FixedStreamArray<T> Array; + uint32_t Index; +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_BINARYSTREAMARRAY_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryStreamError.h b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamError.h new file mode 100644 index 00000000000..73d9cadf930 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamError.h @@ -0,0 +1,58 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryStreamError.h - Error extensions for Binary Streams *- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYSTREAMERROR_H +#define LLVM_SUPPORT_BINARYSTREAMERROR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +enum class stream_error_code { + unspecified, + stream_too_short, + invalid_array_size, + invalid_offset, + filesystem_error +}; + +/// Base class for errors originating when parsing raw PDB files +class BinaryStreamError : public ErrorInfo<BinaryStreamError> { +public: + static char ID; + explicit BinaryStreamError(stream_error_code C); + explicit BinaryStreamError(StringRef Context); + BinaryStreamError(stream_error_code C, StringRef Context); + + void log(raw_ostream &OS) const override; + std::error_code convertToErrorCode() const override; + + StringRef getErrorMessage() const; + + stream_error_code getErrorCode() const { return Code; } + +private: + std::string ErrMsg; + stream_error_code Code; +}; +} // namespace llvm + +#endif // LLVM_SUPPORT_BINARYSTREAMERROR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryStreamReader.h b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamReader.h new file mode 100644 index 00000000000..24d9198a99f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamReader.h @@ -0,0 +1,286 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryStreamReader.h - Reads objects from a binary stream *- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYSTREAMREADER_H +#define LLVM_SUPPORT_BINARYSTREAMREADER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <type_traits> + +namespace llvm { + +/// Provides read only access to a subclass of `BinaryStream`. Provides +/// bounds checking and helpers for writing certain common data types such as +/// null-terminated strings, integers in various flavors of endianness, etc. +/// Can be subclassed to provide reading of custom datatypes, although no +/// are overridable. +class BinaryStreamReader { +public: + BinaryStreamReader() = default; + explicit BinaryStreamReader(BinaryStreamRef Ref); + explicit BinaryStreamReader(BinaryStream &Stream); + explicit BinaryStreamReader(ArrayRef<uint8_t> Data, + llvm::support::endianness Endian); + explicit BinaryStreamReader(StringRef Data, llvm::support::endianness Endian); + + BinaryStreamReader(const BinaryStreamReader &Other) = default; + + BinaryStreamReader &operator=(const BinaryStreamReader &Other) = default; + + virtual ~BinaryStreamReader() = default; + + /// Read as much as possible from the underlying string at the current offset + /// without invoking a copy, and set \p Buffer to the resulting data slice. + /// Updates the stream's offset to point after the newly read data. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer); + + /// Read \p Size bytes from the underlying stream at the current offset and + /// and set \p Buffer to the resulting data slice. Whether a copy occurs + /// depends on the implementation of the underlying stream. Updates the + /// stream's offset to point after the newly read data. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size); + + /// Read an integer of the specified endianness into \p Dest and update the + /// stream's offset. The data is always copied from the stream's underlying + /// buffer into \p Dest. Updates the stream's offset to point after the newly + /// read data. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + template <typename T> Error readInteger(T &Dest) { + static_assert(std::is_integral_v<T>, + "Cannot call readInteger with non-integral value!"); + + ArrayRef<uint8_t> Bytes; + if (auto EC = readBytes(Bytes, sizeof(T))) + return EC; + + Dest = llvm::support::endian::read<T, llvm::support::unaligned>( + Bytes.data(), Stream.getEndian()); + return Error::success(); + } + + /// Similar to readInteger. + template <typename T> Error readEnum(T &Dest) { + static_assert(std::is_enum<T>::value, + "Cannot call readEnum with non-enum value!"); + std::underlying_type_t<T> N; + if (auto EC = readInteger(N)) + return EC; + Dest = static_cast<T>(N); + return Error::success(); + } + + /// Read an unsigned LEB128 encoded value. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readULEB128(uint64_t &Dest); + + /// Read a signed LEB128 encoded value. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readSLEB128(int64_t &Dest); + + /// Read a null terminated string from \p Dest. Whether a copy occurs depends + /// on the implementation of the underlying stream. Updates the stream's + /// offset to point after the newly read data. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readCString(StringRef &Dest); + + /// Similar to readCString, however read a null-terminated UTF16 string + /// instead. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readWideString(ArrayRef<UTF16> &Dest); + + /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends + /// on the implementation of the underlying stream. Updates the stream's + /// offset to point after the newly read data. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readFixedString(StringRef &Dest, uint32_t Length); + + /// Read the entire remainder of the underlying stream into \p Ref. This is + /// equivalent to calling getUnderlyingStream().slice(Offset). Updates the + /// stream's offset to point to the end of the stream. Never causes a copy. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readStreamRef(BinaryStreamRef &Ref); + + /// Read \p Length bytes from the underlying stream into \p Ref. This is + /// equivalent to calling getUnderlyingStream().slice(Offset, Length). + /// Updates the stream's offset to point after the newly read object. Never + /// causes a copy. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readStreamRef(BinaryStreamRef &Ref, uint32_t Length); + + /// Read \p Length bytes from the underlying stream into \p Ref. This is + /// equivalent to calling getUnderlyingStream().slice(Offset, Length). + /// Updates the stream's offset to point after the newly read object. Never + /// causes a copy. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + Error readSubstream(BinarySubstreamRef &Ref, uint32_t Length); + + /// Get a pointer to an object of type T from the underlying stream, as if by + /// memcpy, and store the result into \p Dest. It is up to the caller to + /// ensure that objects of type T can be safely treated in this manner. + /// Updates the stream's offset to point after the newly read object. Whether + /// a copy occurs depends upon the implementation of the underlying + /// stream. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + template <typename T> Error readObject(const T *&Dest) { + ArrayRef<uint8_t> Buffer; + if (auto EC = readBytes(Buffer, sizeof(T))) + return EC; + Dest = reinterpret_cast<const T *>(Buffer.data()); + return Error::success(); + } + + /// Get a reference to a \p NumElements element array of objects of type T + /// from the underlying stream as if by memcpy, and store the resulting array + /// slice into \p array. It is up to the caller to ensure that objects of + /// type T can be safely treated in this manner. Updates the stream's offset + /// to point after the newly read object. Whether a copy occurs depends upon + /// the implementation of the underlying stream. + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + template <typename T> + Error readArray(ArrayRef<T> &Array, uint32_t NumElements) { + ArrayRef<uint8_t> Bytes; + if (NumElements == 0) { + Array = ArrayRef<T>(); + return Error::success(); + } + + if (NumElements > UINT32_MAX / sizeof(T)) + return make_error<BinaryStreamError>( + stream_error_code::invalid_array_size); + + if (auto EC = readBytes(Bytes, NumElements * sizeof(T))) + return EC; + + assert(isAddrAligned(Align::Of<T>(), Bytes.data()) && + "Reading at invalid alignment!"); + + Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements); + return Error::success(); + } + + /// Read a VarStreamArray of size \p Size bytes and store the result into + /// \p Array. Updates the stream's offset to point after the newly read + /// array. Never causes a copy (although iterating the elements of the + /// VarStreamArray may, depending upon the implementation of the underlying + /// stream). + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + template <typename T, typename U> + Error readArray(VarStreamArray<T, U> &Array, uint32_t Size, + uint32_t Skew = 0) { + BinaryStreamRef S; + if (auto EC = readStreamRef(S, Size)) + return EC; + Array.setUnderlyingStream(S, Skew); + return Error::success(); + } + + /// Read a FixedStreamArray of \p NumItems elements and store the result into + /// \p Array. Updates the stream's offset to point after the newly read + /// array. Never causes a copy (although iterating the elements of the + /// FixedStreamArray may, depending upon the implementation of the underlying + /// stream). + /// + /// \returns a success error code if the data was successfully read, otherwise + /// returns an appropriate error code. + template <typename T> + Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) { + if (NumItems == 0) { + Array = FixedStreamArray<T>(); + return Error::success(); + } + + if (NumItems > UINT32_MAX / sizeof(T)) + return make_error<BinaryStreamError>( + stream_error_code::invalid_array_size); + + BinaryStreamRef View; + if (auto EC = readStreamRef(View, NumItems * sizeof(T))) + return EC; + + Array = FixedStreamArray<T>(View); + return Error::success(); + } + + bool empty() const { return bytesRemaining() == 0; } + void setOffset(uint64_t Off) { Offset = Off; } + uint64_t getOffset() const { return Offset; } + uint64_t getLength() const { return Stream.getLength(); } + uint64_t bytesRemaining() const { return getLength() - getOffset(); } + + /// Advance the stream's offset by \p Amount bytes. + /// + /// \returns a success error code if at least \p Amount bytes remain in the + /// stream, otherwise returns an appropriate error code. + Error skip(uint64_t Amount); + + /// Examine the next byte of the underlying stream without advancing the + /// stream's offset. If the stream is empty the behavior is undefined. + /// + /// \returns the next byte in the stream. + uint8_t peek() const; + + Error padToAlignment(uint32_t Align); + + std::pair<BinaryStreamReader, BinaryStreamReader> + split(uint64_t Offset) const; + +private: + BinaryStreamRef Stream; + uint64_t Offset = 0; +}; +} // namespace llvm + +#endif // LLVM_SUPPORT_BINARYSTREAMREADER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryStreamRef.h b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamRef.h new file mode 100644 index 00000000000..4f3e17baa0b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamRef.h @@ -0,0 +1,283 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryStreamRef.h - A copyable reference to a stream -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYSTREAMREF_H +#define LLVM_SUPPORT_BINARYSTREAMREF_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/BinaryStream.h" +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/Error.h" +#include <cstdint> +#include <memory> +#include <optional> + +namespace llvm { + +/// Common stuff for mutable and immutable StreamRefs. +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, uint64_t Offset, + std::optional<uint64_t> Length) + : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()), + ViewOffset(Offset), Length(Length) {} + BinaryStreamRefBase(StreamType &BorrowedImpl, uint64_t Offset, + std::optional<uint64_t> Length) + : BorrowedImpl(&BorrowedImpl), ViewOffset(Offset), Length(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(); + } + + uint64_t getLength() const { + if (Length) + return *Length; + + 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(uint64_t N) const { + if (!BorrowedImpl) + return RefType(); + + N = std::min(N, getLength()); + RefType Result(static_cast<const RefType &>(*this)); + if (N == 0) + return Result; + + Result.ViewOffset += N; + if (Result.Length) + *Result.Length -= N; + return Result; + } + + /// 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(uint64_t N) const { + if (!BorrowedImpl) + return RefType(); + + RefType Result(static_cast<const RefType &>(*this)); + 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) + Result.Length = getLength(); + + *Result.Length -= N; + return Result; + } + + /// Return a new BinaryStreamRef with only the first \p N elements remaining. + RefType keep_front(uint64_t N) const { + assert(N <= getLength()); + return drop_back(getLength() - N); + } + + /// Return a new BinaryStreamRef with only the last \p N elements remaining. + RefType keep_back(uint64_t N) const { + assert(N <= getLength()); + return drop_front(getLength() - N); + } + + /// Return a new BinaryStreamRef with the first and last \p N elements + /// removed. + RefType drop_symmetric(uint64_t N) const { + return drop_front(N).drop_back(N); + } + + /// Return a new BinaryStreamRef with the first \p Offset elements removed, + /// and retaining exactly \p Len elements. + RefType slice(uint64_t Offset, uint64_t Len) const { + return drop_front(Offset).keep_front(Len); + } + + bool valid() const { return BorrowedImpl != nullptr; } + + friend bool operator==(const RefType &LHS, const RefType &RHS) { + if (LHS.BorrowedImpl != RHS.BorrowedImpl) + return false; + if (LHS.ViewOffset != RHS.ViewOffset) + return false; + if (LHS.Length != RHS.Length) + return false; + return true; + } + +protected: + Error checkOffsetForRead(uint64_t Offset, uint64_t DataSize) const { + if (Offset > getLength()) + return make_error<BinaryStreamError>(stream_error_code::invalid_offset); + if (getLength() < DataSize + Offset) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + return Error::success(); + } + + std::shared_ptr<StreamType> SharedImpl; + StreamType *BorrowedImpl = nullptr; + uint64_t ViewOffset = 0; + std::optional<uint64_t> Length; +}; + +/// BinaryStreamRef is to BinaryStream what ArrayRef is to an Array. It +/// provides copy-semantics and read only access to a "window" of the underlying +/// BinaryStream. Note that BinaryStreamRef is *not* a BinaryStream. That is to +/// say, it does not inherit and override the methods of BinaryStream. In +/// general, you should not pass around pointers or references to BinaryStreams +/// and use inheritance to achieve polymorphism. Instead, you should pass +/// around BinaryStreamRefs by value and achieve polymorphism that way. +class BinaryStreamRef + : public BinaryStreamRefBase<BinaryStreamRef, BinaryStream> { + friend BinaryStreamRefBase<BinaryStreamRef, BinaryStream>; + friend class WritableBinaryStreamRef; + BinaryStreamRef(std::shared_ptr<BinaryStream> Impl, uint64_t ViewOffset, + std::optional<uint64_t> Length) + : BinaryStreamRefBase(Impl, ViewOffset, Length) {} + +public: + BinaryStreamRef() = default; + BinaryStreamRef(BinaryStream &Stream); + BinaryStreamRef(BinaryStream &Stream, uint64_t Offset, + std::optional<uint64_t> Length); + explicit BinaryStreamRef(ArrayRef<uint8_t> Data, + llvm::support::endianness Endian); + explicit BinaryStreamRef(StringRef Data, llvm::support::endianness Endian); + + 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, uint64_t Offset, + uint64_t Length) = delete; + + /// Given an Offset into this StreamRef and a Size, return a reference to a + /// buffer owned by the stream. + /// + /// \returns a success error code if the entire range of data is within the + /// bounds of this BinaryStreamRef's view and the implementation could read + /// the data, and an appropriate error code otherwise. + Error readBytes(uint64_t Offset, uint64_t Size, + ArrayRef<uint8_t> &Buffer) const; + + /// Given an Offset into this BinaryStreamRef, return a reference to the + /// largest buffer the stream could support without necessitating a copy. + /// + /// \returns a success error code if implementation could read the data, + /// and an appropriate error code otherwise. + Error readLongestContiguousChunk(uint64_t Offset, + ArrayRef<uint8_t> &Buffer) const; +}; + +struct BinarySubstreamRef { + uint64_t Offset = 0; // Offset in the parent stream + BinaryStreamRef StreamData; // Stream Data + + BinarySubstreamRef slice(uint64_t Off, uint64_t Size) const { + BinaryStreamRef SubSub = StreamData.slice(Off, Size); + return {Off + Offset, SubSub}; + } + BinarySubstreamRef drop_front(uint64_t N) const { + return slice(N, size() - N); + } + BinarySubstreamRef keep_front(uint64_t N) const { return slice(0, N); } + + std::pair<BinarySubstreamRef, BinarySubstreamRef> split(uint64_t Off) const { + return std::make_pair(keep_front(Off), drop_front(Off)); + } + + uint64_t size() const { return StreamData.getLength(); } + bool empty() const { return size() == 0; } +}; + +class WritableBinaryStreamRef + : public BinaryStreamRefBase<WritableBinaryStreamRef, + WritableBinaryStream> { + friend BinaryStreamRefBase<WritableBinaryStreamRef, WritableBinaryStream>; + WritableBinaryStreamRef(std::shared_ptr<WritableBinaryStream> Impl, + uint64_t ViewOffset, std::optional<uint64_t> Length) + : BinaryStreamRefBase(Impl, ViewOffset, Length) {} + + Error checkOffsetForWrite(uint64_t Offset, uint64_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, uint64_t Offset, + std::optional<uint64_t> Length); + explicit WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data, + llvm::support::endianness Endian); + 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, uint64_t Offset, + uint64_t Length) = delete; + + /// Given an Offset into this WritableBinaryStreamRef and some input data, + /// writes the data to the underlying stream. + /// + /// \returns a success error code if the data could fit within the underlying + /// stream at the specified location and the implementation could write the + /// data, and an appropriate error code otherwise. + Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) const; + + /// Conver this WritableBinaryStreamRef to a read-only BinaryStreamRef. + operator BinaryStreamRef() const; + + /// For buffered streams, commits changes to the backing store. + Error commit(); +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_BINARYSTREAMREF_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BinaryStreamWriter.h b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamWriter.h new file mode 100644 index 00000000000..9b934b2f391 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BinaryStreamWriter.h @@ -0,0 +1,201 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BinaryStreamWriter.h - Writes objects to a BinaryStream ---*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BINARYSTREAMWRITER_H +#define LLVM_SUPPORT_BINARYSTREAMWRITER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/BinaryStreamArray.h" +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <cstdint> +#include <type_traits> +#include <utility> + +namespace llvm { + +/// Provides write only access to a subclass of `WritableBinaryStream`. +/// Provides bounds checking and helpers for writing certain common data types +/// such as null-terminated strings, integers in various flavors of endianness, +/// etc. Can be subclassed to provide reading and writing of custom datatypes, +/// although no methods are overridable. +class BinaryStreamWriter { +public: + BinaryStreamWriter() = default; + explicit BinaryStreamWriter(WritableBinaryStreamRef Ref); + explicit BinaryStreamWriter(WritableBinaryStream &Stream); + explicit BinaryStreamWriter(MutableArrayRef<uint8_t> Data, + llvm::support::endianness Endian); + + BinaryStreamWriter(const BinaryStreamWriter &Other) = default; + + BinaryStreamWriter &operator=(const BinaryStreamWriter &Other) = default; + + virtual ~BinaryStreamWriter() = default; + + /// Write the bytes specified in \p Buffer to the underlying stream. + /// On success, updates the offset so that subsequent writes will occur + /// at the next unwritten position. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeBytes(ArrayRef<uint8_t> Buffer); + + /// Write the integer \p Value to the underlying stream in the + /// specified endianness. On success, updates the offset so that + /// subsequent writes occur at the next unwritten position. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + template <typename T> Error writeInteger(T Value) { + static_assert(std::is_integral_v<T>, + "Cannot call writeInteger with non-integral value!"); + uint8_t Buffer[sizeof(T)]; + llvm::support::endian::write<T, llvm::support::unaligned>( + Buffer, Value, Stream.getEndian()); + return writeBytes(Buffer); + } + + /// Similar to writeInteger + template <typename T> Error writeEnum(T Num) { + static_assert(std::is_enum<T>::value, + "Cannot call writeEnum with non-Enum type"); + + using U = std::underlying_type_t<T>; + return writeInteger<U>(static_cast<U>(Num)); + } + + /// Write the unsigned integer Value to the underlying stream using ULEB128 + /// encoding. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeULEB128(uint64_t Value); + + /// Write the unsigned integer Value to the underlying stream using ULEB128 + /// encoding. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeSLEB128(int64_t Value); + + /// Write the string \p Str to the underlying stream followed by a null + /// terminator. On success, updates the offset so that subsequent writes + /// occur at the next unwritten position. \p Str need not be null terminated + /// on input. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeCString(StringRef Str); + + /// Write the string \p Str to the underlying stream without a null + /// terminator. On success, updates the offset so that subsequent writes + /// occur at the next unwritten position. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeFixedString(StringRef Str); + + /// Efficiently reads all data from \p Ref, and writes it to this stream. + /// This operation will not invoke any copies of the source data, regardless + /// of the source stream's implementation. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeStreamRef(BinaryStreamRef Ref); + + /// Efficiently reads \p Size bytes from \p Ref, and writes it to this stream. + /// This operation will not invoke any copies of the source data, regardless + /// of the source stream's implementation. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + Error writeStreamRef(BinaryStreamRef Ref, uint64_t Size); + + /// Writes the object \p Obj to the underlying stream, as if by using memcpy. + /// It is up to the caller to ensure that type of \p Obj can be safely copied + /// in this fashion, as no checks are made to ensure that this is safe. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + template <typename T> Error writeObject(const T &Obj) { + static_assert(!std::is_pointer<T>::value, + "writeObject should not be used with pointers, to write " + "the pointed-to value dereference the pointer before calling " + "writeObject"); + return writeBytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&Obj), sizeof(T))); + } + + /// Writes an array of objects of type T to the underlying stream, as if by + /// using memcpy. It is up to the caller to ensure that type of \p Obj can + /// be safely copied in this fashion, as no checks are made to ensure that + /// this is safe. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + template <typename T> Error writeArray(ArrayRef<T> Array) { + if (Array.empty()) + return Error::success(); + if (Array.size() > UINT32_MAX / sizeof(T)) + return make_error<BinaryStreamError>( + stream_error_code::invalid_array_size); + + return writeBytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Array.data()), + Array.size() * sizeof(T))); + } + + /// Writes all data from the array \p Array to the underlying stream. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + template <typename T, typename U> + Error writeArray(VarStreamArray<T, U> Array) { + return writeStreamRef(Array.getUnderlyingStream()); + } + + /// Writes all elements from the array \p Array to the underlying stream. + /// + /// \returns a success error code if the data was successfully written, + /// otherwise returns an appropriate error code. + template <typename T> Error writeArray(FixedStreamArray<T> Array) { + return writeStreamRef(Array.getUnderlyingStream()); + } + + /// Splits the Writer into two Writers at a given offset. + std::pair<BinaryStreamWriter, BinaryStreamWriter> split(uint64_t Off) const; + + void setOffset(uint64_t Off) { Offset = Off; } + uint64_t getOffset() const { return Offset; } + uint64_t getLength() const { return Stream.getLength(); } + uint64_t bytesRemaining() const { return getLength() - getOffset(); } + Error padToAlignment(uint32_t Align); + +protected: + WritableBinaryStreamRef Stream; + uint64_t Offset = 0; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_BINARYSTREAMWRITER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BlockFrequency.h b/contrib/libs/llvm16/include/llvm/Support/BlockFrequency.h new file mode 100644 index 00000000000..9c2a3a4113e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BlockFrequency.h @@ -0,0 +1,91 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-------- BlockFrequency.h - Block Frequency Wrapper --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements Block Frequency class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BLOCKFREQUENCY_H +#define LLVM_SUPPORT_BLOCKFREQUENCY_H + +#include <cstdint> + +namespace llvm { + +class BranchProbability; + +// This class represents Block Frequency as a 64-bit value. +class BlockFrequency { + uint64_t Frequency; + +public: + BlockFrequency(uint64_t Freq = 0) : Frequency(Freq) { } + + /// Returns the maximum possible frequency, the saturation value. + static uint64_t getMaxFrequency() { return -1ULL; } + + /// Returns the frequency as a fixpoint number scaled by the entry + /// frequency. + uint64_t getFrequency() const { return Frequency; } + + /// Multiplies with a branch probability. The computation will never + /// overflow. + BlockFrequency &operator*=(BranchProbability Prob); + BlockFrequency operator*(BranchProbability Prob) const; + + /// Divide by a non-zero branch probability using saturating + /// arithmetic. + BlockFrequency &operator/=(BranchProbability Prob); + BlockFrequency operator/(BranchProbability Prob) const; + + /// Adds another block frequency using saturating arithmetic. + BlockFrequency &operator+=(BlockFrequency Freq); + BlockFrequency operator+(BlockFrequency Freq) const; + + /// Subtracts another block frequency using saturating arithmetic. + BlockFrequency &operator-=(BlockFrequency Freq); + BlockFrequency operator-(BlockFrequency Freq) const; + + /// Shift block frequency to the right by count digits saturating to 1. + BlockFrequency &operator>>=(const unsigned count); + + bool operator<(BlockFrequency RHS) const { + return Frequency < RHS.Frequency; + } + + bool operator<=(BlockFrequency RHS) const { + return Frequency <= RHS.Frequency; + } + + bool operator>(BlockFrequency RHS) const { + return Frequency > RHS.Frequency; + } + + bool operator>=(BlockFrequency RHS) const { + return Frequency >= RHS.Frequency; + } + + bool operator==(BlockFrequency RHS) const { + return Frequency == RHS.Frequency; + } +}; + +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BranchProbability.h b/contrib/libs/llvm16/include/llvm/Support/BranchProbability.h new file mode 100644 index 00000000000..b05b2be3c0e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BranchProbability.h @@ -0,0 +1,259 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- BranchProbability.h - Branch Probability Wrapper ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Definition of BranchProbability shared by IR and Machine Instructions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BRANCHPROBABILITY_H +#define LLVM_SUPPORT_BRANCHPROBABILITY_H + +#include "llvm/Support/DataTypes.h" +#include <algorithm> +#include <cassert> +#include <iterator> +#include <numeric> + +namespace llvm { + +class raw_ostream; + +// This class represents Branch Probability as a non-negative fraction that is +// no greater than 1. It uses a fixed-point-like implementation, in which the +// denominator is always a constant value (here we use 1<<31 for maximum +// precision). +class BranchProbability { + // Numerator + uint32_t N; + + // Denominator, which is a constant value. + static constexpr uint32_t D = 1u << 31; + static constexpr uint32_t UnknownN = UINT32_MAX; + + // Construct a BranchProbability with only numerator assuming the denominator + // is 1<<31. For internal use only. + explicit BranchProbability(uint32_t n) : N(n) {} + +public: + BranchProbability() : N(UnknownN) {} + BranchProbability(uint32_t Numerator, uint32_t Denominator); + + bool isZero() const { return N == 0; } + bool isUnknown() const { return N == UnknownN; } + + static BranchProbability getZero() { return BranchProbability(0); } + static BranchProbability getOne() { return BranchProbability(D); } + static BranchProbability getUnknown() { return BranchProbability(UnknownN); } + // Create a BranchProbability object with the given numerator and 1<<31 + // as denominator. + static BranchProbability getRaw(uint32_t N) { return BranchProbability(N); } + // Create a BranchProbability object from 64-bit integers. + static BranchProbability getBranchProbability(uint64_t Numerator, + uint64_t Denominator); + + // Normalize given probabilties so that the sum of them becomes approximate + // one. + template <class ProbabilityIter> + static void normalizeProbabilities(ProbabilityIter Begin, + ProbabilityIter End); + + uint32_t getNumerator() const { return N; } + static uint32_t getDenominator() { return D; } + + // Return (1 - Probability). + BranchProbability getCompl() const { return BranchProbability(D - N); } + + raw_ostream &print(raw_ostream &OS) const; + + void dump() const; + + /// Scale a large integer. + /// + /// Scales \c Num. Guarantees full precision. Returns the floor of the + /// result. + /// + /// \return \c Num times \c this. + uint64_t scale(uint64_t Num) const; + + /// Scale a large integer by the inverse. + /// + /// Scales \c Num by the inverse of \c this. Guarantees full precision. + /// Returns the floor of the result. + /// + /// \return \c Num divided by \c this. + uint64_t scaleByInverse(uint64_t Num) const; + + BranchProbability &operator+=(BranchProbability RHS) { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in arithmetics."); + // Saturate the result in case of overflow. + N = (uint64_t(N) + RHS.N > D) ? D : N + RHS.N; + return *this; + } + + BranchProbability &operator-=(BranchProbability RHS) { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in arithmetics."); + // Saturate the result in case of underflow. + N = N < RHS.N ? 0 : N - RHS.N; + return *this; + } + + BranchProbability &operator*=(BranchProbability RHS) { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in arithmetics."); + N = (static_cast<uint64_t>(N) * RHS.N + D / 2) / D; + return *this; + } + + BranchProbability &operator*=(uint32_t RHS) { + assert(N != UnknownN && + "Unknown probability cannot participate in arithmetics."); + N = (uint64_t(N) * RHS > D) ? D : N * RHS; + return *this; + } + + BranchProbability &operator/=(BranchProbability RHS) { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in arithmetics."); + N = (static_cast<uint64_t>(N) * D + RHS.N / 2) / RHS.N; + return *this; + } + + BranchProbability &operator/=(uint32_t RHS) { + assert(N != UnknownN && + "Unknown probability cannot participate in arithmetics."); + assert(RHS > 0 && "The divider cannot be zero."); + N /= RHS; + return *this; + } + + BranchProbability operator+(BranchProbability RHS) const { + BranchProbability Prob(*this); + Prob += RHS; + return Prob; + } + + BranchProbability operator-(BranchProbability RHS) const { + BranchProbability Prob(*this); + Prob -= RHS; + return Prob; + } + + BranchProbability operator*(BranchProbability RHS) const { + BranchProbability Prob(*this); + Prob *= RHS; + return Prob; + } + + BranchProbability operator*(uint32_t RHS) const { + BranchProbability Prob(*this); + Prob *= RHS; + return Prob; + } + + BranchProbability operator/(BranchProbability RHS) const { + BranchProbability Prob(*this); + Prob /= RHS; + return Prob; + } + + BranchProbability operator/(uint32_t RHS) const { + BranchProbability Prob(*this); + Prob /= RHS; + return Prob; + } + + bool operator==(BranchProbability RHS) const { return N == RHS.N; } + bool operator!=(BranchProbability RHS) const { return !(*this == RHS); } + + bool operator<(BranchProbability RHS) const { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in comparisons."); + return N < RHS.N; + } + + bool operator>(BranchProbability RHS) const { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in comparisons."); + return RHS < *this; + } + + bool operator<=(BranchProbability RHS) const { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in comparisons."); + return !(RHS < *this); + } + + bool operator>=(BranchProbability RHS) const { + assert(N != UnknownN && RHS.N != UnknownN && + "Unknown probability cannot participate in comparisons."); + return !(*this < RHS); + } +}; + +inline raw_ostream &operator<<(raw_ostream &OS, BranchProbability Prob) { + return Prob.print(OS); +} + +template <class ProbabilityIter> +void BranchProbability::normalizeProbabilities(ProbabilityIter Begin, + ProbabilityIter End) { + if (Begin == End) + return; + + unsigned UnknownProbCount = 0; + uint64_t Sum = std::accumulate(Begin, End, uint64_t(0), + [&](uint64_t S, const BranchProbability &BP) { + if (!BP.isUnknown()) + return S + BP.N; + UnknownProbCount++; + return S; + }); + + if (UnknownProbCount > 0) { + BranchProbability ProbForUnknown = BranchProbability::getZero(); + // If the sum of all known probabilities is less than one, evenly distribute + // the complement of sum to unknown probabilities. Otherwise, set unknown + // probabilities to zeros and continue to normalize known probabilities. + if (Sum < BranchProbability::getDenominator()) + ProbForUnknown = BranchProbability::getRaw( + (BranchProbability::getDenominator() - Sum) / UnknownProbCount); + + std::replace_if(Begin, End, + [](const BranchProbability &BP) { return BP.isUnknown(); }, + ProbForUnknown); + + if (Sum <= BranchProbability::getDenominator()) + return; + } + + if (Sum == 0) { + BranchProbability BP(1, std::distance(Begin, End)); + std::fill(Begin, End, BP); + return; + } + + for (auto I = Begin; I != End; ++I) + I->N = (I->N * uint64_t(D) + Sum / 2) / Sum; +} + +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/BuryPointer.h b/contrib/libs/llvm16/include/llvm/Support/BuryPointer.h new file mode 100644 index 00000000000..30dfecd025b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/BuryPointer.h @@ -0,0 +1,40 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/BuryPointer.h - Memory Manipulation/Leak ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_BURYPOINTER_H +#define LLVM_SUPPORT_BURYPOINTER_H + +#include <memory> + +namespace llvm { + +// In tools that will exit soon anyway, going through the process of explicitly +// deallocating resources can be unnecessary - better to leak the resources and +// let the OS clean them up when the process ends. Use this function to ensure +// the memory is not misdiagnosed as an unintentional leak by leak detection +// tools (this is achieved by preserving pointers to the object in a globally +// visible array). +void BuryPointer(const void *Ptr); +template <typename T> void BuryPointer(std::unique_ptr<T> Ptr) { + BuryPointer(Ptr.release()); +} + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CBindingWrapping.h b/contrib/libs/llvm16/include/llvm/Support/CBindingWrapping.h new file mode 100644 index 00000000000..5b49d226117 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CBindingWrapping.h @@ -0,0 +1,57 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/CBindingWrapping.h - C Interface Wrapping ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the wrapping macros for the C interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CBINDINGWRAPPING_H +#define LLVM_SUPPORT_CBINDINGWRAPPING_H + +#include "llvm-c/Types.h" +#include "llvm/Support/Casting.h" + +#define DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ty, ref) \ + inline ty *unwrap(ref P) { \ + return reinterpret_cast<ty*>(P); \ + } \ + \ + inline ref wrap(const ty *P) { \ + return reinterpret_cast<ref>(const_cast<ty*>(P)); \ + } + +#define DEFINE_ISA_CONVERSION_FUNCTIONS(ty, ref) \ + DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ty, ref) \ + \ + template<typename T> \ + inline T *unwrap(ref P) { \ + return cast<T>(unwrap(P)); \ + } + +#define DEFINE_STDCXX_CONVERSION_FUNCTIONS(ty, ref) \ + DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ty, ref) \ + \ + template<typename T> \ + inline T *unwrap(ref P) { \ + T *Q = (T*)unwrap(P); \ + assert(Q && "Invalid cast!"); \ + return Q; \ + } + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CFGDiff.h b/contrib/libs/llvm16/include/llvm/Support/CFGDiff.h new file mode 100644 index 00000000000..398314b4fce --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CFGDiff.h @@ -0,0 +1,188 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- CFGDiff.h - Define a CFG snapshot. -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines specializations of GraphTraits that allows generic +// algorithms to see a different snapshot of a CFG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CFGDIFF_H +#define LLVM_SUPPORT_CFGDIFF_H + +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/iterator.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/CFGUpdate.h" +#include "llvm/Support/type_traits.h" +#include <cassert> +#include <cstddef> +#include <iterator> + +// Two booleans are used to define orders in graphs: +// InverseGraph defines when we need to reverse the whole graph and is as such +// also equivalent to applying updates in reverse. +// InverseEdge defines whether we want to change the edges direction. E.g., for +// a non-inversed graph, the children are naturally the successors when +// InverseEdge is false and the predecessors when InverseEdge is true. + +namespace llvm { + +namespace detail { +template <typename Range> +auto reverse_if_helper(Range &&R, std::integral_constant<bool, false>) { + return std::forward<Range>(R); +} + +template <typename Range> +auto reverse_if_helper(Range &&R, std::integral_constant<bool, true>) { + return llvm::reverse(std::forward<Range>(R)); +} + +template <bool B, typename Range> auto reverse_if(Range &&R) { + return reverse_if_helper(std::forward<Range>(R), + std::integral_constant<bool, B>{}); +} +} // namespace detail + +// GraphDiff defines a CFG snapshot: given a set of Update<NodePtr>, provides +// a getChildren method to get a Node's children based on the additional updates +// in the snapshot. The current diff treats the CFG as a graph rather than a +// multigraph. Added edges are pruned to be unique, and deleted edges will +// remove all existing edges between two blocks. +template <typename NodePtr, bool InverseGraph = false> class GraphDiff { + struct DeletesInserts { + SmallVector<NodePtr, 2> DI[2]; + }; + using UpdateMapType = SmallDenseMap<NodePtr, DeletesInserts>; + UpdateMapType Succ; + UpdateMapType Pred; + + // By default, it is assumed that, given a CFG and a set of updates, we wish + // to apply these updates as given. If UpdatedAreReverseApplied is set, the + // updates will be applied in reverse: deleted edges are considered re-added + // and inserted edges are considered deleted when returning children. + bool UpdatedAreReverseApplied; + + // Keep the list of legalized updates for a deterministic order of updates + // when using a GraphDiff for incremental updates in the DominatorTree. + // The list is kept in reverse to allow popping from end. + SmallVector<cfg::Update<NodePtr>, 4> LegalizedUpdates; + + void printMap(raw_ostream &OS, const UpdateMapType &M) const { + StringRef DIText[2] = {"Delete", "Insert"}; + for (auto Pair : M) { + for (unsigned IsInsert = 0; IsInsert <= 1; ++IsInsert) { + OS << DIText[IsInsert] << " edges: \n"; + for (auto Child : Pair.second.DI[IsInsert]) { + OS << "("; + Pair.first->printAsOperand(OS, false); + OS << ", "; + Child->printAsOperand(OS, false); + OS << ") "; + } + } + } + OS << "\n"; + } + +public: + GraphDiff() : UpdatedAreReverseApplied(false) {} + GraphDiff(ArrayRef<cfg::Update<NodePtr>> Updates, + bool ReverseApplyUpdates = false) { + cfg::LegalizeUpdates<NodePtr>(Updates, LegalizedUpdates, InverseGraph); + for (auto U : LegalizedUpdates) { + unsigned IsInsert = + (U.getKind() == cfg::UpdateKind::Insert) == !ReverseApplyUpdates; + Succ[U.getFrom()].DI[IsInsert].push_back(U.getTo()); + Pred[U.getTo()].DI[IsInsert].push_back(U.getFrom()); + } + UpdatedAreReverseApplied = ReverseApplyUpdates; + } + + auto getLegalizedUpdates() const { + return make_range(LegalizedUpdates.begin(), LegalizedUpdates.end()); + } + + unsigned getNumLegalizedUpdates() const { return LegalizedUpdates.size(); } + + cfg::Update<NodePtr> popUpdateForIncrementalUpdates() { + assert(!LegalizedUpdates.empty() && "No updates to apply!"); + auto U = LegalizedUpdates.pop_back_val(); + unsigned IsInsert = + (U.getKind() == cfg::UpdateKind::Insert) == !UpdatedAreReverseApplied; + auto &SuccDIList = Succ[U.getFrom()]; + auto &SuccList = SuccDIList.DI[IsInsert]; + assert(SuccList.back() == U.getTo()); + SuccList.pop_back(); + if (SuccList.empty() && SuccDIList.DI[!IsInsert].empty()) + Succ.erase(U.getFrom()); + + auto &PredDIList = Pred[U.getTo()]; + auto &PredList = PredDIList.DI[IsInsert]; + assert(PredList.back() == U.getFrom()); + PredList.pop_back(); + if (PredList.empty() && PredDIList.DI[!IsInsert].empty()) + Pred.erase(U.getTo()); + return U; + } + + using VectRet = SmallVector<NodePtr, 8>; + template <bool InverseEdge> VectRet getChildren(NodePtr N) const { + using DirectedNodeT = + std::conditional_t<InverseEdge, Inverse<NodePtr>, NodePtr>; + auto R = children<DirectedNodeT>(N); + VectRet Res = VectRet(detail::reverse_if<!InverseEdge>(R)); + + // Remove nullptr children for clang. + llvm::erase_value(Res, nullptr); + + auto &Children = (InverseEdge != InverseGraph) ? Pred : Succ; + auto It = Children.find(N); + if (It == Children.end()) + return Res; + + // Remove children present in the CFG but not in the snapshot. + for (auto *Child : It->second.DI[0]) + llvm::erase_value(Res, Child); + + // Add children present in the snapshot for not in the real CFG. + auto &AddedChildren = It->second.DI[1]; + llvm::append_range(Res, AddedChildren); + + return Res; + } + + void print(raw_ostream &OS) const { + OS << "===== GraphDiff: CFG edge changes to create a CFG snapshot. \n" + "===== (Note: notion of children/inverse_children depends on " + "the direction of edges and the graph.)\n"; + OS << "Children to delete/insert:\n\t"; + printMap(OS, Succ); + OS << "Inverse_children to delete/insert:\n\t"; + printMap(OS, Pred); + OS << "\n"; + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { print(dbgs()); } +#endif +}; +} // end namespace llvm + +#endif // LLVM_SUPPORT_CFGDIFF_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CFGUpdate.h b/contrib/libs/llvm16/include/llvm/Support/CFGUpdate.h new file mode 100644 index 00000000000..41ea4d0fcb9 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CFGUpdate.h @@ -0,0 +1,128 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- CFGUpdate.h - Encode a CFG Edge Update. ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a CFG Edge Update: Insert or Delete, and two Nodes as the +// Edge ends. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CFGUPDATE_H +#define LLVM_SUPPORT_CFGUPDATE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace cfg { +enum class UpdateKind : unsigned char { Insert, Delete }; + +template <typename NodePtr> class Update { + using NodeKindPair = PointerIntPair<NodePtr, 1, UpdateKind>; + NodePtr From; + NodeKindPair ToAndKind; + +public: + 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; + } + + void print(raw_ostream &OS) const { + OS << (getKind() == UpdateKind::Insert ? "Insert " : "Delete "); + getFrom()->printAsOperand(OS, false); + OS << " -> "; + getTo()->printAsOperand(OS, false); + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { print(dbgs()); } +#endif +}; + +// LegalizeUpdates function simplifies updates assuming a graph structure. +// 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. +template <typename NodePtr> +void LegalizeUpdates(ArrayRef<Update<NodePtr>> AllUpdates, + SmallVectorImpl<Update<NodePtr>> &Result, + bool InverseGraph, bool ReverseResultOrder = false) { + // 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 (InverseGraph) + 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 (!InverseGraph) + Operations[{U.getFrom(), U.getTo()}] = int(i); + else + Operations[{U.getTo(), U.getFrom()}] = int(i); + } + + llvm::sort(Result, [&](const Update<NodePtr> &A, const Update<NodePtr> &B) { + const auto &OpA = Operations[{A.getFrom(), A.getTo()}]; + const auto &OpB = Operations[{B.getFrom(), B.getTo()}]; + return ReverseResultOrder ? OpA < OpB : OpA > OpB; + }); +} + +} // end namespace cfg +} // end namespace llvm + +#endif // LLVM_SUPPORT_CFGUPDATE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/COM.h b/contrib/libs/llvm16/include/llvm/Support/COM.h new file mode 100644 index 00000000000..5f927107315 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/COM.h @@ -0,0 +1,46 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/COM.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// Provides a library for accessing COM functionality of the Host OS. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_COM_H +#define LLVM_SUPPORT_COM_H + +namespace llvm { +namespace sys { + +enum class COMThreadingMode { SingleThreaded, MultiThreaded }; + +class InitializeCOMRAII { +public: + explicit InitializeCOMRAII(COMThreadingMode Threading, + bool SpeedOverMemory = false); + ~InitializeCOMRAII(); + +private: + InitializeCOMRAII(const InitializeCOMRAII &) = delete; + void operator=(const InitializeCOMRAII &) = delete; +}; +} +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CRC.h b/contrib/libs/llvm16/include/llvm/Support/CRC.h new file mode 100644 index 00000000000..22d2768fc50 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CRC.h @@ -0,0 +1,71 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/CRC.h - Cyclic Redundancy Check-------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains implementations of CRC functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CRC_H +#define LLVM_SUPPORT_CRC_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { +template <typename T> class ArrayRef; + +// Compute the CRC-32 of Data. +uint32_t crc32(ArrayRef<uint8_t> Data); + +// Compute the running CRC-32 of Data, with CRC being the previous value of the +// checksum. +uint32_t crc32(uint32_t CRC, ArrayRef<uint8_t> Data); + +// Class for computing the JamCRC. +// +// We will use the "Rocksoft^tm Model CRC Algorithm" to describe the properties +// of this CRC: +// Width : 32 +// Poly : 04C11DB7 +// Init : FFFFFFFF +// RefIn : True +// RefOut : True +// XorOut : 00000000 +// Check : 340BC6D9 (result of CRC for "123456789") +// +// In other words, this is the same as CRC-32, except that XorOut is 0 instead +// of FFFFFFFF. +// +// N.B. We permit flexibility of the "Init" value. Some consumers of this need +// it to be zero. +class JamCRC { +public: + JamCRC(uint32_t Init = 0xFFFFFFFFU) : CRC(Init) {} + + // Update the CRC calculation with Data. + void update(ArrayRef<uint8_t> Data); + + uint32_t getCRC() const { return CRC; } + +private: + uint32_t CRC; +}; + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CSKYAttributeParser.h b/contrib/libs/llvm16/include/llvm/Support/CSKYAttributeParser.h new file mode 100644 index 00000000000..082427b42a5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CSKYAttributeParser.h @@ -0,0 +1,54 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===---- CSKYAttributeParser.h - CSKY Attribute Parser ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CSKYATTRIBUTEPARSER_H +#define LLVM_SUPPORT_CSKYATTRIBUTEPARSER_H + +#include "llvm/Support/CSKYAttributes.h" +#include "llvm/Support/ELFAttributeParser.h" + +namespace llvm { +class CSKYAttributeParser : public ELFAttributeParser { + struct DisplayHandler { + CSKYAttrs::AttrType attribute; + Error (CSKYAttributeParser::*routine)(unsigned); + }; + static const DisplayHandler displayRoutines[]; + + Error dspVersion(unsigned tag); + Error vdspVersion(unsigned tag); + Error fpuVersion(unsigned tag); + Error fpuABI(unsigned tag); + Error fpuRounding(unsigned tag); + Error fpuDenormal(unsigned tag); + Error fpuException(unsigned tag); + Error fpuHardFP(unsigned tag); + + Error handler(uint64_t tag, bool &handled) override; + +public: + CSKYAttributeParser(ScopedPrinter *sw) + : ELFAttributeParser(sw, CSKYAttrs::getCSKYAttributeTags(), "csky") {} + CSKYAttributeParser() + : ELFAttributeParser(CSKYAttrs::getCSKYAttributeTags(), "csky") {} +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CSKYAttributes.h b/contrib/libs/llvm16/include/llvm/Support/CSKYAttributes.h new file mode 100644 index 00000000000..824bec45f93 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CSKYAttributes.h @@ -0,0 +1,106 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===---- CSKYAttributes.h - CSKY Attributes --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains enumerations for CSKY attributes. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_CSKYATTRIBUTES_H +#define LLVM_SUPPORT_CSKYATTRIBUTES_H + +#include "llvm/Support/ELFAttributes.h" + +namespace llvm { +namespace CSKYAttrs { + +const TagNameMap &getCSKYAttributeTags(); + +enum AttrType { + CSKY_ARCH_NAME = 4, + CSKY_CPU_NAME = 5, + CSKY_ISA_FLAGS = 6, + CSKY_ISA_EXT_FLAGS = 7, + CSKY_DSP_VERSION = 8, + CSKY_VDSP_VERSION = 9, + CSKY_FPU_VERSION = 16, + CSKY_FPU_ABI = 17, + CSKY_FPU_ROUNDING = 18, + CSKY_FPU_DENORMAL = 19, + CSKY_FPU_EXCEPTION = 20, + CSKY_FPU_NUMBER_MODULE = 21, + CSKY_FPU_HARDFP = 22 +}; + +enum ISA_FLAGS { + V2_ISA_E1 = 1 << 1, + V2_ISA_1E2 = 1 << 2, + V2_ISA_2E3 = 1 << 3, + V2_ISA_3E7 = 1 << 4, + V2_ISA_7E10 = 1 << 5, + V2_ISA_3E3R1 = 1 << 6, + V2_ISA_3E3R2 = 1 << 7, + V2_ISA_10E60 = 1 << 8, + V2_ISA_3E3R3 = 1 << 9, + ISA_TRUST = 1 << 11, + ISA_CACHE = 1 << 12, + ISA_NVIC = 1 << 13, + ISA_CP = 1 << 14, + ISA_MP = 1 << 15, + ISA_MP_1E2 = 1 << 16, + ISA_JAVA = 1 << 17, + ISA_MAC = 1 << 18, + ISA_MAC_DSP = 1 << 19, + ISA_DSP = 1 << 20, + ISA_DSP_1E2 = 1 << 21, + ISA_DSP_ENHANCE = 1 << 22, + ISA_DSP_SILAN = 1 << 23, + ISA_VDSP = 1 << 24, + ISA_VDSP_2 = 1 << 25, + ISA_VDSP_2E3 = 1 << 26, + V2_ISA_DSPE60 = 1 << 27, + ISA_VDSP_2E60F = 1 << 28 +}; + +enum ISA_EXT_FLAGS { + ISA_FLOAT_E1 = 1 << 0, + ISA_FLOAT_1E2 = 1 << 1, + ISA_FLOAT_1E3 = 1 << 2, + ISA_FLOAT_3E4 = 1 << 3, + ISA_FLOAT_7E60 = 1 << 4 +}; + +enum { NONE = 0, NEEDED = 1 }; + +enum DSP_VERSION { DSP_VERSION_EXTENSION = 1, DSP_VERSION_2 = 2 }; + +enum VDSP_VERSION { VDSP_VERSION_1 = 1, VDSP_VERSION_2 = 2 }; + +enum FPU_VERSION { FPU_VERSION_1 = 1, FPU_VERSION_2 = 2, FPU_VERSION_3 = 3 }; + +enum FPU_ABI { FPU_ABI_SOFT = 1, FPU_ABI_SOFTFP = 2, FPU_ABI_HARD = 3 }; + +enum FPU_HARDFP { + FPU_HARDFP_HALF = 1, + FPU_HARDFP_SINGLE = 2, + FPU_HARDFP_DOUBLE = 4 +}; + +} // namespace CSKYAttrs +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CSKYTargetParser.h b/contrib/libs/llvm16/include/llvm/Support/CSKYTargetParser.h new file mode 100644 index 00000000000..d4214c6df68 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CSKYTargetParser.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/CSKYTargetParser.h -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of +/// `llvm/TargetParser/CSKYTargetParser.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/CSKYTargetParser.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CachePruning.h b/contrib/libs/llvm16/include/llvm/Support/CachePruning.h new file mode 100644 index 00000000000..d0ed0a1d28c --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CachePruning.h @@ -0,0 +1,97 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//=- CachePruning.h - Helper to manage the pruning of a cache dir -*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements pruning of a directory intended for cache storage, using +// various policies. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CACHEPRUNING_H +#define LLVM_SUPPORT_CACHEPRUNING_H + +#include "llvm/Support/MemoryBuffer.h" +#include <chrono> +#include <optional> + +namespace llvm { + +template <typename T> class Expected; +class StringRef; + +/// Policy for the pruneCache() function. A default constructed +/// CachePruningPolicy provides a reasonable default policy. +struct CachePruningPolicy { + /// The pruning interval. This is intended to be used to avoid scanning the + /// directory too often. It does not impact the decision of which file to + /// prune. A value of 0 forces the scan to occur. A value of None disables + /// pruning. + std::optional<std::chrono::seconds> Interval = std::chrono::seconds(1200); + + /// The expiration for a file. When a file hasn't been accessed for Expiration + /// seconds, it is removed from the cache. A value of 0 disables the + /// expiration-based pruning. + std::chrono::seconds Expiration = std::chrono::hours(7 * 24); // 1w + + /// The maximum size for the cache directory, in terms of percentage of the + /// available space on the disk. Set to 100 to indicate no limit, 50 to + /// indicate that the cache size will not be left over half the available disk + /// space. A value over 100 will be reduced to 100. A value of 0 disables the + /// percentage size-based pruning. + unsigned MaxSizePercentageOfAvailableSpace = 75; + + /// The maximum size for the cache directory in bytes. A value over the amount + /// 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. Some systems have a + /// limit on total number of files, and some also limit the number of files + /// per directory, such as Linux ext4, with the default setting (block size is + /// 4096 and large_dir disabled), there is a per-directory entry limit of + /// 508*510*floor(4096/(40+8))~=20M for average filename length of 40. + uint64_t MaxSizeFiles = 1000000; +}; + +/// Parse the given string as a cache pruning policy. Defaults are taken from a +/// default constructed CachePruningPolicy object. +/// For example: "prune_interval=30s:prune_after=24h:cache_size=50%" +/// which means a pruning interval of 30 seconds, expiration time of 24 hours +/// and maximum cache size of 50% of available disk space. +Expected<CachePruningPolicy> parseCachePruningPolicy(StringRef PolicyStr); + +/// Peform pruning using the supplied policy, returns true if pruning +/// occurred, i.e. if Policy.Interval was expired. +/// +/// Check whether cache pruning happens using the supplied policy, adds a +/// ThinLTO warning if cache_size_bytes or cache_size_files is too small for the +/// current link job. The warning recommends the user to consider adjusting +/// --thinlto-cache-policy. +/// +/// As a safeguard against data loss if the user specifies the wrong directory +/// as their cache directory, this function will ignore files not matching the +/// pattern "llvmcache-*". +bool pruneCache(StringRef Path, CachePruningPolicy Policy, + const std::vector<std::unique_ptr<MemoryBuffer>> &Files = {}); +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Caching.h b/contrib/libs/llvm16/include/llvm/Support/Caching.h new file mode 100644 index 00000000000..68c7111b0e2 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Caching.h @@ -0,0 +1,90 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- Caching.h - LLVM Local File Cache ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the CachedFileStream and the localCache function, which +// simplifies caching files on the local filesystem in a directory whose +// contents are managed by a CachePruningPolicy. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CACHING_H +#define LLVM_SUPPORT_CACHING_H + +#include "llvm/Support/Error.h" + +namespace llvm { + +class MemoryBuffer; + +/// This class wraps an output stream for a file. Most clients should just be +/// able to return an instance of this base class from the stream callback, but +/// if a client needs to perform some action after the stream is written to, +/// that can be done by deriving from this class and overriding the destructor. +class CachedFileStream { +public: + CachedFileStream(std::unique_ptr<raw_pwrite_stream> OS, + std::string OSPath = "") + : OS(std::move(OS)), ObjectPathName(OSPath) {} + std::unique_ptr<raw_pwrite_stream> OS; + std::string ObjectPathName; + virtual ~CachedFileStream() = default; +}; + +/// This type defines the callback to add a file that is generated on the fly. +/// +/// Stream callbacks must be thread safe. +using AddStreamFn = std::function<Expected<std::unique_ptr<CachedFileStream>>( + unsigned Task, const Twine &ModuleName)>; + +/// This is the type of a file cache. To request an item from the cache, pass a +/// unique string as the Key. For hits, the cached file will be added to the +/// link and this function will return AddStreamFn(). For misses, the cache will +/// return a stream callback which must be called at most once to produce +/// content for the stream. The file stream produced by the stream callback will +/// add the file to the link after the stream is written to. ModuleName is the +/// unique module identifier for the bitcode module the cache is being checked +/// for. +/// +/// Clients generally look like this: +/// +/// if (AddStreamFn AddStream = Cache(Task, Key, ModuleName)) +/// ProduceContent(AddStream); +using FileCache = std::function<Expected<AddStreamFn>( + unsigned Task, StringRef Key, const Twine &ModuleName)>; + +/// This type defines the callback to add a pre-existing file (e.g. in a cache). +/// +/// Buffer callbacks must be thread safe. +using AddBufferFn = std::function<void(unsigned Task, const Twine &ModuleName, + std::unique_ptr<MemoryBuffer> MB)>; + +/// Create a local file system cache which uses the given cache name, temporary +/// file prefix, cache directory and file callback. This function does not +/// immediately create the cache directory if it does not yet exist; this is +/// done lazily the first time a file is added. The cache name appears in error +/// messages for errors during caching. The temporary file prefix is used in the +/// temporary file naming scheme used when writing files atomically. +Expected<FileCache> localCache( + const Twine &CacheNameRef, const Twine &TempFilePrefixRef, + const Twine &CacheDirectoryPathRef, + AddBufferFn AddBuffer = [](size_t Task, const Twine &ModuleName, + std::unique_ptr<MemoryBuffer> MB) {}); +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Capacity.h b/contrib/libs/llvm16/include/llvm/Support/Capacity.h new file mode 100644 index 00000000000..263a5475095 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Capacity.h @@ -0,0 +1,42 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- Capacity.h - Generic computation of ADT memory use -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the capacity function that computes the amount of +// memory used by an ADT. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CAPACITY_H +#define LLVM_SUPPORT_CAPACITY_H + +#include <cstddef> + +namespace llvm { + +template <typename T> +static inline size_t capacity_in_bytes(const T &x) { + // This default definition of capacity should work for things like std::vector + // and friends. More specialized versions will work for others. + return x.capacity() * sizeof(typename T::value_type); +} + +} // end namespace llvm + +#endif + + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Casting.h b/contrib/libs/llvm16/include/llvm/Support/Casting.h new file mode 100644 index 00000000000..d2cf49b7fd4 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Casting.h @@ -0,0 +1,819 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Casting.h - Allow flexible, checked, casts --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the isa<X>(), cast<X>(), dyn_cast<X>(), +// cast_if_present<X>(), and dyn_cast_if_present<X>() templates. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CASTING_H +#define LLVM_SUPPORT_CASTING_H + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/type_traits.h" +#include <cassert> +#include <memory> +#include <optional> +#include <type_traits> + +namespace llvm { + +//===----------------------------------------------------------------------===// +// simplify_type +//===----------------------------------------------------------------------===// + +/// Define a template that can be specialized by smart pointers to reflect the +/// fact that they are automatically dereferenced, and are not involved with the +/// template selection process... the default implementation is a noop. +// TODO: rename this and/or replace it with other cast traits. +template <typename From> struct simplify_type { + using SimpleType = From; // The real type this represents... + + // An accessor to get the real value... + static SimpleType &getSimplifiedValue(From &Val) { return Val; } +}; + +template <typename From> struct simplify_type<const From> { + using NonConstSimpleType = typename simplify_type<From>::SimpleType; + using SimpleType = typename add_const_past_pointer<NonConstSimpleType>::type; + using RetType = + typename add_lvalue_reference_if_not_pointer<SimpleType>::type; + + static RetType getSimplifiedValue(const From &Val) { + return simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val)); + } +}; + +// TODO: add this namespace once everyone is switched to using the new +// interface. +// namespace detail { + +//===----------------------------------------------------------------------===// +// isa_impl +//===----------------------------------------------------------------------===// + +// The core of the implementation of isa<X> is here; To and From should be +// the names of classes. This template can be specialized to customize the +// implementation of isa<> without rewriting it from scratch. +template <typename To, typename From, typename Enabler = void> struct isa_impl { + static inline bool doit(const From &Val) { return To::classof(&Val); } +}; + +// Always allow upcasts, and perform no dynamic check for them. +template <typename To, typename From> +struct isa_impl<To, From, std::enable_if_t<std::is_base_of<To, From>::value>> { + static inline bool doit(const From &) { return true; } +}; + +template <typename To, typename From> struct isa_impl_cl { + static inline bool doit(const From &Val) { + return isa_impl<To, From>::doit(Val); + } +}; + +template <typename To, typename From> struct isa_impl_cl<To, const From> { + static inline bool doit(const From &Val) { + return isa_impl<To, From>::doit(Val); + } +}; + +template <typename To, typename From> +struct isa_impl_cl<To, const std::unique_ptr<From>> { + static inline bool doit(const std::unique_ptr<From> &Val) { + assert(Val && "isa<> used on a null pointer"); + return isa_impl_cl<To, From>::doit(*Val); + } +}; + +template <typename To, typename From> struct isa_impl_cl<To, From *> { + static inline bool doit(const From *Val) { + assert(Val && "isa<> used on a null pointer"); + return isa_impl<To, From>::doit(*Val); + } +}; + +template <typename To, typename From> struct isa_impl_cl<To, From *const> { + static inline bool doit(const From *Val) { + assert(Val && "isa<> used on a null pointer"); + return isa_impl<To, From>::doit(*Val); + } +}; + +template <typename To, typename From> struct isa_impl_cl<To, const From *> { + static inline bool doit(const From *Val) { + assert(Val && "isa<> used on a null pointer"); + return isa_impl<To, From>::doit(*Val); + } +}; + +template <typename To, typename From> +struct isa_impl_cl<To, const From *const> { + static inline bool doit(const From *Val) { + assert(Val && "isa<> used on a null pointer"); + return isa_impl<To, From>::doit(*Val); + } +}; + +template <typename To, typename From, typename SimpleFrom> +struct isa_impl_wrap { + // When From != SimplifiedType, we can simplify the type some more by using + // the simplify_type template. + static bool doit(const From &Val) { + return isa_impl_wrap<To, SimpleFrom, + typename simplify_type<SimpleFrom>::SimpleType>:: + doit(simplify_type<const From>::getSimplifiedValue(Val)); + } +}; + +template <typename To, typename FromTy> +struct isa_impl_wrap<To, FromTy, FromTy> { + // When From == SimpleType, we are as simple as we are going to get. + static bool doit(const FromTy &Val) { + return isa_impl_cl<To, FromTy>::doit(Val); + } +}; + +//===----------------------------------------------------------------------===// +// cast_retty + cast_retty_impl +//===----------------------------------------------------------------------===// + +template <class To, class From> struct cast_retty; + +// Calculate what type the 'cast' function should return, based on a requested +// type of To and a source type of From. +template <class To, class From> struct cast_retty_impl { + using ret_type = To &; // Normal case, return Ty& +}; +template <class To, class From> struct cast_retty_impl<To, const From> { + using ret_type = const To &; // Normal case, return Ty& +}; + +template <class To, class From> struct cast_retty_impl<To, From *> { + using ret_type = To *; // Pointer arg case, return Ty* +}; + +template <class To, class From> struct cast_retty_impl<To, const From *> { + using ret_type = const To *; // Constant pointer arg case, return const Ty* +}; + +template <class To, class From> struct cast_retty_impl<To, const From *const> { + using ret_type = const To *; // Constant pointer arg case, return const Ty* +}; + +template <class To, class From> +struct cast_retty_impl<To, std::unique_ptr<From>> { +private: + using PointerType = typename cast_retty_impl<To, From *>::ret_type; + using ResultType = std::remove_pointer_t<PointerType>; + +public: + using ret_type = std::unique_ptr<ResultType>; +}; + +template <class To, class From, class SimpleFrom> struct cast_retty_wrap { + // When the simplified type and the from type are not the same, use the type + // simplifier to reduce the type, then reuse cast_retty_impl to get the + // resultant type. + using ret_type = typename cast_retty<To, SimpleFrom>::ret_type; +}; + +template <class To, class FromTy> struct cast_retty_wrap<To, FromTy, FromTy> { + // When the simplified type is equal to the from type, use it directly. + using ret_type = typename cast_retty_impl<To, FromTy>::ret_type; +}; + +template <class To, class From> struct cast_retty { + using ret_type = typename cast_retty_wrap< + To, From, typename simplify_type<From>::SimpleType>::ret_type; +}; + +//===----------------------------------------------------------------------===// +// cast_convert_val +//===----------------------------------------------------------------------===// + +// Ensure the non-simple values are converted using the simplify_type template +// that may be specialized by smart pointers... +// +template <class To, class From, class SimpleFrom> struct cast_convert_val { + // This is not a simple type, use the template to simplify it... + static typename cast_retty<To, From>::ret_type doit(const From &Val) { + return cast_convert_val<To, SimpleFrom, + typename simplify_type<SimpleFrom>::SimpleType>:: + doit(simplify_type<From>::getSimplifiedValue(const_cast<From &>(Val))); + } +}; + +template <class To, class FromTy> struct cast_convert_val<To, FromTy, FromTy> { + // If it's a reference, switch to a pointer to do the cast and then deref it. + static typename cast_retty<To, FromTy>::ret_type doit(const FromTy &Val) { + return *(std::remove_reference_t<typename cast_retty<To, FromTy>::ret_type> + *)&const_cast<FromTy &>(Val); + } +}; + +template <class To, class FromTy> +struct cast_convert_val<To, FromTy *, FromTy *> { + // If it's a pointer, we can use c-style casting directly. + static typename cast_retty<To, FromTy *>::ret_type doit(const FromTy *Val) { + return (typename cast_retty<To, FromTy *>::ret_type) const_cast<FromTy *>( + Val); + } +}; + +//===----------------------------------------------------------------------===// +// is_simple_type +//===----------------------------------------------------------------------===// + +template <class X> struct is_simple_type { + static const bool value = + std::is_same<X, typename simplify_type<X>::SimpleType>::value; +}; + +// } // namespace detail + +//===----------------------------------------------------------------------===// +// CastIsPossible +//===----------------------------------------------------------------------===// + +/// This struct provides a way to check if a given cast is possible. It provides +/// a static function called isPossible that is used to check if a cast can be +/// performed. It should be overridden like this: +/// +/// template<> struct CastIsPossible<foo, bar> { +/// static inline bool isPossible(const bar &b) { +/// return bar.isFoo(); +/// } +/// }; +template <typename To, typename From, typename Enable = void> +struct CastIsPossible { + static inline bool isPossible(const From &f) { + return isa_impl_wrap< + To, const From, + typename simplify_type<const From>::SimpleType>::doit(f); + } +}; + +// Needed for optional unwrapping. This could be implemented with isa_impl, but +// we want to implement things in the new method and move old implementations +// over. In fact, some of the isa_impl templates should be moved over to +// CastIsPossible. +template <typename To, typename From> +struct CastIsPossible<To, std::optional<From>> { + static inline bool isPossible(const std::optional<From> &f) { + assert(f && "CastIsPossible::isPossible called on a nullopt!"); + return isa_impl_wrap< + To, const From, + typename simplify_type<const From>::SimpleType>::doit(*f); + } +}; + +/// Upcasting (from derived to base) and casting from a type to itself should +/// always be possible. +template <typename To, typename From> +struct CastIsPossible<To, From, + std::enable_if_t<std::is_base_of<To, From>::value>> { + static inline bool isPossible(const From &f) { return true; } +}; + +//===----------------------------------------------------------------------===// +// Cast traits +//===----------------------------------------------------------------------===// + +/// All of these cast traits are meant to be implementations for useful casts +/// that users may want to use that are outside the standard behavior. An +/// example of how to use a special cast called `CastTrait` is: +/// +/// template<> struct CastInfo<foo, bar> : public CastTrait<foo, bar> {}; +/// +/// Essentially, if your use case falls directly into one of the use cases +/// supported by a given cast trait, simply inherit your special CastInfo +/// directly from one of these to avoid having to reimplement the boilerplate +/// `isPossible/castFailed/doCast/doCastIfPossible`. A cast trait can also +/// provide a subset of those functions. + +/// This cast trait just provides castFailed for the specified `To` type to make +/// CastInfo specializations more declarative. In order to use this, the target +/// result type must be `To` and `To` must be constructible from `nullptr`. +template <typename To> struct NullableValueCastFailed { + static To castFailed() { return To(nullptr); } +}; + +/// This cast trait just provides the default implementation of doCastIfPossible +/// to make CastInfo specializations more declarative. The `Derived` template +/// parameter *must* be provided for forwarding castFailed and doCast. +template <typename To, typename From, typename Derived> +struct DefaultDoCastIfPossible { + static To doCastIfPossible(From f) { + if (!Derived::isPossible(f)) + return Derived::castFailed(); + return Derived::doCast(f); + } +}; + +namespace detail { +/// A helper to derive the type to use with `Self` for cast traits, when the +/// provided CRTP derived type is allowed to be void. +template <typename OptionalDerived, typename Default> +using SelfType = std::conditional_t<std::is_same<OptionalDerived, void>::value, + Default, OptionalDerived>; +} // namespace detail + +/// This cast trait provides casting for the specific case of casting to a +/// value-typed object from a pointer-typed object. Note that `To` must be +/// nullable/constructible from a pointer to `From` to use this cast. +template <typename To, typename From, typename Derived = void> +struct ValueFromPointerCast + : public CastIsPossible<To, From *>, + public NullableValueCastFailed<To>, + public DefaultDoCastIfPossible< + To, From *, + detail::SelfType<Derived, ValueFromPointerCast<To, From>>> { + static inline To doCast(From *f) { return To(f); } +}; + +/// This cast trait provides std::unique_ptr casting. It has the semantics of +/// moving the contents of the input unique_ptr into the output unique_ptr +/// during the cast. It's also a good example of how to implement a move-only +/// cast. +template <typename To, typename From, typename Derived = void> +struct UniquePtrCast : public CastIsPossible<To, From *> { + using Self = detail::SelfType<Derived, UniquePtrCast<To, From>>; + using CastResultType = std::unique_ptr< + std::remove_reference_t<typename cast_retty<To, From>::ret_type>>; + + static inline CastResultType doCast(std::unique_ptr<From> &&f) { + return CastResultType((typename CastResultType::element_type *)f.release()); + } + + static inline CastResultType castFailed() { return CastResultType(nullptr); } + + static inline CastResultType doCastIfPossible(std::unique_ptr<From> &&f) { + if (!Self::isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// This cast trait provides std::optional<T> casting. This means that if you +/// have a value type, you can cast it to another value type and have dyn_cast +/// return an std::optional<T>. +template <typename To, typename From, typename Derived = void> +struct OptionalValueCast + : public CastIsPossible<To, From>, + public DefaultDoCastIfPossible< + std::optional<To>, From, + detail::SelfType<Derived, OptionalValueCast<To, From>>> { + static inline std::optional<To> castFailed() { return std::optional<To>{}; } + + static inline std::optional<To> doCast(const From &f) { return To(f); } +}; + +/// Provides a cast trait that strips `const` from types to make it easier to +/// implement a const-version of a non-const cast. It just removes boilerplate +/// and reduces the amount of code you as the user need to implement. You can +/// use it like this: +/// +/// template<> struct CastInfo<foo, bar> { +/// ...verbose implementation... +/// }; +/// +/// template<> struct CastInfo<foo, const bar> : public +/// ConstStrippingForwardingCast<foo, const bar, CastInfo<foo, bar>> {}; +/// +template <typename To, typename From, typename ForwardTo> +struct ConstStrippingForwardingCast { + // Remove the pointer if it exists, then we can get rid of consts/volatiles. + using DecayedFrom = std::remove_cv_t<std::remove_pointer_t<From>>; + // Now if it's a pointer, add it back. Otherwise, we want a ref. + using NonConstFrom = std::conditional_t<std::is_pointer<From>::value, + DecayedFrom *, DecayedFrom &>; + + static inline bool isPossible(const From &f) { + return ForwardTo::isPossible(const_cast<NonConstFrom>(f)); + } + + static inline decltype(auto) castFailed() { return ForwardTo::castFailed(); } + + static inline decltype(auto) doCast(const From &f) { + return ForwardTo::doCast(const_cast<NonConstFrom>(f)); + } + + static inline decltype(auto) doCastIfPossible(const From &f) { + return ForwardTo::doCastIfPossible(const_cast<NonConstFrom>(f)); + } +}; + +/// Provides a cast trait that uses a defined pointer to pointer cast as a base +/// for reference-to-reference casts. Note that it does not provide castFailed +/// and doCastIfPossible because a pointer-to-pointer cast would likely just +/// return `nullptr` which could cause nullptr dereference. You can use it like +/// this: +/// +/// template <> struct CastInfo<foo, bar *> { ... verbose implementation... }; +/// +/// template <> +/// struct CastInfo<foo, bar> +/// : public ForwardToPointerCast<foo, bar, CastInfo<foo, bar *>> {}; +/// +template <typename To, typename From, typename ForwardTo> +struct ForwardToPointerCast { + static inline bool isPossible(const From &f) { + return ForwardTo::isPossible(&f); + } + + static inline decltype(auto) doCast(const From &f) { + return *ForwardTo::doCast(&f); + } +}; + +//===----------------------------------------------------------------------===// +// CastInfo +//===----------------------------------------------------------------------===// + +/// This struct provides a method for customizing the way a cast is performed. +/// It inherits from CastIsPossible, to support the case of declaring many +/// CastIsPossible specializations without having to specialize the full +/// CastInfo. +/// +/// In order to specialize different behaviors, specify different functions in +/// your CastInfo specialization. +/// For isa<> customization, provide: +/// +/// `static bool isPossible(const From &f)` +/// +/// For cast<> customization, provide: +/// +/// `static To doCast(const From &f)` +/// +/// For dyn_cast<> and the *_if_present<> variants' customization, provide: +/// +/// `static To castFailed()` and `static To doCastIfPossible(const From &f)` +/// +/// Your specialization might look something like this: +/// +/// template<> struct CastInfo<foo, bar> : public CastIsPossible<foo, bar> { +/// static inline foo doCast(const bar &b) { +/// return foo(const_cast<bar &>(b)); +/// } +/// static inline foo castFailed() { return foo(); } +/// static inline foo doCastIfPossible(const bar &b) { +/// if (!CastInfo<foo, bar>::isPossible(b)) +/// return castFailed(); +/// return doCast(b); +/// } +/// }; + +// The default implementations of CastInfo don't use cast traits for now because +// we need to specify types all over the place due to the current expected +// casting behavior and the way cast_retty works. New use cases can and should +// take advantage of the cast traits whenever possible! + +template <typename To, typename From, typename Enable = void> +struct CastInfo : public CastIsPossible<To, From> { + using Self = CastInfo<To, From, Enable>; + + using CastReturnType = typename cast_retty<To, From>::ret_type; + + static inline CastReturnType doCast(const From &f) { + return cast_convert_val< + To, From, + typename simplify_type<From>::SimpleType>::doit(const_cast<From &>(f)); + } + + // This assumes that you can construct the cast return type from `nullptr`. + // This is largely to support legacy use cases - if you don't want this + // behavior you should specialize CastInfo for your use case. + static inline CastReturnType castFailed() { return CastReturnType(nullptr); } + + static inline CastReturnType doCastIfPossible(const From &f) { + if (!Self::isPossible(f)) + return castFailed(); + return doCast(f); + } +}; + +/// This struct provides an overload for CastInfo where From has simplify_type +/// defined. This simply forwards to the appropriate CastInfo with the +/// simplified type/value, so you don't have to implement both. +template <typename To, typename From> +struct CastInfo<To, From, std::enable_if_t<!is_simple_type<From>::value>> { + using Self = CastInfo<To, From>; + using SimpleFrom = typename simplify_type<From>::SimpleType; + using SimplifiedSelf = CastInfo<To, SimpleFrom>; + + static inline bool isPossible(From &f) { + return SimplifiedSelf::isPossible( + simplify_type<From>::getSimplifiedValue(f)); + } + + static inline decltype(auto) doCast(From &f) { + return SimplifiedSelf::doCast(simplify_type<From>::getSimplifiedValue(f)); + } + + static inline decltype(auto) castFailed() { + return SimplifiedSelf::castFailed(); + } + + static inline decltype(auto) doCastIfPossible(From &f) { + return SimplifiedSelf::doCastIfPossible( + simplify_type<From>::getSimplifiedValue(f)); + } +}; + +//===----------------------------------------------------------------------===// +// Pre-specialized CastInfo +//===----------------------------------------------------------------------===// + +/// Provide a CastInfo specialized for std::unique_ptr. +template <typename To, typename From> +struct CastInfo<To, std::unique_ptr<From>> : public UniquePtrCast<To, From> {}; + +/// Provide a CastInfo specialized for std::optional<From>. It's assumed that if +/// the input is std::optional<From> that the output can be std::optional<To>. +/// If that's not the case, specialize CastInfo for your use case. +template <typename To, typename From> +struct CastInfo<To, std::optional<From>> : public OptionalValueCast<To, From> { +}; + +/// isa<X> - Return true if the parameter to the template is an instance of one +/// of the template type arguments. Used like this: +/// +/// if (isa<Type>(myVal)) { ... } +/// if (isa<Type0, Type1, Type2>(myVal)) { ... } +template <typename To, typename From> +[[nodiscard]] inline bool isa(const From &Val) { + return CastInfo<To, const From>::isPossible(Val); +} + +template <typename First, typename Second, typename... Rest, typename From> +[[nodiscard]] inline bool isa(const From &Val) { + return isa<First>(Val) || isa<Second, Rest...>(Val); +} + +/// cast<X> - Return the argument parameter cast to the specified type. This +/// casting operator asserts that the type is correct, so it does not return +/// null on failure. It does not allow a null argument (use cast_if_present for +/// that). It is typically used like this: +/// +/// cast<Instruction>(myVal)->getParent() + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) cast(const From &Val) { + assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!"); + return CastInfo<To, const From>::doCast(Val); +} + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) cast(From &Val) { + assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!"); + return CastInfo<To, From>::doCast(Val); +} + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) cast(From *Val) { + assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!"); + return CastInfo<To, From *>::doCast(Val); +} + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) cast(std::unique_ptr<From> &&Val) { + assert(isa<To>(Val) && "cast<Ty>() argument of incompatible type!"); + return CastInfo<To, std::unique_ptr<From>>::doCast(std::move(Val)); +} + +//===----------------------------------------------------------------------===// +// ValueIsPresent +//===----------------------------------------------------------------------===// + +template <typename T> +constexpr bool IsNullable = + std::is_pointer_v<T> || std::is_constructible_v<T, std::nullptr_t>; + +/// ValueIsPresent provides a way to check if a value is, well, present. For +/// pointers, this is the equivalent of checking against nullptr, for Optionals +/// this is the equivalent of checking hasValue(). It also provides a method for +/// unwrapping a value (think calling .value() on an optional). + +// Generic values can't *not* be present. +template <typename T, typename Enable = void> struct ValueIsPresent { + using UnwrappedType = T; + static inline bool isPresent(const T &t) { return true; } + static inline decltype(auto) unwrapValue(T &t) { return t; } +}; + +// Optional provides its own way to check if something is present. +template <typename T> struct ValueIsPresent<std::optional<T>> { + using UnwrappedType = T; + static inline bool isPresent(const std::optional<T> &t) { + return t.has_value(); + } + static inline decltype(auto) unwrapValue(std::optional<T> &t) { return *t; } +}; + +// If something is "nullable" then we just compare it to nullptr to see if it +// exists. +template <typename T> +struct ValueIsPresent<T, std::enable_if_t<IsNullable<T>>> { + using UnwrappedType = T; + static inline bool isPresent(const T &t) { return t != T(nullptr); } + static inline decltype(auto) unwrapValue(T &t) { return t; } +}; + +namespace detail { +// Convenience function we can use to check if a value is present. Because of +// simplify_type, we have to call it on the simplified type for now. +template <typename T> inline bool isPresent(const T &t) { + return ValueIsPresent<typename simplify_type<T>::SimpleType>::isPresent( + simplify_type<T>::getSimplifiedValue(const_cast<T &>(t))); +} + +// Convenience function we can use to unwrap a value. +template <typename T> inline decltype(auto) unwrapValue(T &t) { + return ValueIsPresent<T>::unwrapValue(t); +} +} // namespace detail + +/// dyn_cast<X> - Return the argument parameter cast to the specified type. This +/// casting operator returns null if the argument is of the wrong type, so it +/// can be used to test for a type as well as cast if successful. The value +/// passed in must be present, if not, use dyn_cast_if_present. This should be +/// used in the context of an if statement like this: +/// +/// if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... } + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) dyn_cast(const From &Val) { + assert(detail::isPresent(Val) && "dyn_cast on a non-existent value"); + return CastInfo<To, const From>::doCastIfPossible(Val); +} + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) dyn_cast(From &Val) { + assert(detail::isPresent(Val) && "dyn_cast on a non-existent value"); + return CastInfo<To, From>::doCastIfPossible(Val); +} + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) dyn_cast(From *Val) { + assert(detail::isPresent(Val) && "dyn_cast on a non-existent value"); + return CastInfo<To, From *>::doCastIfPossible(Val); +} + +template <typename To, typename From> +[[nodiscard]] inline decltype(auto) dyn_cast(std::unique_ptr<From> &&Val) { + assert(detail::isPresent(Val) && "dyn_cast on a non-existent value"); + return CastInfo<To, std::unique_ptr<From>>::doCastIfPossible( + std::forward<std::unique_ptr<From> &&>(Val)); +} + +/// isa_and_present<X> - Functionally identical to isa, except that a null value +/// is accepted. +template <typename... X, class Y> +[[nodiscard]] inline bool isa_and_present(const Y &Val) { + if (!detail::isPresent(Val)) + return false; + return isa<X...>(Val); +} + +template <typename... X, class Y> +[[nodiscard]] inline bool isa_and_nonnull(const Y &Val) { + return isa_and_present<X...>(Val); +} + +/// cast_if_present<X> - Functionally identical to cast, except that a null +/// value is accepted. +template <class X, class Y> +[[nodiscard]] inline auto cast_if_present(const Y &Val) { + if (!detail::isPresent(Val)) + return CastInfo<X, const Y>::castFailed(); + assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!"); + return cast<X>(detail::unwrapValue(Val)); +} + +template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y &Val) { + if (!detail::isPresent(Val)) + return CastInfo<X, Y>::castFailed(); + assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!"); + return cast<X>(detail::unwrapValue(Val)); +} + +template <class X, class Y> [[nodiscard]] inline auto cast_if_present(Y *Val) { + if (!detail::isPresent(Val)) + return CastInfo<X, Y *>::castFailed(); + assert(isa<X>(Val) && "cast_if_present<Ty>() argument of incompatible type!"); + return cast<X>(detail::unwrapValue(Val)); +} + +template <class X, class Y> +[[nodiscard]] inline auto cast_if_present(std::unique_ptr<Y> &&Val) { + if (!detail::isPresent(Val)) + return UniquePtrCast<X, Y>::castFailed(); + return UniquePtrCast<X, Y>::doCast(std::move(Val)); +} + +// Provide a forwarding from cast_or_null to cast_if_present for current +// users. This is deprecated and will be removed in a future patch, use +// cast_if_present instead. +template <class X, class Y> auto cast_or_null(const Y &Val) { + return cast_if_present<X>(Val); +} + +template <class X, class Y> auto cast_or_null(Y &Val) { + return cast_if_present<X>(Val); +} + +template <class X, class Y> auto cast_or_null(Y *Val) { + return cast_if_present<X>(Val); +} + +template <class X, class Y> auto cast_or_null(std::unique_ptr<Y> &&Val) { + return cast_if_present<X>(std::move(Val)); +} + +/// dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a +/// null (or none in the case of optionals) value is accepted. +template <class X, class Y> auto dyn_cast_if_present(const Y &Val) { + if (!detail::isPresent(Val)) + return CastInfo<X, const Y>::castFailed(); + return CastInfo<X, const Y>::doCastIfPossible(detail::unwrapValue(Val)); +} + +template <class X, class Y> auto dyn_cast_if_present(Y &Val) { + if (!detail::isPresent(Val)) + return CastInfo<X, Y>::castFailed(); + return CastInfo<X, Y>::doCastIfPossible(detail::unwrapValue(Val)); +} + +template <class X, class Y> auto dyn_cast_if_present(Y *Val) { + if (!detail::isPresent(Val)) + return CastInfo<X, Y *>::castFailed(); + return CastInfo<X, Y *>::doCastIfPossible(detail::unwrapValue(Val)); +} + +// Forwards to dyn_cast_if_present to avoid breaking current users. This is +// deprecated and will be removed in a future patch, use +// cast_if_present instead. +template <class X, class Y> auto dyn_cast_or_null(const Y &Val) { + return dyn_cast_if_present<X>(Val); +} + +template <class X, class Y> auto dyn_cast_or_null(Y &Val) { + return dyn_cast_if_present<X>(Val); +} + +template <class X, class Y> auto dyn_cast_or_null(Y *Val) { + return dyn_cast_if_present<X>(Val); +} + +/// unique_dyn_cast<X> - Given a unique_ptr<Y>, try to return a unique_ptr<X>, +/// taking ownership of the input pointer iff isa<X>(Val) is true. If the +/// cast is successful, From refers to nullptr on exit and the casted value +/// is returned. If the cast is unsuccessful, the function returns nullptr +/// and From is unchanged. +template <class X, class Y> +[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType +unique_dyn_cast(std::unique_ptr<Y> &Val) { + if (!isa<X>(Val)) + return nullptr; + return cast<X>(std::move(Val)); +} + +template <class X, class Y> +[[nodiscard]] inline auto unique_dyn_cast(std::unique_ptr<Y> &&Val) { + return unique_dyn_cast<X, Y>(Val); +} + +// unique_dyn_cast_or_null<X> - Functionally identical to unique_dyn_cast, +// except that a null value is accepted. +template <class X, class Y> +[[nodiscard]] inline typename CastInfo<X, std::unique_ptr<Y>>::CastResultType +unique_dyn_cast_or_null(std::unique_ptr<Y> &Val) { + if (!Val) + return nullptr; + return unique_dyn_cast<X, Y>(Val); +} + +template <class X, class Y> +[[nodiscard]] inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &&Val) { + return unique_dyn_cast_or_null<X, Y>(Val); +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_CASTING_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CheckedArithmetic.h b/contrib/libs/llvm16/include/llvm/Support/CheckedArithmetic.h new file mode 100644 index 00000000000..190b17c63ce --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CheckedArithmetic.h @@ -0,0 +1,123 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==-- llvm/Support/CheckedArithmetic.h - Safe arithmetical operations *- C++ // +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains generic functions for operating on integers which +// give the indication on whether the operation has overflown. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CHECKEDARITHMETIC_H +#define LLVM_SUPPORT_CHECKEDARITHMETIC_H + +#include "llvm/ADT/APInt.h" + +#include <optional> +#include <type_traits> + +namespace { + +/// Utility function to apply a given method of \c APInt \p F to \p LHS and +/// \p RHS. +/// \return Empty optional if the operation overflows, or result otherwise. +template <typename T, typename F> +std::enable_if_t<std::is_integral<T>::value && sizeof(T) * 8 <= 64, + std::optional<T>> +checkedOp(T LHS, T RHS, F Op, bool Signed = true) { + llvm::APInt ALHS(sizeof(T) * 8, LHS, Signed); + llvm::APInt ARHS(sizeof(T) * 8, RHS, Signed); + bool Overflow; + llvm::APInt Out = (ALHS.*Op)(ARHS, Overflow); + if (Overflow) + return std::nullopt; + return Signed ? Out.getSExtValue() : Out.getZExtValue(); +} +} + +namespace llvm { + +/// Add two signed integers \p LHS and \p RHS. +/// \return Optional of sum if no signed overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, std::optional<T>> +checkedAdd(T LHS, T RHS) { + return checkedOp(LHS, RHS, &llvm::APInt::sadd_ov); +} + +/// Subtract two signed integers \p LHS and \p RHS. +/// \return Optional of sum if no signed overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, std::optional<T>> +checkedSub(T LHS, T RHS) { + return checkedOp(LHS, RHS, &llvm::APInt::ssub_ov); +} + +/// Multiply two signed integers \p LHS and \p RHS. +/// \return Optional of product if no signed overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, std::optional<T>> +checkedMul(T LHS, T RHS) { + return checkedOp(LHS, RHS, &llvm::APInt::smul_ov); +} + +/// Multiply A and B, and add C to the resulting product. +/// \return Optional of result if no signed overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, std::optional<T>> +checkedMulAdd(T A, T B, T C) { + if (auto Product = checkedMul(A, B)) + return checkedAdd(*Product, C); + return std::nullopt; +} + +/// Add two unsigned integers \p LHS and \p RHS. +/// \return Optional of sum if no unsigned overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, std::optional<T>> +checkedAddUnsigned(T LHS, T RHS) { + return checkedOp(LHS, RHS, &llvm::APInt::uadd_ov, /*Signed=*/false); +} + +/// Multiply two unsigned integers \p LHS and \p RHS. +/// \return Optional of product if no unsigned overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, std::optional<T>> +checkedMulUnsigned(T LHS, T RHS) { + return checkedOp(LHS, RHS, &llvm::APInt::umul_ov, /*Signed=*/false); +} + +/// Multiply unsigned integers A and B, and add C to the resulting product. +/// \return Optional of result if no unsigned overflow occurred, +/// \c std::nullopt otherwise. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, std::optional<T>> +checkedMulAddUnsigned(T A, T B, T C) { + if (auto Product = checkedMulUnsigned(A, B)) + return checkedAddUnsigned(*Product, C); + return std::nullopt; +} + +} // End llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Chrono.h b/contrib/libs/llvm16/include/llvm/Support/Chrono.h new file mode 100644 index 00000000000..3ec37ad884b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Chrono.h @@ -0,0 +1,183 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Chrono.h - Utilities for Timing Manipulation-*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CHRONO_H +#define LLVM_SUPPORT_CHRONO_H + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/FormatProviders.h" + +#include <chrono> +#include <ctime> +#include <ratio> + +namespace llvm { + +class raw_ostream; + +namespace sys { + +/// A time point on the system clock. This is provided for two reasons: +/// - to insulate us against subtle differences in behavior to differences in +/// system clock precision (which is implementation-defined and differs +/// between platforms). +/// - to shorten the type name +/// The default precision is nanoseconds. If you need a specific precision +/// specify it explicitly. If unsure, use the default. If you need a time point +/// on a clock other than the system_clock, use std::chrono directly. +template <typename D = std::chrono::nanoseconds> +using TimePoint = std::chrono::time_point<std::chrono::system_clock, D>; + +/// Convert a TimePoint to std::time_t +inline std::time_t toTimeT(TimePoint<> TP) { + using namespace std::chrono; + return system_clock::to_time_t( + time_point_cast<system_clock::time_point::duration>(TP)); +} + +/// Convert a std::time_t to a TimePoint +inline TimePoint<std::chrono::seconds> +toTimePoint(std::time_t T) { + using namespace std::chrono; + return time_point_cast<seconds>(system_clock::from_time_t(T)); +} + +/// Convert a std::time_t + nanoseconds to a TimePoint +inline TimePoint<> +toTimePoint(std::time_t T, uint32_t nsec) { + using namespace std::chrono; + return time_point_cast<nanoseconds>(system_clock::from_time_t(T)) + + nanoseconds(nsec); +} + +} // namespace sys + +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); +}; + +namespace detail { +template <typename Period> struct unit { static const char value[]; }; +template <typename Period> const char unit<Period>::value[] = ""; + +template <> struct unit<std::ratio<3600>> { static const char value[]; }; +template <> struct unit<std::ratio<60>> { static const char value[]; }; +template <> struct unit<std::ratio<1>> { static const char value[]; }; +template <> struct unit<std::milli> { static const char value[]; }; +template <> struct unit<std::micro> { static const char value[]; }; +template <> struct unit<std::nano> { static const char value[]; }; +} // namespace detail + +/// Implementation of format_provider<T> for duration types. +/// +/// The options string of a duration type has the grammar: +/// +/// duration_options ::= [unit][show_unit [number_options]] +/// unit ::= `h`|`m`|`s`|`ms|`us`|`ns` +/// show_unit ::= `+` | `-` +/// number_options ::= options string for a integral or floating point type +/// +/// Examples +/// ================================= +/// | options | Input | Output | +/// ================================= +/// | "" | 1s | 1 s | +/// | "ms" | 1s | 1000 ms | +/// | "ms-" | 1s | 1000 | +/// | "ms-n" | 1s | 1,000 | +/// | "" | 1.0s | 1.00 s | +/// ================================= +/// +/// If the unit of the duration type is not one of the units specified above, +/// it is still possible to format it, provided you explicitly request a +/// display unit or you request that the unit is not displayed. + +template <typename Rep, typename Period> +struct format_provider<std::chrono::duration<Rep, Period>> { +private: + typedef std::chrono::duration<Rep, Period> Dur; + typedef std::conditional_t<std::chrono::treat_as_floating_point<Rep>::value, + double, intmax_t> + InternalRep; + + template <typename AsPeriod> static InternalRep getAs(const Dur &D) { + using namespace std::chrono; + return duration_cast<duration<InternalRep, AsPeriod>>(D).count(); + } + + static std::pair<InternalRep, StringRef> consumeUnit(StringRef &Style, + const Dur &D) { + using namespace std::chrono; + if (Style.consume_front("ns")) + return {getAs<std::nano>(D), "ns"}; + if (Style.consume_front("us")) + return {getAs<std::micro>(D), "us"}; + if (Style.consume_front("ms")) + return {getAs<std::milli>(D), "ms"}; + if (Style.consume_front("s")) + return {getAs<std::ratio<1>>(D), "s"}; + if (Style.consume_front("m")) + return {getAs<std::ratio<60>>(D), "m"}; + if (Style.consume_front("h")) + return {getAs<std::ratio<3600>>(D), "h"}; + return {D.count(), detail::unit<Period>::value}; + } + + static bool consumeShowUnit(StringRef &Style) { + if (Style.empty()) + return true; + if (Style.consume_front("-")) + return false; + if (Style.consume_front("+")) + return true; + assert(0 && "Unrecognised duration format"); + return true; + } + +public: + static void format(const Dur &D, llvm::raw_ostream &Stream, StringRef Style) { + InternalRep count; + StringRef unit; + std::tie(count, unit) = consumeUnit(Style, D); + bool show_unit = consumeShowUnit(Style); + + format_provider<InternalRep>::format(count, Stream, Style); + + if (show_unit) { + assert(!unit.empty()); + Stream << " " << unit; + } + } +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_CHRONO_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CodeGen.h b/contrib/libs/llvm16/include/llvm/Support/CodeGen.h new file mode 100644 index 00000000000..7f8b0200df3 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CodeGen.h @@ -0,0 +1,147 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/CodeGen.h - CodeGen Concepts ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file define some types which define code generation concepts. For +// example, relocation model. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CODEGEN_H +#define LLVM_SUPPORT_CODEGEN_H + +#include <cstdint> +#include <optional> + +namespace llvm { + + // Relocation model types. + namespace Reloc { + // Cannot be named PIC due to collision with -DPIC + enum Model { Static, PIC_, DynamicNoPIC, ROPI, RWPI, ROPI_RWPI }; + } + + // Code model types. + namespace CodeModel { + // Sync changes with CodeGenCWrappers.h. + enum Model { Tiny, Small, Kernel, Medium, Large }; + } + + namespace PICLevel { + // This is used to map -fpic/-fPIC. + enum Level { NotPIC=0, SmallPIC=1, BigPIC=2 }; + } + + namespace PIELevel { + enum Level { Default=0, Small=1, Large=2 }; + } + + // TLS models. + namespace TLSModel { + enum Model { + GeneralDynamic, + LocalDynamic, + InitialExec, + LocalExec + }; + } + + namespace CodeGenOpt { + /// Type for the unique integer IDs of code generation optimization levels. + using IDType = int; + /// Code generation optimization level. + enum Level : IDType { + None = 0, ///< -O0 + Less = 1, ///< -O1 + Default = 2, ///< -O2, -Os + Aggressive = 3 ///< -O3 + }; + /// Get the \c Level identified by the integer \p ID. + /// + /// Returns std::nullopt if \p ID is invalid. + inline std::optional<Level> getLevel(IDType ID) { + if (ID < 0 || ID > 3) + return std::nullopt; + return static_cast<Level>(ID); + } + /// Parse \p C as a single digit integer ID and get matching \c Level. + /// + /// Returns std::nullopt if the input is not a valid digit or not a valid ID. + inline std::optional<Level> parseLevel(char C) { + if (C < '0') + return std::nullopt; + return getLevel(static_cast<IDType>(C - '0')); + } + } // namespace CodeGenOpt + + /// These enums are meant to be passed into addPassesToEmitFile to indicate + /// what type of file to emit, and returned by it to indicate what type of + /// file could actually be made. + enum CodeGenFileType { + CGFT_AssemblyFile, + CGFT_ObjectFile, + CGFT_Null // Do not emit any output. + }; + + // Specify what functions should keep the frame pointer. + enum class FramePointerKind { None, NonLeaf, All }; + + // Specify what type of zeroing callee-used registers. + namespace ZeroCallUsedRegs { + const unsigned ONLY_USED = 1U << 1; + const unsigned ONLY_GPR = 1U << 2; + const unsigned ONLY_ARG = 1U << 3; + + enum class ZeroCallUsedRegsKind : unsigned int { + // Don't zero any call-used regs. + Skip = 1U << 0, + // Only zeros call-used GPRs used in the fn and pass args. + UsedGPRArg = ONLY_USED | ONLY_GPR | ONLY_ARG, + // Only zeros call-used GPRs used in the fn. + UsedGPR = ONLY_USED | ONLY_GPR, + // Only zeros call-used regs used in the fn and pass args. + UsedArg = ONLY_USED | ONLY_ARG, + // Only zeros call-used regs used in the fn. + Used = ONLY_USED, + // Zeros all call-used GPRs that pass args. + AllGPRArg = ONLY_GPR | ONLY_ARG, + // Zeros all call-used GPRs. + AllGPR = ONLY_GPR, + // Zeros all call-used regs that pass args. + AllArg = ONLY_ARG, + // Zeros all call-used regs. + All = 0, + }; + } // namespace ZeroCallUsedRegs + + enum class UWTableKind { + None = 0, ///< No unwind table requested + Sync = 1, ///< "Synchronous" unwind tables + Async = 2, ///< "Asynchronous" unwind tables (instr precise) + Default = 2, + }; + + enum class FunctionReturnThunksKind : unsigned int { + Keep = 0, ///< No function return thunk. + Extern = 1, ///< Replace returns with jump to thunk, don't emit thunk. + Invalid = 2, ///< Not used. + }; + + } // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CodeGenCoverage.h b/contrib/libs/llvm16/include/llvm/Support/CodeGenCoverage.h new file mode 100644 index 00000000000..51173d3edb9 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CodeGenCoverage.h @@ -0,0 +1,49 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//== llvm/Support/CodeGenCoverage.h ------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \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 MemoryBuffer; + +class CodeGenCoverage { +protected: + BitVector RuleCoverage; + +public: + using const_covered_iterator = BitVector::const_set_bits_iterator; + + CodeGenCoverage(); + + void setCovered(uint64_t RuleID); + bool isCovered(uint64_t RuleID) const; + iterator_range<const_covered_iterator> covered() const; + + bool parse(MemoryBuffer &Buffer, StringRef BackendName); + bool emit(StringRef FilePrefix, StringRef BackendName) const; + void reset(); +}; +} // namespace llvm + +#endif // LLVM_SUPPORT_CODEGENCOVERAGE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CommandLine.h b/contrib/libs/llvm16/include/llvm/Support/CommandLine.h new file mode 100644 index 00000000000..03ff0a5235e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CommandLine.h @@ -0,0 +1,2282 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/CommandLine.h - Command line handler --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This class implements a command line argument processor that is useful when +// creating a tool. It provides a simple, minimalistic interface that is easily +// extensible and supports nonlocal (library) command line options. +// +// Note that rather than trying to figure out what this code does, you should +// read the library documentation located in docs/CommandLine.html or looks at +// the many example usages in tools/*/*.cpp +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_COMMANDLINE_H +#define LLVM_SUPPORT_COMMANDLINE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <climits> +#include <cstddef> +#include <functional> +#include <initializer_list> +#include <string> +#include <type_traits> +#include <vector> + +namespace llvm { + +namespace vfs { +class FileSystem; +} + +class StringSaver; + +/// This namespace contains all of the command line option processing machinery. +/// It is intentionally a short name to make qualified usage concise. +namespace cl { + +//===----------------------------------------------------------------------===// +// Command line option processing entry point. +// +// Returns true on success. Otherwise, this will print the error message to +// stderr and exit if \p Errs is not set (nullptr by default), or print the +// error message to \p Errs and return false if \p Errs is provided. +// +// If EnvVar is not nullptr, command-line options are also parsed from the +// environment variable named by EnvVar. Precedence is given to occurrences +// from argv. This precedence is currently implemented by parsing argv after +// the environment variable, so it is only implemented correctly for options +// that give precedence to later occurrences. If your program supports options +// that give precedence to earlier occurrences, you will need to extend this +// function to support it correctly. +bool ParseCommandLineOptions(int argc, const char *const *argv, + StringRef Overview = "", + raw_ostream *Errs = nullptr, + const char *EnvVar = nullptr, + bool LongOptionsUseDoubleDash = false); + +// Function pointer type for printing version information. +using VersionPrinterTy = std::function<void(raw_ostream &)>; + +///===---------------------------------------------------------------------===// +/// 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(VersionPrinterTy func); + +///===---------------------------------------------------------------------===// +/// Add an extra printer to use in addition to the default one. This can be +/// called multiple times, and each time it adds a new function to the list +/// which will be called after the basic LLVM version printing is complete. +/// Each can then add additional information specific to the tool. +void AddExtraVersionPrinter(VersionPrinterTy func); + +// Print option values. +// With -print-options print the difference between option values and defaults. +// With -print-all-options print all option values. +// (Currently not perfect, but best-effort.) +void PrintOptionValues(); + +// Forward declaration - AddLiteralOption needs to be up here to make gcc happy. +class Option; + +/// Adds a new option for parsing and provides the option it refers to. +/// +/// \param O pointer to the option +/// \param Name the string name for the option to handle during parsing +/// +/// Literal options are used by some parsers to register special option values. +/// This is how the PassNameParser registers pass names for opt. +void AddLiteralOption(Option &O, StringRef Name); + +//===----------------------------------------------------------------------===// +// Flags permitted to be passed to command line arguments +// + +enum NumOccurrencesFlag { // Flags for the number of occurrences allowed + Optional = 0x00, // Zero or One occurrence + ZeroOrMore = 0x01, // Zero or more occurrences allowed + Required = 0x02, // One occurrence required + OneOrMore = 0x03, // One or more occurrences required + + // Indicates that this option is fed anything that follows the last positional + // argument required by the application (it is an error if there are zero + // positional arguments, and a ConsumeAfter option is used). + // Thus, for example, all arguments to LLI are processed until a filename is + // found. Once a filename is found, all of the succeeding arguments are + // passed, unprocessed, to the ConsumeAfter option. + // + ConsumeAfter = 0x04 +}; + +enum ValueExpected { // Is a value required for the option? + // zero reserved for the unspecified value + ValueOptional = 0x01, // The value can appear... or not + ValueRequired = 0x02, // The value is required to appear! + ValueDisallowed = 0x03 // A value may not be specified (for flags) +}; + +enum OptionHidden { // Control whether -help shows this option + NotHidden = 0x00, // Option included in -help & -help-hidden + Hidden = 0x01, // -help doesn't, but -help-hidden does + ReallyHidden = 0x02 // Neither -help nor -help-hidden show this arg +}; + +// This controls special features that the option might have that cause it to be +// parsed differently... +// +// Prefix - This option allows arguments that are otherwise unrecognized to be +// matched by options that are a prefix of the actual value. This is useful for +// cases like a linker, where options are typically of the form '-lfoo' or +// '-L../../include' where -l or -L are the actual flags. When prefix is +// enabled, and used, the value for the flag comes from the suffix of the +// argument. +// +// AlwaysPrefix - Only allow the behavior enabled by the Prefix flag and reject +// the Option=Value form. +// + +enum FormattingFlags { + NormalFormatting = 0x00, // Nothing special + Positional = 0x01, // Is a positional argument, no '-' required + Prefix = 0x02, // Can this option directly prefix its value? + AlwaysPrefix = 0x03 // Can this option only directly prefix its value? +}; + +enum MiscFlags { // Miscellaneous flags to adjust argument + CommaSeparated = 0x01, // Should this cl::list split between commas? + PositionalEatsArgs = 0x02, // Should this positional cl::list eat -args? + Sink = 0x04, // Should this cl::list eat all unknown options? + + // Can this option group with other options? + // If this is enabled, multiple letter options are allowed to bunch together + // with only a single hyphen for the whole group. This allows emulation + // of the behavior that ls uses for example: ls -la === ls -l -a + Grouping = 0x08, + + // Default option + DefaultOption = 0x10 +}; + +//===----------------------------------------------------------------------===// +// +class OptionCategory { +private: + StringRef const Name; + StringRef const Description; + + void registerCategory(); + +public: + OptionCategory(StringRef const Name, + StringRef const Description = "") + : Name(Name), Description(Description) { + registerCategory(); + } + + StringRef getName() const { return Name; } + StringRef getDescription() const { return Description; } +}; + +// The general Option Category (used as default category). +OptionCategory &getGeneralCategory(); + +//===----------------------------------------------------------------------===// +// +class SubCommand { +private: + StringRef Name; + StringRef Description; + +protected: + void registerSubCommand(); + void unregisterSubCommand(); + +public: + SubCommand(StringRef Name, StringRef Description = "") + : Name(Name), Description(Description) { + registerSubCommand(); + } + SubCommand() = default; + + // Get the special subcommand representing no subcommand. + static SubCommand &getTopLevel(); + + // Get the special subcommand that can be used to put an option into all + // subcomands. + static SubCommand &getAll(); + + void reset(); + + explicit operator bool() const; + + StringRef getName() const { return Name; } + StringRef getDescription() const { return Description; } + + SmallVector<Option *, 4> PositionalOpts; + SmallVector<Option *, 4> SinkOpts; + StringMap<Option *> OptionsMap; + + Option *ConsumeAfterOpt = nullptr; // The ConsumeAfter option if it exists. +}; + +// A special subcommand representing no subcommand +extern ManagedStatic<SubCommand> TopLevelSubCommand; + +// A special subcommand that can be used to put an option into all subcommands. +extern ManagedStatic<SubCommand> AllSubCommands; + +//===----------------------------------------------------------------------===// +// +class Option { + friend class alias; + + // Overriden by subclasses to handle the value passed into an argument. Should + // return true if there was an error processing the argument and the program + // should exit. + // + virtual bool handleOccurrence(unsigned pos, StringRef ArgName, + StringRef Arg) = 0; + + virtual enum ValueExpected getValueExpectedFlagDefault() const { + return ValueOptional; + } + + // Out of line virtual function to provide home for the class. + virtual void anchor(); + + uint16_t NumOccurrences; // The number of times specified + // Occurrences, HiddenFlag, and Formatting are all enum types but to avoid + // problems with signed enums in bitfields. + uint16_t Occurrences : 3; // enum NumOccurrencesFlag + // not using the enum type for 'Value' because zero is an implementation + // detail representing the non-value + uint16_t Value : 2; + uint16_t HiddenFlag : 2; // enum OptionHidden + uint16_t Formatting : 2; // enum FormattingFlags + uint16_t Misc : 5; + uint16_t FullyInitialized : 1; // Has addArgument been called? + uint16_t Position; // Position of last occurrence of the option + uint16_t AdditionalVals; // Greater than 0 for multi-valued option. + +public: + StringRef ArgStr; // The argument string itself (ex: "help", "o") + StringRef HelpStr; // The descriptive text message for -help + StringRef ValueStr; // String describing what the value of this option is + SmallVector<OptionCategory *, 1> + Categories; // The Categories this option belongs to + SmallPtrSet<SubCommand *, 1> Subs; // The subcommands this option belongs to. + + inline enum NumOccurrencesFlag getNumOccurrencesFlag() const { + return (enum NumOccurrencesFlag)Occurrences; + } + + inline enum ValueExpected getValueExpectedFlag() const { + return Value ? ((enum ValueExpected)Value) : getValueExpectedFlagDefault(); + } + + inline enum OptionHidden getOptionHiddenFlag() const { + return (enum OptionHidden)HiddenFlag; + } + + inline enum FormattingFlags getFormattingFlag() const { + return (enum FormattingFlags)Formatting; + } + + inline unsigned getMiscFlags() const { return Misc; } + inline unsigned getPosition() const { return Position; } + inline unsigned getNumAdditionalVals() const { return AdditionalVals; } + + // Return true if the argstr != "" + bool hasArgStr() const { return !ArgStr.empty(); } + bool isPositional() const { return getFormattingFlag() == cl::Positional; } + bool isSink() const { return getMiscFlags() & cl::Sink; } + bool isDefaultOption() const { return getMiscFlags() & cl::DefaultOption; } + + bool isConsumeAfter() const { + return getNumOccurrencesFlag() == cl::ConsumeAfter; + } + + bool isInAllSubCommands() const { + return llvm::is_contained(Subs, &SubCommand::getAll()); + } + + //-------------------------------------------------------------------------=== + // Accessor functions set by OptionModifiers + // + void setArgStr(StringRef S); + void setDescription(StringRef S) { HelpStr = S; } + void setValueStr(StringRef S) { ValueStr = S; } + void setNumOccurrencesFlag(enum NumOccurrencesFlag Val) { Occurrences = Val; } + void setValueExpectedFlag(enum ValueExpected Val) { Value = Val; } + void setHiddenFlag(enum OptionHidden Val) { HiddenFlag = Val; } + void setFormattingFlag(enum FormattingFlags V) { Formatting = V; } + void setMiscFlag(enum MiscFlags M) { Misc |= M; } + void setPosition(unsigned pos) { Position = pos; } + void addCategory(OptionCategory &C); + void addSubCommand(SubCommand &S) { Subs.insert(&S); } + +protected: + explicit Option(enum NumOccurrencesFlag OccurrencesFlag, + enum OptionHidden Hidden) + : NumOccurrences(0), Occurrences(OccurrencesFlag), Value(0), + HiddenFlag(Hidden), Formatting(NormalFormatting), Misc(0), + FullyInitialized(false), Position(0), AdditionalVals(0) { + Categories.push_back(&getGeneralCategory()); + } + + inline void setNumAdditionalVals(unsigned n) { AdditionalVals = n; } + +public: + virtual ~Option() = default; + + // Register this argument with the commandline system. + // + void addArgument(); + + /// Unregisters this option from the CommandLine system. + /// + /// This option must have been the last option registered. + /// For testing purposes only. + void removeArgument(); + + // Return the width of the option tag for printing... + virtual size_t getOptionWidth() const = 0; + + // Print out information about this option. The to-be-maintained width is + // specified. + // + virtual void printOptionInfo(size_t GlobalWidth) const = 0; + + virtual void printOptionValue(size_t GlobalWidth, bool Force) const = 0; + + virtual void setDefault() = 0; + + // Prints the help string for an option. + // + // This maintains the Indent for multi-line descriptions. + // FirstLineIndentedBy is the count of chars of the first line + // i.e. the one containing the --<option name>. + static void printHelpStr(StringRef HelpStr, size_t Indent, + size_t FirstLineIndentedBy); + + // Prints the help string for an enum value. + // + // This maintains the Indent for multi-line descriptions. + // FirstLineIndentedBy is the count of chars of the first line + // i.e. the one containing the =<value>. + static void printEnumValHelpStr(StringRef HelpStr, size_t Indent, + size_t FirstLineIndentedBy); + + virtual void getExtraOptionNames(SmallVectorImpl<StringRef> &) {} + + // Wrapper around handleOccurrence that enforces Flags. + // + virtual bool addOccurrence(unsigned pos, StringRef ArgName, StringRef Value, + bool MultiArg = false); + + // Prints option name followed by message. Always returns true. + bool error(const Twine &Message, StringRef ArgName = StringRef(), raw_ostream &Errs = llvm::errs()); + bool error(const Twine &Message, raw_ostream &Errs) { + return error(Message, StringRef(), Errs); + } + + inline int getNumOccurrences() const { return NumOccurrences; } + void reset(); +}; + +//===----------------------------------------------------------------------===// +// Command line option modifiers that can be used to modify the behavior of +// command line option parsers... +// + +// Modifier to set the description shown in the -help output... +struct desc { + StringRef Desc; + + desc(StringRef Str) : Desc(Str) {} + + void apply(Option &O) const { O.setDescription(Desc); } +}; + +// Modifier to set the value description shown in the -help output... +struct value_desc { + StringRef Desc; + + value_desc(StringRef Str) : Desc(Str) {} + + void apply(Option &O) const { O.setValueStr(Desc); } +}; + +// Specify a default (initial) value for the command line argument, if the +// default constructor for the argument type does not give you what you want. +// This is only valid on "opt" arguments, not on "list" arguments. +template <class Ty> struct initializer { + const Ty &Init; + initializer(const Ty &Val) : Init(Val) {} + + template <class Opt> void apply(Opt &O) const { O.setInitialValue(Init); } +}; + +template <class Ty> struct list_initializer { + ArrayRef<Ty> Inits; + list_initializer(ArrayRef<Ty> Vals) : Inits(Vals) {} + + template <class Opt> void apply(Opt &O) const { O.setInitialValues(Inits); } +}; + +template <class Ty> initializer<Ty> init(const Ty &Val) { + return initializer<Ty>(Val); +} + +template <class Ty> +list_initializer<Ty> list_init(ArrayRef<Ty> Vals) { + return list_initializer<Ty>(Vals); +} + +// Allow the user to specify which external variable they want to store the +// results of the command line argument processing into, if they don't want to +// store it in the option itself. +template <class Ty> struct LocationClass { + Ty &Loc; + + LocationClass(Ty &L) : Loc(L) {} + + template <class Opt> void apply(Opt &O) const { O.setLocation(O, Loc); } +}; + +template <class Ty> LocationClass<Ty> location(Ty &L) { + return LocationClass<Ty>(L); +} + +// Specify the Option category for the command line argument to belong to. +struct cat { + OptionCategory &Category; + + cat(OptionCategory &c) : Category(c) {} + + template <class Opt> void apply(Opt &O) const { O.addCategory(Category); } +}; + +// Specify the subcommand that this option belongs to. +struct sub { + SubCommand ⋐ + + sub(SubCommand &S) : Sub(S) {} + + template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); } +}; + +// Specify a callback function to be called when an option is seen. +// Can be used to set other options automatically. +template <typename R, typename Ty> struct cb { + std::function<R(Ty)> CB; + + cb(std::function<R(Ty)> CB) : CB(CB) {} + + template <typename Opt> void apply(Opt &O) const { O.setCallback(CB); } +}; + +namespace detail { +template <typename F> +struct callback_traits : public callback_traits<decltype(&F::operator())> {}; + +template <typename R, typename C, typename... Args> +struct callback_traits<R (C::*)(Args...) const> { + using result_type = R; + using arg_type = std::tuple_element_t<0, std::tuple<Args...>>; + static_assert(sizeof...(Args) == 1, "callback function must have one and only one parameter"); + static_assert(std::is_same<result_type, void>::value, + "callback return type must be void"); + static_assert(std::is_lvalue_reference<arg_type>::value && + std::is_const<std::remove_reference_t<arg_type>>::value, + "callback arg_type must be a const lvalue reference"); +}; +} // namespace detail + +template <typename F> +cb<typename detail::callback_traits<F>::result_type, + typename detail::callback_traits<F>::arg_type> +callback(F CB) { + using result_type = typename detail::callback_traits<F>::result_type; + using arg_type = typename detail::callback_traits<F>::arg_type; + return cb<result_type, arg_type>(CB); +} + +//===----------------------------------------------------------------------===// + +// Support value comparison outside the template. +struct GenericOptionValue { + virtual bool compare(const GenericOptionValue &V) const = 0; + +protected: + GenericOptionValue() = default; + GenericOptionValue(const GenericOptionValue&) = default; + GenericOptionValue &operator=(const GenericOptionValue &) = default; + ~GenericOptionValue() = default; + +private: + virtual void anchor(); +}; + +template <class DataType> struct OptionValue; + +// The default value safely does nothing. Option value printing is only +// best-effort. +template <class DataType, bool isClass> +struct OptionValueBase : public GenericOptionValue { + // Temporary storage for argument passing. + using WrapperType = OptionValue<DataType>; + + bool hasValue() const { return false; } + + const DataType &getValue() const { llvm_unreachable("no default value"); } + + // Some options may take their value from a different data type. + template <class DT> void setValue(const DT & /*V*/) {} + + bool compare(const DataType & /*V*/) const { return false; } + + bool compare(const GenericOptionValue & /*V*/) const override { + return false; + } + +protected: + ~OptionValueBase() = default; +}; + +// Simple copy of the option value. +template <class DataType> class OptionValueCopy : public GenericOptionValue { + DataType Value; + bool Valid = false; + +protected: + OptionValueCopy(const OptionValueCopy&) = default; + OptionValueCopy &operator=(const OptionValueCopy &) = default; + ~OptionValueCopy() = default; + +public: + OptionValueCopy() = default; + + bool hasValue() const { return Valid; } + + const DataType &getValue() const { + assert(Valid && "invalid option value"); + return Value; + } + + void setValue(const DataType &V) { + Valid = true; + Value = V; + } + + bool compare(const DataType &V) const { return Valid && (Value != V); } + + bool compare(const GenericOptionValue &V) const override { + const OptionValueCopy<DataType> &VC = + static_cast<const OptionValueCopy<DataType> &>(V); + if (!VC.hasValue()) + return false; + return compare(VC.getValue()); + } +}; + +// Non-class option values. +template <class DataType> +struct OptionValueBase<DataType, false> : OptionValueCopy<DataType> { + using WrapperType = DataType; + +protected: + OptionValueBase() = default; + OptionValueBase(const OptionValueBase&) = default; + OptionValueBase &operator=(const OptionValueBase &) = default; + ~OptionValueBase() = default; +}; + +// Top-level option class. +template <class DataType> +struct OptionValue final + : OptionValueBase<DataType, std::is_class<DataType>::value> { + OptionValue() = default; + + OptionValue(const DataType &V) { this->setValue(V); } + + // Some options may take their value from a different data type. + template <class DT> OptionValue<DataType> &operator=(const DT &V) { + this->setValue(V); + return *this; + } +}; + +// Other safe-to-copy-by-value common option types. +enum boolOrDefault { BOU_UNSET, BOU_TRUE, BOU_FALSE }; +template <> +struct OptionValue<cl::boolOrDefault> final + : OptionValueCopy<cl::boolOrDefault> { + using WrapperType = cl::boolOrDefault; + + OptionValue() = default; + + OptionValue(const cl::boolOrDefault &V) { this->setValue(V); } + + OptionValue<cl::boolOrDefault> &operator=(const cl::boolOrDefault &V) { + setValue(V); + return *this; + } + +private: + void anchor() override; +}; + +template <> +struct OptionValue<std::string> final : OptionValueCopy<std::string> { + using WrapperType = StringRef; + + OptionValue() = default; + + OptionValue(const std::string &V) { this->setValue(V); } + + OptionValue<std::string> &operator=(const std::string &V) { + setValue(V); + return *this; + } + +private: + void anchor() override; +}; + +//===----------------------------------------------------------------------===// +// Enum valued command line option +// + +// This represents a single enum value, using "int" as the underlying type. +struct OptionEnumValue { + StringRef Name; + int Value; + StringRef Description; +}; + +#define clEnumVal(ENUMVAL, DESC) \ + llvm::cl::OptionEnumValue { #ENUMVAL, int(ENUMVAL), DESC } +#define clEnumValN(ENUMVAL, FLAGNAME, DESC) \ + llvm::cl::OptionEnumValue { FLAGNAME, int(ENUMVAL), DESC } + +// For custom data types, allow specifying a group of values together as the +// values that go into the mapping that the option handler uses. +// +class ValuesClass { + // Use a vector instead of a map, because the lists should be short, + // the overhead is less, and most importantly, it keeps them in the order + // inserted so we can print our option out nicely. + SmallVector<OptionEnumValue, 4> Values; + +public: + ValuesClass(std::initializer_list<OptionEnumValue> Options) + : Values(Options) {} + + template <class Opt> void apply(Opt &O) const { + for (const auto &Value : Values) + O.getParser().addLiteralOption(Value.Name, Value.Value, + Value.Description); + } +}; + +/// Helper to build a ValuesClass by forwarding a variable number of arguments +/// as an initializer list to the ValuesClass constructor. +template <typename... OptsTy> ValuesClass values(OptsTy... Options) { + return ValuesClass({Options...}); +} + +//===----------------------------------------------------------------------===// +// Parameterizable parser for different data types. By default, known data types +// (string, int, bool) have specialized parsers, that do what you would expect. +// The default parser, used for data types that are not built-in, uses a mapping +// table to map specific options to values, which is used, among other things, +// to handle enum types. + +//-------------------------------------------------- +// This class holds all the non-generic code that we do not need replicated for +// every instance of the generic parser. This also allows us to put stuff into +// CommandLine.cpp +// +class generic_parser_base { +protected: + class GenericOptionInfo { + public: + GenericOptionInfo(StringRef name, StringRef helpStr) + : Name(name), HelpStr(helpStr) {} + StringRef Name; + StringRef HelpStr; + }; + +public: + generic_parser_base(Option &O) : Owner(O) {} + + virtual ~generic_parser_base() = default; + // Base class should have virtual-destructor + + // Virtual function implemented by generic subclass to indicate how many + // entries are in Values. + // + virtual unsigned getNumOptions() const = 0; + + // Return option name N. + virtual StringRef getOption(unsigned N) const = 0; + + // Return description N + virtual StringRef getDescription(unsigned N) const = 0; + + // Return the width of the option tag for printing... + virtual size_t getOptionWidth(const Option &O) const; + + virtual const GenericOptionValue &getOptionValue(unsigned N) const = 0; + + // Print out information about this option. The to-be-maintained width is + // specified. + // + virtual void printOptionInfo(const Option &O, size_t GlobalWidth) const; + + void printGenericOptionDiff(const Option &O, const GenericOptionValue &V, + const GenericOptionValue &Default, + size_t GlobalWidth) const; + + // Print the value of an option and it's default. + // + // Template definition ensures that the option and default have the same + // DataType (via the same AnyOptionValue). + template <class AnyOptionValue> + void printOptionDiff(const Option &O, const AnyOptionValue &V, + const AnyOptionValue &Default, + size_t GlobalWidth) const { + printGenericOptionDiff(O, V, Default, GlobalWidth); + } + + void initialize() {} + + void getExtraOptionNames(SmallVectorImpl<StringRef> &OptionNames) { + // If there has been no argstr specified, that means that we need to add an + // argument for every possible option. This ensures that our options are + // vectored to us. + if (!Owner.hasArgStr()) + for (unsigned i = 0, e = getNumOptions(); i != e; ++i) + OptionNames.push_back(getOption(i)); + } + + enum ValueExpected getValueExpectedFlagDefault() const { + // If there is an ArgStr specified, then we are of the form: + // + // -opt=O2 or -opt O2 or -optO2 + // + // In which case, the value is required. Otherwise if an arg str has not + // been specified, we are of the form: + // + // -O2 or O2 or -la (where -l and -a are separate options) + // + // If this is the case, we cannot allow a value. + // + if (Owner.hasArgStr()) + return ValueRequired; + else + return ValueDisallowed; + } + + // Return the option number corresponding to the specified + // argument string. If the option is not found, getNumOptions() is returned. + // + unsigned findOption(StringRef Name); + +protected: + Option &Owner; +}; + +// Default parser implementation - This implementation depends on having a +// mapping of recognized options to values of some sort. In addition to this, +// each entry in the mapping also tracks a help message that is printed with the +// command line option for -help. Because this is a simple mapping parser, the +// data type can be any unsupported type. +// +template <class DataType> class parser : public generic_parser_base { +protected: + class OptionInfo : public GenericOptionInfo { + public: + OptionInfo(StringRef name, DataType v, StringRef helpStr) + : GenericOptionInfo(name, helpStr), V(v) {} + + OptionValue<DataType> V; + }; + SmallVector<OptionInfo, 8> Values; + +public: + parser(Option &O) : generic_parser_base(O) {} + + using parser_data_type = DataType; + + // Implement virtual functions needed by generic_parser_base + unsigned getNumOptions() const override { return unsigned(Values.size()); } + StringRef getOption(unsigned N) const override { return Values[N].Name; } + StringRef getDescription(unsigned N) const override { + return Values[N].HelpStr; + } + + // Return the value of option name N. + const GenericOptionValue &getOptionValue(unsigned N) const override { + return Values[N].V; + } + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, DataType &V) { + StringRef ArgVal; + if (Owner.hasArgStr()) + ArgVal = Arg; + else + ArgVal = ArgName; + + for (size_t i = 0, e = Values.size(); i != e; ++i) + if (Values[i].Name == ArgVal) { + V = Values[i].V.getValue(); + return false; + } + + return O.error("Cannot find option named '" + ArgVal + "'!"); + } + + /// Add an entry to the mapping table. + /// + template <class DT> + void addLiteralOption(StringRef Name, const DT &V, StringRef HelpStr) { + assert(findOption(Name) == Values.size() && "Option already exists!"); + OptionInfo X(Name, static_cast<DataType>(V), HelpStr); + Values.push_back(X); + AddLiteralOption(Owner, Name); + } + + /// Remove the specified option. + /// + void removeLiteralOption(StringRef Name) { + unsigned N = findOption(Name); + assert(N != Values.size() && "Option not found!"); + Values.erase(Values.begin() + N); + } +}; + +//-------------------------------------------------- +// Super class of parsers to provide boilerplate code +// +class basic_parser_impl { // non-template implementation of basic_parser<t> +public: + basic_parser_impl(Option &) {} + + virtual ~basic_parser_impl() = default; + + enum ValueExpected getValueExpectedFlagDefault() const { + return ValueRequired; + } + + void getExtraOptionNames(SmallVectorImpl<StringRef> &) {} + + void initialize() {} + + // Return the width of the option tag for printing... + size_t getOptionWidth(const Option &O) const; + + // Print out information about this option. The to-be-maintained width is + // specified. + // + void printOptionInfo(const Option &O, size_t GlobalWidth) const; + + // Print a placeholder for options that don't yet support printOptionDiff(). + void printOptionNoValue(const Option &O, size_t GlobalWidth) const; + + // Overload in subclass to provide a better default value. + virtual StringRef getValueName() const { return "value"; } + + // An out-of-line virtual method to provide a 'home' for this class. + virtual void anchor(); + +protected: + // A helper for basic_parser::printOptionDiff. + void printOptionName(const Option &O, size_t GlobalWidth) const; +}; + +// The real basic parser is just a template wrapper that provides a typedef for +// the provided data type. +// +template <class DataType> class basic_parser : public basic_parser_impl { +public: + using parser_data_type = DataType; + using OptVal = OptionValue<DataType>; + + basic_parser(Option &O) : basic_parser_impl(O) {} +}; + +//-------------------------------------------------- + +extern template class basic_parser<bool>; + +template <> class parser<bool> : public basic_parser<bool> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, bool &Val); + + void initialize() {} + + enum ValueExpected getValueExpectedFlagDefault() const { + return ValueOptional; + } + + // Do not print =<value> at all. + StringRef getValueName() const override { return StringRef(); } + + void printOptionDiff(const Option &O, bool V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<boolOrDefault>; + +template <> class parser<boolOrDefault> : public basic_parser<boolOrDefault> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, boolOrDefault &Val); + + enum ValueExpected getValueExpectedFlagDefault() const { + return ValueOptional; + } + + // Do not print =<value> at all. + StringRef getValueName() const override { return StringRef(); } + + void printOptionDiff(const Option &O, boolOrDefault V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<int>; + +template <> class parser<int> : public basic_parser<int> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, int &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "int"; } + + void printOptionDiff(const Option &O, int V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<long>; + +template <> class parser<long> final : public basic_parser<long> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, long &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "long"; } + + void printOptionDiff(const Option &O, long V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<long long>; + +template <> class parser<long long> : public basic_parser<long long> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, long long &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "long"; } + + void printOptionDiff(const Option &O, long long V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<unsigned>; + +template <> class parser<unsigned> : public basic_parser<unsigned> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, unsigned &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "uint"; } + + void printOptionDiff(const Option &O, unsigned V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<unsigned long>; + +template <> +class parser<unsigned long> final : public basic_parser<unsigned long> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, unsigned long &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "ulong"; } + + void printOptionDiff(const Option &O, unsigned long V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<unsigned long long>; + +template <> +class parser<unsigned long long> : public basic_parser<unsigned long long> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, + unsigned long long &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "ulong"; } + + void printOptionDiff(const Option &O, unsigned long long V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<double>; + +template <> class parser<double> : public basic_parser<double> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, double &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "number"; } + + void printOptionDiff(const Option &O, double V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<float>; + +template <> class parser<float> : public basic_parser<float> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, float &Val); + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "number"; } + + void printOptionDiff(const Option &O, float V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<std::string>; + +template <> class parser<std::string> : public basic_parser<std::string> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &, StringRef, StringRef Arg, std::string &Value) { + Value = Arg.str(); + return false; + } + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "string"; } + + void printOptionDiff(const Option &O, StringRef V, const OptVal &Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- + +extern template class basic_parser<char>; + +template <> class parser<char> : public basic_parser<char> { +public: + parser(Option &O) : basic_parser(O) {} + + // Return true on error. + bool parse(Option &, StringRef, StringRef Arg, char &Value) { + Value = Arg[0]; + return false; + } + + // Overload in subclass to provide a better default value. + StringRef getValueName() const override { return "char"; } + + void printOptionDiff(const Option &O, char V, OptVal Default, + size_t GlobalWidth) const; + + // An out-of-line virtual method to provide a 'home' for this class. + void anchor() override; +}; + +//-------------------------------------------------- +// This collection of wrappers is the intermediary between class opt and class +// parser to handle all the template nastiness. + +// This overloaded function is selected by the generic parser. +template <class ParserClass, class DT> +void printOptionDiff(const Option &O, const generic_parser_base &P, const DT &V, + const OptionValue<DT> &Default, size_t GlobalWidth) { + OptionValue<DT> OV = V; + P.printOptionDiff(O, OV, Default, GlobalWidth); +} + +// This is instantiated for basic parsers when the parsed value has a different +// type than the option value. e.g. HelpPrinter. +template <class ParserDT, class ValDT> struct OptionDiffPrinter { + void print(const Option &O, const parser<ParserDT> &P, const ValDT & /*V*/, + const OptionValue<ValDT> & /*Default*/, size_t GlobalWidth) { + P.printOptionNoValue(O, GlobalWidth); + } +}; + +// This is instantiated for basic parsers when the parsed value has the same +// type as the option value. +template <class DT> struct OptionDiffPrinter<DT, DT> { + void print(const Option &O, const parser<DT> &P, const DT &V, + const OptionValue<DT> &Default, size_t GlobalWidth) { + P.printOptionDiff(O, V, Default, GlobalWidth); + } +}; + +// This overloaded function is selected by the basic parser, which may parse a +// different type than the option type. +template <class ParserClass, class ValDT> +void printOptionDiff( + const Option &O, + const basic_parser<typename ParserClass::parser_data_type> &P, + const ValDT &V, const OptionValue<ValDT> &Default, size_t GlobalWidth) { + + OptionDiffPrinter<typename ParserClass::parser_data_type, ValDT> printer; + printer.print(O, static_cast<const ParserClass &>(P), V, Default, + GlobalWidth); +} + +//===----------------------------------------------------------------------===// +// This class is used because we must use partial specialization to handle +// literal string arguments specially (const char* does not correctly respond to +// the apply method). Because the syntax to use this is a pain, we have the +// 'apply' method below to handle the nastiness... +// +template <class Mod> struct applicator { + template <class Opt> static void opt(const Mod &M, Opt &O) { M.apply(O); } +}; + +// Handle const char* as a special case... +template <unsigned n> struct applicator<char[n]> { + template <class Opt> static void opt(StringRef Str, Opt &O) { + O.setArgStr(Str); + } +}; +template <unsigned n> struct applicator<const char[n]> { + template <class Opt> static void opt(StringRef Str, Opt &O) { + O.setArgStr(Str); + } +}; +template <> struct applicator<StringRef > { + template <class Opt> static void opt(StringRef Str, Opt &O) { + O.setArgStr(Str); + } +}; + +template <> struct applicator<NumOccurrencesFlag> { + static void opt(NumOccurrencesFlag N, Option &O) { + O.setNumOccurrencesFlag(N); + } +}; + +template <> struct applicator<ValueExpected> { + static void opt(ValueExpected VE, Option &O) { O.setValueExpectedFlag(VE); } +}; + +template <> struct applicator<OptionHidden> { + static void opt(OptionHidden OH, Option &O) { O.setHiddenFlag(OH); } +}; + +template <> struct applicator<FormattingFlags> { + static void opt(FormattingFlags FF, Option &O) { O.setFormattingFlag(FF); } +}; + +template <> struct applicator<MiscFlags> { + static void opt(MiscFlags MF, Option &O) { + assert((MF != Grouping || O.ArgStr.size() == 1) && + "cl::Grouping can only apply to single character Options."); + O.setMiscFlag(MF); + } +}; + +// Apply modifiers to an option in a type safe way. +template <class Opt, class Mod, class... Mods> +void apply(Opt *O, const Mod &M, const Mods &... Ms) { + applicator<Mod>::opt(M, *O); + apply(O, Ms...); +} + +template <class Opt, class Mod> void apply(Opt *O, const Mod &M) { + applicator<Mod>::opt(M, *O); +} + +//===----------------------------------------------------------------------===// +// Default storage class definition: external storage. This implementation +// assumes the user will specify a variable to store the data into with the +// cl::location(x) modifier. +// +template <class DataType, bool ExternalStorage, bool isClass> +class opt_storage { + DataType *Location = nullptr; // Where to store the object... + OptionValue<DataType> Default; + + void check_location() const { + assert(Location && "cl::location(...) not specified for a command " + "line option with external storage, " + "or cl::init specified before cl::location()!!"); + } + +public: + opt_storage() = default; + + bool setLocation(Option &O, DataType &L) { + if (Location) + return O.error("cl::location(x) specified more than once!"); + Location = &L; + Default = L; + return false; + } + + template <class T> void setValue(const T &V, bool initial = false) { + check_location(); + *Location = V; + if (initial) + Default = V; + } + + DataType &getValue() { + check_location(); + return *Location; + } + const DataType &getValue() const { + check_location(); + return *Location; + } + + operator DataType() const { return this->getValue(); } + + const OptionValue<DataType> &getDefault() const { return Default; } +}; + +// Define how to hold a class type object, such as a string. Since we can +// inherit from a class, we do so. This makes us exactly compatible with the +// object in all cases that it is used. +// +template <class DataType> +class opt_storage<DataType, false, true> : public DataType { +public: + OptionValue<DataType> Default; + + template <class T> void setValue(const T &V, bool initial = false) { + DataType::operator=(V); + if (initial) + Default = V; + } + + DataType &getValue() { return *this; } + const DataType &getValue() const { return *this; } + + const OptionValue<DataType> &getDefault() const { return Default; } +}; + +// Define a partial specialization to handle things we cannot inherit from. In +// this case, we store an instance through containment, and overload operators +// to get at the value. +// +template <class DataType> class opt_storage<DataType, false, false> { +public: + DataType Value; + OptionValue<DataType> Default; + + // Make sure we initialize the value with the default constructor for the + // type. + opt_storage() : Value(DataType()), Default() {} + + template <class T> void setValue(const T &V, bool initial = false) { + Value = V; + if (initial) + Default = V; + } + DataType &getValue() { return Value; } + DataType getValue() const { return Value; } + + const OptionValue<DataType> &getDefault() const { return Default; } + + operator DataType() const { return getValue(); } + + // If the datatype is a pointer, support -> on it. + DataType operator->() const { return Value; } +}; + +//===----------------------------------------------------------------------===// +// A scalar command line option. +// +template <class DataType, bool ExternalStorage = false, + class ParserClass = parser<DataType>> +class opt : public Option, + public opt_storage<DataType, ExternalStorage, + std::is_class<DataType>::value> { + ParserClass Parser; + + bool handleOccurrence(unsigned pos, StringRef ArgName, + StringRef Arg) override { + typename ParserClass::parser_data_type Val = + typename ParserClass::parser_data_type(); + if (Parser.parse(*this, ArgName, Arg, Val)) + return true; // Parse error! + this->setValue(Val); + this->setPosition(pos); + Callback(Val); + return false; + } + + enum ValueExpected getValueExpectedFlagDefault() const override { + return Parser.getValueExpectedFlagDefault(); + } + + void getExtraOptionNames(SmallVectorImpl<StringRef> &OptionNames) override { + return Parser.getExtraOptionNames(OptionNames); + } + + // Forward printing stuff to the parser... + size_t getOptionWidth() const override { + return Parser.getOptionWidth(*this); + } + + void printOptionInfo(size_t GlobalWidth) const override { + Parser.printOptionInfo(*this, GlobalWidth); + } + + void printOptionValue(size_t GlobalWidth, bool Force) const override { + if (Force || this->getDefault().compare(this->getValue())) { + cl::printOptionDiff<ParserClass>(*this, Parser, this->getValue(), + this->getDefault(), GlobalWidth); + } + } + + template <class T, + class = std::enable_if_t<std::is_assignable<T &, T>::value>> + void setDefaultImpl() { + const OptionValue<DataType> &V = this->getDefault(); + if (V.hasValue()) + this->setValue(V.getValue()); + else + this->setValue(T()); + } + + template <class T, + class = std::enable_if_t<!std::is_assignable<T &, T>::value>> + void setDefaultImpl(...) {} + + void setDefault() override { setDefaultImpl<DataType>(); } + + void done() { + addArgument(); + Parser.initialize(); + } + +public: + // Command line options should not be copyable + opt(const opt &) = delete; + opt &operator=(const opt &) = delete; + + // setInitialValue - Used by the cl::init modifier... + void setInitialValue(const DataType &V) { this->setValue(V, true); } + + ParserClass &getParser() { return Parser; } + + template <class T> DataType &operator=(const T &Val) { + this->setValue(Val); + Callback(Val); + return this->getValue(); + } + + template <class... Mods> + explicit opt(const Mods &... Ms) + : Option(llvm::cl::Optional, NotHidden), Parser(*this) { + apply(this, Ms...); + done(); + } + + void setCallback( + std::function<void(const typename ParserClass::parser_data_type &)> CB) { + Callback = CB; + } + + std::function<void(const typename ParserClass::parser_data_type &)> Callback = + [](const typename ParserClass::parser_data_type &) {}; +}; + +extern template class opt<unsigned>; +extern template class opt<int>; +extern template class opt<std::string>; +extern template class opt<char>; +extern template class opt<bool>; + +//===----------------------------------------------------------------------===// +// Default storage class definition: external storage. This implementation +// assumes the user will specify a variable to store the data into with the +// cl::location(x) modifier. +// +template <class DataType, class StorageClass> class list_storage { + StorageClass *Location = nullptr; // Where to store the object... + std::vector<OptionValue<DataType>> Default = + std::vector<OptionValue<DataType>>(); + bool DefaultAssigned = false; + +public: + list_storage() = default; + + void clear() {} + + bool setLocation(Option &O, StorageClass &L) { + if (Location) + return O.error("cl::location(x) specified more than once!"); + Location = &L; + return false; + } + + template <class T> void addValue(const T &V, bool initial = false) { + assert(Location != nullptr && + "cl::location(...) not specified for a command " + "line option with external storage!"); + Location->push_back(V); + if (initial) + Default.push_back(V); + } + + const std::vector<OptionValue<DataType>> &getDefault() const { + return Default; + } + + void assignDefault() { DefaultAssigned = true; } + void overwriteDefault() { DefaultAssigned = false; } + bool isDefaultAssigned() { return DefaultAssigned; } +}; + +// Define how to hold a class type object, such as a string. +// Originally this code inherited from std::vector. In transitioning to a new +// API for command line options we should change this. The new implementation +// of this list_storage specialization implements the minimum subset of the +// std::vector API required for all the current clients. +// +// FIXME: Reduce this API to a more narrow subset of std::vector +// +template <class DataType> class list_storage<DataType, bool> { + std::vector<DataType> Storage; + std::vector<OptionValue<DataType>> Default; + bool DefaultAssigned = false; + +public: + using iterator = typename std::vector<DataType>::iterator; + + iterator begin() { return Storage.begin(); } + iterator end() { return Storage.end(); } + + using const_iterator = typename std::vector<DataType>::const_iterator; + + const_iterator begin() const { return Storage.begin(); } + const_iterator end() const { return Storage.end(); } + + using size_type = typename std::vector<DataType>::size_type; + + size_type size() const { return Storage.size(); } + + bool empty() const { return Storage.empty(); } + + void push_back(const DataType &value) { Storage.push_back(value); } + void push_back(DataType &&value) { Storage.push_back(value); } + + using reference = typename std::vector<DataType>::reference; + using const_reference = typename std::vector<DataType>::const_reference; + + reference operator[](size_type pos) { return Storage[pos]; } + const_reference operator[](size_type pos) const { return Storage[pos]; } + + void clear() { + Storage.clear(); + } + + iterator erase(const_iterator pos) { return Storage.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { + return Storage.erase(first, last); + } + + iterator erase(iterator pos) { return Storage.erase(pos); } + iterator erase(iterator first, iterator last) { + return Storage.erase(first, last); + } + + iterator insert(const_iterator pos, const DataType &value) { + return Storage.insert(pos, value); + } + iterator insert(const_iterator pos, DataType &&value) { + return Storage.insert(pos, value); + } + + iterator insert(iterator pos, const DataType &value) { + return Storage.insert(pos, value); + } + iterator insert(iterator pos, DataType &&value) { + return Storage.insert(pos, value); + } + + reference front() { return Storage.front(); } + const_reference front() const { return Storage.front(); } + + operator std::vector<DataType> &() { return Storage; } + operator ArrayRef<DataType>() const { return Storage; } + std::vector<DataType> *operator&() { return &Storage; } + const std::vector<DataType> *operator&() const { return &Storage; } + + template <class T> void addValue(const T &V, bool initial = false) { + Storage.push_back(V); + if (initial) + Default.push_back(OptionValue<DataType>(V)); + } + + const std::vector<OptionValue<DataType>> &getDefault() const { + return Default; + } + + void assignDefault() { DefaultAssigned = true; } + void overwriteDefault() { DefaultAssigned = false; } + bool isDefaultAssigned() { return DefaultAssigned; } +}; + +//===----------------------------------------------------------------------===// +// A list of command line options. +// +template <class DataType, class StorageClass = bool, + class ParserClass = parser<DataType>> +class list : public Option, public list_storage<DataType, StorageClass> { + std::vector<unsigned> Positions; + ParserClass Parser; + + enum ValueExpected getValueExpectedFlagDefault() const override { + return Parser.getValueExpectedFlagDefault(); + } + + void getExtraOptionNames(SmallVectorImpl<StringRef> &OptionNames) override { + return Parser.getExtraOptionNames(OptionNames); + } + + bool handleOccurrence(unsigned pos, StringRef ArgName, + StringRef Arg) override { + typename ParserClass::parser_data_type Val = + typename ParserClass::parser_data_type(); + if (list_storage<DataType, StorageClass>::isDefaultAssigned()) { + clear(); + list_storage<DataType, StorageClass>::overwriteDefault(); + } + if (Parser.parse(*this, ArgName, Arg, Val)) + return true; // Parse Error! + list_storage<DataType, StorageClass>::addValue(Val); + setPosition(pos); + Positions.push_back(pos); + Callback(Val); + return false; + } + + // Forward printing stuff to the parser... + size_t getOptionWidth() const override { + return Parser.getOptionWidth(*this); + } + + void printOptionInfo(size_t GlobalWidth) const override { + Parser.printOptionInfo(*this, GlobalWidth); + } + + // Unimplemented: list options don't currently store their default value. + void printOptionValue(size_t /*GlobalWidth*/, bool /*Force*/) const override { + } + + void setDefault() override { + Positions.clear(); + list_storage<DataType, StorageClass>::clear(); + for (auto &Val : list_storage<DataType, StorageClass>::getDefault()) + list_storage<DataType, StorageClass>::addValue(Val.getValue()); + } + + void done() { + addArgument(); + Parser.initialize(); + } + +public: + // Command line options should not be copyable + list(const list &) = delete; + list &operator=(const list &) = delete; + + ParserClass &getParser() { return Parser; } + + unsigned getPosition(unsigned optnum) const { + assert(optnum < this->size() && "Invalid option index"); + return Positions[optnum]; + } + + void clear() { + Positions.clear(); + list_storage<DataType, StorageClass>::clear(); + } + + // setInitialValues - Used by the cl::list_init modifier... + void setInitialValues(ArrayRef<DataType> Vs) { + assert(!(list_storage<DataType, StorageClass>::isDefaultAssigned()) && + "Cannot have two default values"); + list_storage<DataType, StorageClass>::assignDefault(); + for (auto &Val : Vs) + list_storage<DataType, StorageClass>::addValue(Val, true); + } + + void setNumAdditionalVals(unsigned n) { Option::setNumAdditionalVals(n); } + + template <class... Mods> + explicit list(const Mods &... Ms) + : Option(ZeroOrMore, NotHidden), Parser(*this) { + apply(this, Ms...); + done(); + } + + void setCallback( + std::function<void(const typename ParserClass::parser_data_type &)> CB) { + Callback = CB; + } + + std::function<void(const typename ParserClass::parser_data_type &)> Callback = + [](const typename ParserClass::parser_data_type &) {}; +}; + +// Modifier to set the number of additional values. +struct multi_val { + unsigned AdditionalVals; + explicit multi_val(unsigned N) : AdditionalVals(N) {} + + template <typename D, typename S, typename P> + void apply(list<D, S, P> &L) const { + L.setNumAdditionalVals(AdditionalVals); + } +}; + +//===----------------------------------------------------------------------===// +// Default storage class definition: external storage. This implementation +// assumes the user will specify a variable to store the data into with the +// cl::location(x) modifier. +// +template <class DataType, class StorageClass> class bits_storage { + unsigned *Location = nullptr; // Where to store the bits... + + template <class T> static unsigned Bit(const T &V) { + unsigned BitPos = static_cast<unsigned>(V); + assert(BitPos < sizeof(unsigned) * CHAR_BIT && + "enum exceeds width of bit vector!"); + return 1 << BitPos; + } + +public: + bits_storage() = default; + + bool setLocation(Option &O, unsigned &L) { + if (Location) + return O.error("cl::location(x) specified more than once!"); + Location = &L; + return false; + } + + template <class T> void addValue(const T &V) { + assert(Location != nullptr && + "cl::location(...) not specified for a command " + "line option with external storage!"); + *Location |= Bit(V); + } + + unsigned getBits() { return *Location; } + + void clear() { + if (Location) + *Location = 0; + } + + template <class T> bool isSet(const T &V) { + return (*Location & Bit(V)) != 0; + } +}; + +// Define how to hold bits. Since we can inherit from a class, we do so. +// This makes us exactly compatible with the bits in all cases that it is used. +// +template <class DataType> class bits_storage<DataType, bool> { + unsigned Bits{0}; // Where to store the bits... + + template <class T> static unsigned Bit(const T &V) { + unsigned BitPos = static_cast<unsigned>(V); + assert(BitPos < sizeof(unsigned) * CHAR_BIT && + "enum exceeds width of bit vector!"); + return 1 << BitPos; + } + +public: + template <class T> void addValue(const T &V) { Bits |= Bit(V); } + + unsigned getBits() { return Bits; } + + void clear() { Bits = 0; } + + template <class T> bool isSet(const T &V) { return (Bits & Bit(V)) != 0; } +}; + +//===----------------------------------------------------------------------===// +// A bit vector of command options. +// +template <class DataType, class Storage = bool, + class ParserClass = parser<DataType>> +class bits : public Option, public bits_storage<DataType, Storage> { + std::vector<unsigned> Positions; + ParserClass Parser; + + enum ValueExpected getValueExpectedFlagDefault() const override { + return Parser.getValueExpectedFlagDefault(); + } + + void getExtraOptionNames(SmallVectorImpl<StringRef> &OptionNames) override { + return Parser.getExtraOptionNames(OptionNames); + } + + bool handleOccurrence(unsigned pos, StringRef ArgName, + StringRef Arg) override { + typename ParserClass::parser_data_type Val = + typename ParserClass::parser_data_type(); + if (Parser.parse(*this, ArgName, Arg, Val)) + return true; // Parse Error! + this->addValue(Val); + setPosition(pos); + Positions.push_back(pos); + Callback(Val); + return false; + } + + // Forward printing stuff to the parser... + size_t getOptionWidth() const override { + return Parser.getOptionWidth(*this); + } + + void printOptionInfo(size_t GlobalWidth) const override { + Parser.printOptionInfo(*this, GlobalWidth); + } + + // Unimplemented: bits options don't currently store their default values. + void printOptionValue(size_t /*GlobalWidth*/, bool /*Force*/) const override { + } + + void setDefault() override { bits_storage<DataType, Storage>::clear(); } + + void done() { + addArgument(); + Parser.initialize(); + } + +public: + // Command line options should not be copyable + bits(const bits &) = delete; + bits &operator=(const bits &) = delete; + + ParserClass &getParser() { return Parser; } + + unsigned getPosition(unsigned optnum) const { + assert(optnum < this->size() && "Invalid option index"); + return Positions[optnum]; + } + + template <class... Mods> + explicit bits(const Mods &... Ms) + : Option(ZeroOrMore, NotHidden), Parser(*this) { + apply(this, Ms...); + done(); + } + + void setCallback( + std::function<void(const typename ParserClass::parser_data_type &)> CB) { + Callback = CB; + } + + std::function<void(const typename ParserClass::parser_data_type &)> Callback = + [](const typename ParserClass::parser_data_type &) {}; +}; + +//===----------------------------------------------------------------------===// +// Aliased command line option (alias this name to a preexisting name) +// + +class alias : public Option { + Option *AliasFor; + + bool handleOccurrence(unsigned pos, StringRef /*ArgName*/, + StringRef Arg) override { + return AliasFor->handleOccurrence(pos, AliasFor->ArgStr, Arg); + } + + bool addOccurrence(unsigned pos, StringRef /*ArgName*/, StringRef Value, + bool MultiArg = false) override { + return AliasFor->addOccurrence(pos, AliasFor->ArgStr, Value, MultiArg); + } + + // Handle printing stuff... + size_t getOptionWidth() const override; + void printOptionInfo(size_t GlobalWidth) const override; + + // Aliases do not need to print their values. + void printOptionValue(size_t /*GlobalWidth*/, bool /*Force*/) const override { + } + + void setDefault() override { AliasFor->setDefault(); } + + ValueExpected getValueExpectedFlagDefault() const override { + return AliasFor->getValueExpectedFlag(); + } + + void done() { + if (!hasArgStr()) + error("cl::alias must have argument name specified!"); + if (!AliasFor) + error("cl::alias must have an cl::aliasopt(option) specified!"); + if (!Subs.empty()) + error("cl::alias must not have cl::sub(), aliased option's cl::sub() will be used!"); + Subs = AliasFor->Subs; + Categories = AliasFor->Categories; + addArgument(); + } + +public: + // Command line options should not be copyable + alias(const alias &) = delete; + alias &operator=(const alias &) = delete; + + void setAliasFor(Option &O) { + if (AliasFor) + error("cl::alias must only have one cl::aliasopt(...) specified!"); + AliasFor = &O; + } + + template <class... Mods> + explicit alias(const Mods &... Ms) + : Option(Optional, Hidden), AliasFor(nullptr) { + apply(this, Ms...); + done(); + } +}; + +// Modifier to set the option an alias aliases. +struct aliasopt { + Option &Opt; + + explicit aliasopt(Option &O) : Opt(O) {} + + void apply(alias &A) const { A.setAliasFor(Opt); } +}; + +// Provide additional help at the end of the normal help output. All occurrences +// of cl::extrahelp will be accumulated and printed to stderr at the end of the +// regular help, just before exit is called. +struct extrahelp { + StringRef morehelp; + + explicit extrahelp(StringRef help); +}; + +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. +/// +/// \param Hidden if true will print hidden options +/// \param Categorized if true print options in categories +void PrintHelpMessage(bool Hidden = false, bool Categorized = false); + +//===----------------------------------------------------------------------===// +// Public interface for accessing registered options. +// + +/// Use this to get a StringMap to all registered named options +/// (e.g. -help). +/// +/// \return A reference to the StringMap used by the cl APIs to parse options. +/// +/// Access to unnamed arguments (i.e. positional) are not provided because +/// it is expected that the client already has access to these. +/// +/// Typical usage: +/// \code +/// main(int argc,char* argv[]) { +/// StringMap<llvm::cl::Option*> &opts = llvm::cl::getRegisteredOptions(); +/// assert(opts.count("help") == 1) +/// opts["help"]->setDescription("Show alphabetical help information") +/// // More code +/// llvm::cl::ParseCommandLineOptions(argc,argv); +/// //More code +/// } +/// \endcode +/// +/// This interface is useful for modifying options in libraries that are out of +/// the control of the client. The options should be modified before calling +/// llvm::cl::ParseCommandLineOptions(). +/// +/// Hopefully this API can be deprecated soon. Any situation where options need +/// to be modified by tools or libraries should be handled by sane APIs rather +/// than just handing around a global list. +StringMap<Option *> & +getRegisteredOptions(SubCommand &Sub = SubCommand::getTopLevel()); + +/// Use this to get all registered SubCommands from the provided parser. +/// +/// \return A range of all SubCommand pointers registered with the parser. +/// +/// Typical usage: +/// \code +/// main(int argc, char* argv[]) { +/// llvm::cl::ParseCommandLineOptions(argc, argv); +/// for (auto* S : llvm::cl::getRegisteredSubcommands()) { +/// if (*S) { +/// std::cout << "Executing subcommand: " << S->getName() << std::endl; +/// // Execute some function based on the name... +/// } +/// } +/// } +/// \endcode +/// +/// This interface is useful for defining subcommands in libraries and +/// the dispatch from a single point (like in the main function). +iterator_range<typename SmallPtrSet<SubCommand *, 4>::iterator> +getRegisteredSubcommands(); + +//===----------------------------------------------------------------------===// +// Standalone command line processing utilities. +// + +/// Tokenizes a command line that can contain escapes and quotes. +// +/// The quoting rules match those used by GCC and other tools that use +/// libiberty's buildargv() or expandargv() utilities, and do not match bash. +/// They differ from buildargv() on treatment of backslashes that do not escape +/// a special character to make it possible to accept most Windows file paths. +/// +/// \param [in] Source The string to be split on whitespace with quotes. +/// \param [in] Saver Delegates back to the caller for saving parsed strings. +/// \param [in] MarkEOLs true if tokenizing a response file and you want end of +/// lines and end of the response file to be marked with a nullptr string. +/// \param [out] NewArgv All parsed strings are appended to NewArgv. +void TokenizeGNUCommandLine(StringRef Source, StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv, + bool MarkEOLs = false); + +/// Tokenizes a string of Windows command line arguments, which may contain +/// quotes and escaped quotes. +/// +/// See MSDN docs for CommandLineToArgvW for information on the quoting rules. +/// http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft(v=vs.85).aspx +/// +/// For handling a full Windows command line including the executable name at +/// the start, see TokenizeWindowsCommandLineFull below. +/// +/// \param [in] Source The string to be split on whitespace with quotes. +/// \param [in] Saver Delegates back to the caller for saving parsed strings. +/// \param [in] MarkEOLs true if tokenizing a response file and you want end of +/// lines and end of the response file to be marked with a nullptr string. +/// \param [out] NewArgv All parsed strings are appended to NewArgv. +void TokenizeWindowsCommandLine(StringRef Source, StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv, + bool MarkEOLs = false); + +/// Tokenizes a Windows command line while attempting to avoid copies. If no +/// quoting or escaping was used, this produces substrings of the original +/// string. If a token requires unquoting, it will be allocated with the +/// StringSaver. +void TokenizeWindowsCommandLineNoCopy(StringRef Source, StringSaver &Saver, + SmallVectorImpl<StringRef> &NewArgv); + +/// Tokenizes a Windows full command line, including command name at the start. +/// +/// This uses the same syntax rules as TokenizeWindowsCommandLine for all but +/// the first token. But the first token is expected to be parsed as the +/// executable file name in the way CreateProcess would do it, rather than the +/// way the C library startup code would do it: CreateProcess does not consider +/// that \ is ever an escape character (because " is not a valid filename char, +/// hence there's never a need to escape it to be used literally). +/// +/// Parameters are the same as for TokenizeWindowsCommandLine. In particular, +/// if you set MarkEOLs = true, then the first word of every line will be +/// parsed using the special rules for command names, making this function +/// suitable for parsing a file full of commands to execute. +void TokenizeWindowsCommandLineFull(StringRef Source, StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv, + bool MarkEOLs = false); + +/// String tokenization function type. Should be compatible with either +/// Windows or Unix command line tokenizers. +using TokenizerCallback = void (*)(StringRef Source, StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv, + bool MarkEOLs); + +/// Tokenizes content of configuration file. +/// +/// \param [in] Source The string representing content of config file. +/// \param [in] Saver Delegates back to the caller for saving parsed strings. +/// \param [out] NewArgv All parsed strings are appended to NewArgv. +/// \param [in] MarkEOLs Added for compatibility with TokenizerCallback. +/// +/// It works like TokenizeGNUCommandLine with ability to skip comment lines. +/// +void tokenizeConfigFile(StringRef Source, StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv, + bool MarkEOLs = false); + +/// Contains options that control response file expansion. +class ExpansionContext { + /// Provides persistent storage for parsed strings. + StringSaver Saver; + + /// Tokenization strategy. Typically Unix or Windows. + TokenizerCallback Tokenizer; + + /// File system used for all file access when running the expansion. + vfs::FileSystem *FS; + + /// Path used to resolve relative rsp files. If empty, the file system + /// current directory is used instead. + StringRef CurrentDir; + + /// Directories used for search of config files. + ArrayRef<StringRef> SearchDirs; + + /// True if names of nested response files must be resolved relative to + /// including file. + bool RelativeNames = false; + + /// If true, mark end of lines and the end of the response file with nullptrs + /// in the Argv vector. + bool MarkEOLs = false; + + /// If true, body of config file is expanded. + bool InConfigFile = false; + + llvm::Error expandResponseFile(StringRef FName, + SmallVectorImpl<const char *> &NewArgv); + +public: + ExpansionContext(BumpPtrAllocator &A, TokenizerCallback T); + + ExpansionContext &setMarkEOLs(bool X) { + MarkEOLs = X; + return *this; + } + + ExpansionContext &setRelativeNames(bool X) { + RelativeNames = X; + return *this; + } + + ExpansionContext &setCurrentDir(StringRef X) { + CurrentDir = X; + return *this; + } + + ExpansionContext &setSearchDirs(ArrayRef<StringRef> X) { + SearchDirs = X; + return *this; + } + + ExpansionContext &setVFS(vfs::FileSystem *X) { + FS = X; + return *this; + } + + /// Looks for the specified configuration file. + /// + /// \param[in] FileName Name of the file to search for. + /// \param[out] FilePath File absolute path, if it was found. + /// \return True if file was found. + /// + /// If the specified file name contains a directory separator, it is searched + /// for by its absolute path. Otherwise looks for file sequentially in + /// directories specified by SearchDirs field. + bool findConfigFile(StringRef FileName, SmallVectorImpl<char> &FilePath); + + /// Reads command line options from the given configuration file. + /// + /// \param [in] CfgFile Path to configuration file. + /// \param [out] Argv Array to which the read options are added. + /// \return true if the file was successfully read. + /// + /// It reads content of the specified file, tokenizes it and expands "@file" + /// commands resolving file names in them relative to the directory where + /// CfgFilename resides. It also expands "<CFGDIR>" to the base path of the + /// current config file. + Error readConfigFile(StringRef CfgFile, SmallVectorImpl<const char *> &Argv); + + /// Expands constructs "@file" in the provided array of arguments recursively. + Error expandResponseFiles(SmallVectorImpl<const char *> &Argv); +}; + +/// A convenience helper which concatenates the options specified by the +/// environment variable EnvVar and command line options, then expands +/// response files recursively. +/// \return true if all @files were expanded successfully or there were none. +bool expandResponseFiles(int Argc, const char *const *Argv, const char *EnvVar, + SmallVectorImpl<const char *> &NewArgv); + +/// A convenience helper which supports the typical use case of expansion +/// function call. +bool ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, + SmallVectorImpl<const char *> &Argv); + +/// A convenience helper which concatenates the options specified by the +/// environment variable EnvVar and command line options, then expands response +/// files recursively. The tokenizer is a predefined GNU or Windows one. +/// \return true if all @files were expanded successfully or there were none. +bool expandResponseFiles(int Argc, const char *const *Argv, const char *EnvVar, + StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv); + +/// Mark all options not part of this category as cl::ReallyHidden. +/// +/// \param Category the category of options to keep displaying +/// +/// Some tools (like clang-format) like to be able to hide all options that are +/// not specific to the tool. This function allows a tool to specify a single +/// option category to display in the -help output. +void HideUnrelatedOptions(cl::OptionCategory &Category, + SubCommand &Sub = SubCommand::getTopLevel()); + +/// Mark all options not part of the categories as cl::ReallyHidden. +/// +/// \param Categories the categories of options to keep displaying. +/// +/// Some tools (like clang-format) like to be able to hide all options that are +/// not specific to the tool. This function allows a tool to specify a single +/// option category to display in the -help output. +void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories, + SubCommand &Sub = SubCommand::getTopLevel()); + +/// Reset all command line options to a state that looks as if they have +/// never appeared on the command line. This is useful for being able to parse +/// a command line multiple times (especially useful for writing tests). +void ResetAllOptionOccurrences(); + +/// Reset the command line parser back to its initial state. This +/// removes +/// all options, categories, and subcommands and returns the parser to a state +/// where no options are supported. +void ResetCommandLineParser(); + +/// Parses `Arg` into the option handler `Handler`. +bool ProvidePositionalOption(Option *Handler, StringRef Arg, int i); + +} // end namespace cl + +} // end namespace llvm + +#endif // LLVM_SUPPORT_COMMANDLINE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Compiler.h b/contrib/libs/llvm16/include/llvm/Support/Compiler.h new file mode 100644 index 00000000000..ccae94b064f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Compiler.h @@ -0,0 +1,565 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Compiler.h - Compiler abstraction support --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines several macros, based on the current compiler. This allows +// use of compiler-specific features in a way that remains portable. This header +// can be included from either C or C++. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_COMPILER_H +#define LLVM_SUPPORT_COMPILER_H + +#include "llvm/Config/llvm-config.h" + +#include <stddef.h> + +#if defined(_MSC_VER) +#include <sal.h> +#endif + +#ifndef __has_feature +# define __has_feature(x) 0 +#endif + +#ifndef __has_extension +# define __has_extension(x) 0 +#endif + +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#ifndef __has_include +# define __has_include(x) 0 +#endif + +// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in +// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid. +#ifndef LLVM_HAS_CPP_ATTRIBUTE +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define LLVM_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define LLVM_HAS_CPP_ATTRIBUTE(x) 0 +#endif +#endif + +/// \macro LLVM_GNUC_PREREQ +/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't +/// available. +#ifndef LLVM_GNUC_PREREQ +# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) +# define LLVM_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ + ((maj) << 20) + ((min) << 10) + (patch)) +# elif defined(__GNUC__) && defined(__GNUC_MINOR__) +# define LLVM_GNUC_PREREQ(maj, min, patch) \ + ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) +# else +# define LLVM_GNUC_PREREQ(maj, min, patch) 0 +# endif +#endif + +/// \macro LLVM_MSC_PREREQ +/// Is the compiler MSVC of at least the specified version? +/// The common \param version values to check for are: +/// * 1910: VS2017, version 15.1 & 15.2 +/// * 1911: VS2017, version 15.3 & 15.4 +/// * 1912: VS2017, version 15.5 +/// * 1913: VS2017, version 15.6 +/// * 1914: VS2017, version 15.7 +/// * 1915: VS2017, version 15.8 +/// * 1916: VS2017, version 15.9 +/// * 1920: VS2019, version 16.0 +/// * 1921: VS2019, version 16.1 +/// * 1922: VS2019, version 16.2 +/// * 1923: VS2019, version 16.3 +/// * 1924: VS2019, version 16.4 +/// * 1925: VS2019, version 16.5 +/// * 1926: VS2019, version 16.6 +/// * 1927: VS2019, version 16.7 +/// * 1928: VS2019, version 16.8 + 16.9 +/// * 1929: VS2019, version 16.10 + 16.11 +/// * 1930: VS2022, version 17.0 +#ifdef _MSC_VER +#define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version)) + +// We require at least VS 2019. +#if !defined(LLVM_FORCE_USE_OLD_TOOLCHAIN) +#if !LLVM_MSC_PREREQ(1920) +#error LLVM requires at least VS 2019. +#endif +#endif + +#else +#define LLVM_MSC_PREREQ(version) 0 +#endif + +/// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked +/// into a shared library, then the class should be private to the library and +/// not accessible from outside it. Can also be used to mark variables and +/// functions, making them private to any shared library they are linked into. +/// On PE/COFF targets, library visibility is the default, so this isn't needed. +/// +/// LLVM_EXTERNAL_VISIBILITY - classes, functions, and variables marked with +/// this attribute will be made public and visible outside of any shared library +/// they are linked in to. +#if __has_attribute(visibility) && \ + (!(defined(_WIN32) || defined(__CYGWIN__)) || \ + (defined(__MINGW32__) && defined(__clang__))) +#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden"))) +#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) +#define LLVM_EXTERNAL_VISIBILITY __attribute__((visibility("default"))) +#else +#define LLVM_EXTERNAL_VISIBILITY +#endif +#else +#define LLVM_LIBRARY_VISIBILITY +#define LLVM_EXTERNAL_VISIBILITY +#endif + +#if defined(__GNUC__) +#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality) +#else +#define LLVM_PREFETCH(addr, rw, locality) +#endif + +#if __has_attribute(used) +#define LLVM_ATTRIBUTE_USED __attribute__((__used__)) +#else +#define LLVM_ATTRIBUTE_USED +#endif + +#if defined(__clang__) +#define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX))) +#else +#define LLVM_DEPRECATED(MSG, FIX) [[deprecated(MSG)]] +#endif + +// Indicate that a non-static, non-const C++ member function reinitializes +// the entire object to a known state, independent of the previous state of +// the object. +// +// The clang-tidy check bugprone-use-after-move recognizes this attribute as a +// marker that a moved-from object has left the indeterminate state and can be +// reused. +#if LLVM_HAS_CPP_ATTRIBUTE(clang::reinitializes) +#define LLVM_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define LLVM_ATTRIBUTE_REINITIALIZES +#endif + +// Some compilers warn about unused functions. When a function is sometimes +// used or not depending on build settings (e.g. a function only called from +// within "assert"), this attribute can be used to suppress such warnings. +// +// However, it shouldn't be used for unused *variables*, as those have a much +// more portable solution: +// (void)unused_var_name; +// Prefer cast-to-void wherever it is sufficient. +#if __has_attribute(unused) +#define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define LLVM_ATTRIBUTE_UNUSED +#endif + +// FIXME: Provide this for PE/COFF targets. +#if __has_attribute(weak) && !defined(__MINGW32__) && !defined(__CYGWIN__) && \ + !defined(_WIN32) +#define LLVM_ATTRIBUTE_WEAK __attribute__((__weak__)) +#else +#define LLVM_ATTRIBUTE_WEAK +#endif + +// Prior to clang 3.2, clang did not accept any spelling of +// __has_attribute(const), so assume it is supported. +#if defined(__clang__) || defined(__GNUC__) +// aka 'CONST' but following LLVM Conventions. +#define LLVM_READNONE __attribute__((__const__)) +#else +#define LLVM_READNONE +#endif + +#if __has_attribute(pure) || defined(__GNUC__) +// aka 'PURE' but following LLVM Conventions. +#define LLVM_READONLY __attribute__((__pure__)) +#else +#define LLVM_READONLY +#endif + +#if __has_attribute(minsize) +#define LLVM_ATTRIBUTE_MINSIZE __attribute__((minsize)) +#else +#define LLVM_ATTRIBUTE_MINSIZE +#endif + +#if __has_builtin(__builtin_expect) || defined(__GNUC__) +#define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) +#define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) +#else +#define LLVM_LIKELY(EXPR) (EXPR) +#define LLVM_UNLIKELY(EXPR) (EXPR) +#endif + +/// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, +/// mark a method "not for inlining". +#if __has_attribute(noinline) +#define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define LLVM_ATTRIBUTE_NOINLINE +#endif + +/// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do +/// so, mark a method "always inline" because it is performance sensitive. +#if __has_attribute(always_inline) +#define LLVM_ATTRIBUTE_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define LLVM_ATTRIBUTE_ALWAYS_INLINE inline +#endif + +/// LLVM_ATTRIBUTE_NO_DEBUG - On compilers where we have a directive to do +/// so, mark a method "no debug" because debug info makes the debugger +/// experience worse. +#if __has_attribute(nodebug) +#define LLVM_ATTRIBUTE_NODEBUG __attribute__((nodebug)) +#else +#define LLVM_ATTRIBUTE_NODEBUG +#endif + +#if __has_attribute(returns_nonnull) +#define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_RETURNS_NONNULL _Ret_notnull_ +#else +#define LLVM_ATTRIBUTE_RETURNS_NONNULL +#endif + +/// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a +/// pointer that does not alias any other valid pointer. +#ifdef __GNUC__ +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict) +#else +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS +#endif + +/// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements. +#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough) +#define LLVM_FALLTHROUGH [[fallthrough]] +#elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough) +#define LLVM_FALLTHROUGH [[gnu::fallthrough]] +#elif __has_attribute(fallthrough) +#define LLVM_FALLTHROUGH __attribute__((fallthrough)) +#elif LLVM_HAS_CPP_ATTRIBUTE(clang::fallthrough) +#define LLVM_FALLTHROUGH [[clang::fallthrough]] +#else +#define LLVM_FALLTHROUGH +#endif + +/// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that +/// they are constant initialized. +#if LLVM_HAS_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define LLVM_REQUIRE_CONSTANT_INITIALIZATION \ + [[clang::require_constant_initialization]] +#else +#define LLVM_REQUIRE_CONSTANT_INITIALIZATION +#endif + +/// LLVM_GSL_OWNER - Apply this to owning classes like SmallVector to enable +/// lifetime warnings. +#if LLVM_HAS_CPP_ATTRIBUTE(gsl::Owner) +#define LLVM_GSL_OWNER [[gsl::Owner]] +#else +#define LLVM_GSL_OWNER +#endif + +/// LLVM_GSL_POINTER - Apply this to non-owning classes like +/// StringRef to enable lifetime warnings. +#if LLVM_HAS_CPP_ATTRIBUTE(gsl::Pointer) +#define LLVM_GSL_POINTER [[gsl::Pointer]] +#else +#define LLVM_GSL_POINTER +#endif + +/// LLVM_EXTENSION - Support compilers where we have a keyword to suppress +/// pedantic diagnostics. +#ifdef __GNUC__ +#define LLVM_EXTENSION __extension__ +#else +#define LLVM_EXTENSION +#endif + +/// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands +/// to an expression which states that it is undefined behavior for the +/// compiler to reach this point. Otherwise is not defined. +/// +/// '#else' is intentionally left out so that other macro logic (e.g., +/// LLVM_ASSUME_ALIGNED and llvm_unreachable()) can detect whether +/// LLVM_BUILTIN_UNREACHABLE has a definition. +#if __has_builtin(__builtin_unreachable) || defined(__GNUC__) +# define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +# define LLVM_BUILTIN_UNREACHABLE __assume(false) +#endif + +/// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression +/// which causes the program to exit abnormally. +#if __has_builtin(__builtin_trap) || defined(__GNUC__) +# define LLVM_BUILTIN_TRAP __builtin_trap() +#elif defined(_MSC_VER) +// The __debugbreak intrinsic is supported by MSVC, does not require forward +// declarations involving platform-specific typedefs (unlike RaiseException), +// results in a call to vectored exception handlers, and encodes to a short +// instruction that still causes the trapping behavior we want. +# define LLVM_BUILTIN_TRAP __debugbreak() +#else +# define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0 +#endif + +/// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to +/// an expression which causes the program to break while running +/// under a debugger. +#if __has_builtin(__builtin_debugtrap) +# define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap() +#elif defined(_MSC_VER) +// The __debugbreak intrinsic is supported by MSVC and breaks while +// running under the debugger, and also supports invoking a debugger +// when the OS is configured appropriately. +# define LLVM_BUILTIN_DEBUGTRAP __debugbreak() +#else +// Just continue execution when built with compilers that have no +// support. This is a debugging aid and not intended to force the +// program to abort if encountered. +# define LLVM_BUILTIN_DEBUGTRAP +#endif + +/// \macro LLVM_ASSUME_ALIGNED +/// Returns a pointer with an assumed alignment. +#if __has_builtin(__builtin_assume_aligned) || defined(__GNUC__) +# define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a) +#elif defined(LLVM_BUILTIN_UNREACHABLE) +# define LLVM_ASSUME_ALIGNED(p, a) \ + (((uintptr_t(p) % (a)) == 0) ? (p) : (LLVM_BUILTIN_UNREACHABLE, (p))) +#else +# define LLVM_ASSUME_ALIGNED(p, a) (p) +#endif + +/// \macro LLVM_PACKED +/// Used to specify a packed structure. +/// LLVM_PACKED( +/// struct A { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }); +/// +/// LLVM_PACKED_START +/// struct B { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }; +/// LLVM_PACKED_END +#ifdef _MSC_VER +# define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop)) +# define LLVM_PACKED_START __pragma(pack(push, 1)) +# define LLVM_PACKED_END __pragma(pack(pop)) +#else +# define LLVM_PACKED(d) d __attribute__((packed)) +# define LLVM_PACKED_START _Pragma("pack(push, 1)") +# define LLVM_PACKED_END _Pragma("pack(pop)") +#endif + +/// \macro LLVM_MEMORY_SANITIZER_BUILD +/// Whether LLVM itself is built with MemorySanitizer instrumentation. +#if __has_feature(memory_sanitizer) +# define LLVM_MEMORY_SANITIZER_BUILD 1 +# include <sanitizer/msan_interface.h> +# define LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE __attribute__((no_sanitize_memory)) +#else +# define LLVM_MEMORY_SANITIZER_BUILD 0 +# define __msan_allocated_memory(p, size) +# define __msan_unpoison(p, size) +# define LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE +#endif + +/// \macro LLVM_ADDRESS_SANITIZER_BUILD +/// Whether LLVM itself is built with AddressSanitizer instrumentation. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +# define LLVM_ADDRESS_SANITIZER_BUILD 1 +#if __has_include(<sanitizer/asan_interface.h>) +# include <sanitizer/asan_interface.h> +#else +// These declarations exist to support ASan with MSVC. If MSVC eventually ships +// asan_interface.h in their headers, then we can remove this. +#ifdef __cplusplus +extern "C" { +#endif +void __asan_poison_memory_region(void const volatile *addr, size_t size); +void __asan_unpoison_memory_region(void const volatile *addr, size_t size); +#ifdef __cplusplus +} // extern "C" +#endif +#endif +#else +# define LLVM_ADDRESS_SANITIZER_BUILD 0 +# define __asan_poison_memory_region(p, size) +# define __asan_unpoison_memory_region(p, size) +#endif + +/// \macro LLVM_HWADDRESS_SANITIZER_BUILD +/// Whether LLVM itself is built with HWAddressSanitizer instrumentation. +#if __has_feature(hwaddress_sanitizer) +#define LLVM_HWADDRESS_SANITIZER_BUILD 1 +#else +#define LLVM_HWADDRESS_SANITIZER_BUILD 0 +#endif + +/// \macro LLVM_THREAD_SANITIZER_BUILD +/// Whether LLVM itself is built with ThreadSanitizer instrumentation. +#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__) +# define LLVM_THREAD_SANITIZER_BUILD 1 +#else +# define LLVM_THREAD_SANITIZER_BUILD 0 +#endif + +#if LLVM_THREAD_SANITIZER_BUILD +// Thread Sanitizer is a tool that finds races in code. +// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations . +// tsan detects these exact functions by name. +#ifdef __cplusplus +extern "C" { +#endif +void AnnotateHappensAfter(const char *file, int line, const volatile void *cv); +void AnnotateHappensBefore(const char *file, int line, const volatile void *cv); +void AnnotateIgnoreWritesBegin(const char *file, int line); +void AnnotateIgnoreWritesEnd(const char *file, int line); +#ifdef __cplusplus +} +#endif + +// This marker is used to define a happens-before arc. The race detector will +// infer an arc from the begin to the end when they share the same pointer +// argument. +# define TsanHappensBefore(cv) AnnotateHappensBefore(__FILE__, __LINE__, cv) + +// This marker defines the destination of a happens-before arc. +# define TsanHappensAfter(cv) AnnotateHappensAfter(__FILE__, __LINE__, cv) + +// Ignore any races on writes between here and the next TsanIgnoreWritesEnd. +# define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin(__FILE__, __LINE__) + +// Resume checking for racy writes. +# define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__) +#else +# define TsanHappensBefore(cv) +# define TsanHappensAfter(cv) +# define TsanIgnoreWritesBegin() +# define TsanIgnoreWritesEnd() +#endif + +/// \macro LLVM_NO_SANITIZE +/// Disable a particular sanitizer for a function. +#if __has_attribute(no_sanitize) +#define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND))) +#else +#define LLVM_NO_SANITIZE(KIND) +#endif + +/// Mark debug helper function definitions like dump() that should not be +/// stripped from debug builds. +/// Note that you should also surround dump() functions with +/// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always +/// get stripped in release builds. +// FIXME: Move this to a private config.h as it's not usable in public headers. +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED +#else +#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE +#endif + +/// \macro LLVM_PRETTY_FUNCTION +/// Gets a user-friendly looking function signature for the current scope +/// using the best available method on each platform. The exact format of the +/// resulting string is implementation specific and non-portable, so this should +/// only be used, for example, for logging or diagnostics. +#if defined(_MSC_VER) +#define LLVM_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) || defined(__clang__) +#define LLVM_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define LLVM_PRETTY_FUNCTION __func__ +#endif + +/// \macro LLVM_THREAD_LOCAL +/// A thread-local storage specifier which can be used with globals, +/// extern globals, and static globals. +/// +/// This is essentially an extremely restricted analog to C++11's thread_local +/// support. It uses thread_local if available, falling back on gcc __thread +/// if not. __thread doesn't support many of the C++11 thread_local's +/// features. You should only use this for PODs that you can statically +/// initialize to some constant value. In almost all circumstances this is most +/// appropriate for use with a pointer, integer, or small aggregation of +/// pointers and integers. +#if LLVM_ENABLE_THREADS +#if __has_feature(cxx_thread_local) || defined(_MSC_VER) +#define LLVM_THREAD_LOCAL thread_local +#else +// Clang, GCC, and other compatible compilers used __thread prior to C++11 and +// we only need the restricted functionality that provides. +#define LLVM_THREAD_LOCAL __thread +#endif +#else // !LLVM_ENABLE_THREADS +// If threading is disabled entirely, this compiles to nothing and you get +// a normal global variable. +#define LLVM_THREAD_LOCAL +#endif + +/// \macro LLVM_ENABLE_EXCEPTIONS +/// Whether LLVM is built with exception support. +#if __has_feature(cxx_exceptions) +#define LLVM_ENABLE_EXCEPTIONS 1 +#elif defined(__GNUC__) && defined(__EXCEPTIONS) +#define LLVM_ENABLE_EXCEPTIONS 1 +#elif defined(_MSC_VER) && defined(_CPPUNWIND) +#define LLVM_ENABLE_EXCEPTIONS 1 +#endif + +/// \macro LLVM_NO_PROFILE_INSTRUMENT_FUNCTION +/// Disable the profile instrument for a function. +#if __has_attribute(no_profile_instrument_function) +#define LLVM_NO_PROFILE_INSTRUMENT_FUNCTION \ + __attribute__((no_profile_instrument_function)) +#else +#define LLVM_NO_PROFILE_INSTRUMENT_FUNCTION +#endif + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Compression.h b/contrib/libs/llvm16/include/llvm/Support/Compression.h new file mode 100644 index 00000000000..e4c677ab988 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Compression.h @@ -0,0 +1,139 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Compression.h ---Compression----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains basic functions for compression/decompression. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_COMPRESSION_H +#define LLVM_SUPPORT_COMPRESSION_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/DataTypes.h" + +namespace llvm { +template <typename T> class SmallVectorImpl; +class Error; + +// None indicates no compression. The other members are a subset of +// compression::Format, which is used for compressed debug sections in some +// object file formats (e.g. ELF). This is a separate class as we may add new +// compression::Format members for non-debugging purposes. +enum class DebugCompressionType { + None, ///< No compression + Zlib, ///< zlib + Zstd, ///< Zstandard +}; + +namespace compression { +namespace zlib { + +constexpr int NoCompression = 0; +constexpr int BestSpeedCompression = 1; +constexpr int DefaultCompression = 6; +constexpr int BestSizeCompression = 9; + +bool isAvailable(); + +void compress(ArrayRef<uint8_t> Input, + SmallVectorImpl<uint8_t> &CompressedBuffer, + int Level = DefaultCompression); + +Error decompress(ArrayRef<uint8_t> Input, uint8_t *Output, + size_t &UncompressedSize); + +Error decompress(ArrayRef<uint8_t> Input, SmallVectorImpl<uint8_t> &Output, + size_t UncompressedSize); + +} // End of namespace zlib + +namespace zstd { + +constexpr int NoCompression = -5; +constexpr int BestSpeedCompression = 1; +constexpr int DefaultCompression = 5; +constexpr int BestSizeCompression = 12; + +bool isAvailable(); + +void compress(ArrayRef<uint8_t> Input, + SmallVectorImpl<uint8_t> &CompressedBuffer, + int Level = DefaultCompression); + +Error decompress(ArrayRef<uint8_t> Input, uint8_t *Output, + size_t &UncompressedSize); + +Error decompress(ArrayRef<uint8_t> Input, SmallVectorImpl<uint8_t> &Output, + size_t UncompressedSize); + +} // End of namespace zstd + +enum class Format { + Zlib, + Zstd, +}; + +inline Format formatFor(DebugCompressionType Type) { + switch (Type) { + case DebugCompressionType::None: + llvm_unreachable("not a compression type"); + case DebugCompressionType::Zlib: + return Format::Zlib; + case DebugCompressionType::Zstd: + return Format::Zstd; + } + llvm_unreachable(""); +} + +struct Params { + constexpr Params(Format F) + : format(F), level(F == Format::Zlib ? zlib::DefaultCompression + : zstd::DefaultCompression) {} + Params(DebugCompressionType Type) : Params(formatFor(Type)) {} + + Format format; + int level; + // This may support multi-threading for zstd in the future. Note that + // different threads may produce different output, so be careful if certain + // output determinism is desired. +}; + +// Return nullptr if LLVM was built with support (LLVM_ENABLE_ZLIB, +// LLVM_ENABLE_ZSTD) for the specified compression format; otherwise +// return a string literal describing the reason. +const char *getReasonIfUnsupported(Format F); + +// Compress Input with the specified format P.Format. If Level is -1, use +// *::DefaultCompression for the format. +void compress(Params P, ArrayRef<uint8_t> Input, + SmallVectorImpl<uint8_t> &Output); + +// Decompress Input. The uncompressed size must be available. +Error decompress(DebugCompressionType T, ArrayRef<uint8_t> Input, + uint8_t *Output, size_t UncompressedSize); +Error decompress(Format F, ArrayRef<uint8_t> Input, + SmallVectorImpl<uint8_t> &Output, size_t UncompressedSize); +Error decompress(DebugCompressionType T, ArrayRef<uint8_t> Input, + SmallVectorImpl<uint8_t> &Output, size_t UncompressedSize); + +} // End of namespace compression + +} // End of namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ConvertUTF.h b/contrib/libs/llvm16/include/llvm/Support/ConvertUTF.h new file mode 100644 index 00000000000..d67aa50ed4d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ConvertUTF.h @@ -0,0 +1,359 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +/*===--- ConvertUTF.h - Universal Character Names conversions ---------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *==------------------------------------------------------------------------==*/ +/* + * Copyright © 1991-2015 Unicode, Inc. All rights reserved. + * Distributed under the Terms of Use in + * http://www.unicode.org/copyright.html. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of the Unicode data files and any associated documentation + * (the "Data Files") or Unicode software and any associated documentation + * (the "Software") to deal in the Data Files or Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, and/or sell copies of + * the Data Files or Software, and to permit persons to whom the Data Files + * or Software are furnished to do so, provided that + * (a) this copyright and permission notice appear with all copies + * of the Data Files or Software, + * (b) this copyright and permission notice appear in associated + * documentation, and + * (c) there is clear notice in each modified Data File or in the Software + * as well as in the documentation associated with the Data File(s) or + * Software that the data or software has been modified. + * + * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL + * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THE DATA FILES OR SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, + * use or other dealings in these Data Files or Software without prior + * written authorization of the copyright holder. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, + or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +#ifndef LLVM_SUPPORT_CONVERTUTF_H +#define LLVM_SUPPORT_CONVERTUTF_H + +#include <cstddef> +#include <string> + +#if defined(_WIN32) +#include <system_error> +#endif + +// Wrap everything in namespace llvm so that programs can link with llvm and +// their own version of the unicode libraries. + +namespace llvm { + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ + +typedef unsigned int UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +#define UNI_MAX_UTF8_BYTES_PER_CODE_POINT 4 + +#define UNI_UTF16_BYTE_ORDER_MARK_NATIVE 0xFEFF +#define UNI_UTF16_BYTE_ORDER_MARK_SWAPPED 0xFFFE + +#define UNI_UTF32_BYTE_ORDER_MARK_NATIVE 0x0000FEFF +#define UNI_UTF32_BYTE_ORDER_MARK_SWAPPED 0xFFFE0000 + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +/** + * Convert a partial UTF8 sequence to UTF32. If the sequence ends in an + * incomplete code unit sequence, returns \c sourceExhausted. + */ +ConversionResult ConvertUTF8toUTF32Partial( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +/** + * Convert a partial UTF8 sequence to UTF32. If the sequence ends in an + * incomplete code unit sequence, returns \c sourceIllegal. + */ +ConversionResult ConvertUTF8toUTF32( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd); + +unsigned getUTF8SequenceSize(const UTF8 *source, const UTF8 *sourceEnd); + +unsigned getNumBytesForUTF8(UTF8 firstByte); + +/*************************************************************************/ +/* Below are LLVM-specific wrappers of the functions above. */ + +template <typename T> class ArrayRef; +template <typename T> class SmallVectorImpl; +class StringRef; + +/** + * Convert an UTF8 StringRef to UTF8, UTF16, or UTF32 depending on + * WideCharWidth. The converted data is written to ResultPtr, which needs to + * point to at least WideCharWidth * (Source.Size() + 1) bytes. On success, + * ResultPtr will point one after the end of the copied string. On failure, + * ResultPtr will not be changed, and ErrorPtr will be set to the location of + * the first character which could not be converted. + * \return true on success. + */ +bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source, + char *&ResultPtr, const UTF8 *&ErrorPtr); + +/** +* Converts a UTF-8 StringRef to a std::wstring. +* \return true on success. +*/ +bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result); + +/** +* Converts a UTF-8 C-string to a std::wstring. +* \return true on success. +*/ +bool ConvertUTF8toWide(const char *Source, std::wstring &Result); + +/** +* Converts a std::wstring to a UTF-8 encoded std::string. +* \return true on success. +*/ +bool convertWideToUTF8(const std::wstring &Source, std::string &Result); + + +/** + * Convert an Unicode code point to UTF8 sequence. + * + * \param Source a Unicode code point. + * \param [in,out] ResultPtr pointer to the output buffer, needs to be at least + * \c UNI_MAX_UTF8_BYTES_PER_CODE_POINT bytes. On success \c ResultPtr is + * updated one past end of the converted sequence. + * + * \returns true on success. + */ +bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr); + +/** + * Convert the first UTF8 sequence in the given source buffer to a UTF32 + * code point. + * + * \param [in,out] source A pointer to the source buffer. If the conversion + * succeeds, this pointer will be updated to point to the byte just past the + * end of the converted sequence. + * \param sourceEnd A pointer just past the end of the source buffer. + * \param [out] target The converted code + * \param flags Whether the conversion is strict or lenient. + * + * \returns conversionOK on success + * + * \sa ConvertUTF8toUTF32 + */ +inline ConversionResult convertUTF8Sequence(const UTF8 **source, + const UTF8 *sourceEnd, + UTF32 *target, + ConversionFlags flags) { + if (*source == sourceEnd) + return sourceExhausted; + unsigned size = getNumBytesForUTF8(**source); + if ((ptrdiff_t)size > sourceEnd - *source) + return sourceExhausted; + return ConvertUTF8toUTF32(source, *source + size, &target, target + 1, flags); +} + +/** + * Returns true if a blob of text starts with a UTF-16 big or little endian byte + * order mark. + */ +bool hasUTF16ByteOrderMark(ArrayRef<char> SrcBytes); + +/** + * Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string. + * + * \param [in] SrcBytes A buffer of what is assumed to be UTF-16 encoded text. + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out); + +/** +* Converts a UTF16 string into a UTF8 std::string. +* +* \param [in] Src A buffer of UTF-16 encoded text. +* \param [out] Out Converted UTF-8 is stored here on success. +* \returns true on success +*/ +bool convertUTF16ToUTF8String(ArrayRef<UTF16> Src, std::string &Out); + +/** + * Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string. + * + * \param [in] SrcBytes A buffer of what is assumed to be UTF-32 encoded text. + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +bool convertUTF32ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out); + +/** + * Converts a UTF32 string into a UTF8 std::string. + * + * \param [in] Src A buffer of UTF-32 encoded text. + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +bool convertUTF32ToUTF8String(ArrayRef<UTF32> Src, std::string &Out); + +/** + * Converts a UTF-8 string into a UTF-16 string with native endianness. + * + * \returns true on success + */ +bool convertUTF8ToUTF16String(StringRef SrcUTF8, + SmallVectorImpl<UTF16> &DstUTF16); + +#if defined(_WIN32) +namespace sys { +namespace windows { +std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16); +/// Convert to UTF16 from the current code page used in the system +std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16); +std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + SmallVectorImpl<char> &utf8); +/// Convert from UTF16 to the current code page used in the system +std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, + SmallVectorImpl<char> &utf8); +} // namespace windows +} // namespace sys +#endif + +} /* end namespace llvm */ + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/CrashRecoveryContext.h b/contrib/libs/llvm16/include/llvm/Support/CrashRecoveryContext.h new file mode 100644 index 00000000000..80c1d1d96ee --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/CrashRecoveryContext.h @@ -0,0 +1,283 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- CrashRecoveryContext.h - Crash Recovery ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H +#define LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H + +#include "llvm/ADT/STLFunctionalExtras.h" + +namespace llvm { +class CrashRecoveryContextCleanup; + +/// Crash recovery helper object. +/// +/// This class implements support for running operations in a safe context so +/// that crashes (memory errors, stack overflow, assertion violations) can be +/// detected and control restored to the crashing thread. Crash detection is +/// purely "best effort", the exact set of failures which can be recovered from +/// is platform dependent. +/// +/// Clients make use of this code by first calling +/// CrashRecoveryContext::Enable(), and then executing unsafe operations via a +/// CrashRecoveryContext object. For example: +/// +/// \code +/// void actual_work(void *); +/// +/// void foo() { +/// CrashRecoveryContext CRC; +/// +/// if (!CRC.RunSafely(actual_work, 0)) { +/// ... a crash was detected, report error to user ... +/// } +/// +/// ... no crash was detected ... +/// } +/// \endcode +/// +/// To assist recovery the class allows specifying set of actions that will be +/// executed in any case, whether crash occurs or not. These actions may be used +/// to reclaim resources in the case of crash. +class CrashRecoveryContext { + void *Impl = nullptr; + CrashRecoveryContextCleanup *head = nullptr; + +public: + CrashRecoveryContext(); + ~CrashRecoveryContext(); + + /// Register cleanup handler, which is used when the recovery context is + /// finished. + /// The recovery context owns the handler. + void registerCleanup(CrashRecoveryContextCleanup *cleanup); + + void unregisterCleanup(CrashRecoveryContextCleanup *cleanup); + + /// Enable crash recovery. + static void Enable(); + + /// Disable crash recovery. + static void Disable(); + + /// Return the active context, if the code is currently executing in a + /// thread which is in a protected context. + static CrashRecoveryContext *GetCurrent(); + + /// Return true if the current thread is recovering from a crash. + static bool isRecoveringFromCrash(); + + /// Execute the provided callback function (with the given arguments) in + /// a protected context. + /// + /// \return True if the function completed successfully, and false if the + /// function crashed (or HandleCrash was called explicitly). Clients should + /// make as little assumptions as possible about the program state when + /// RunSafely has returned false. + bool RunSafely(function_ref<void()> Fn); + bool RunSafely(void (*Fn)(void*), void *UserData) { + return RunSafely([&]() { Fn(UserData); }); + } + + /// Execute the provide callback function (with the given arguments) in + /// a protected context which is run in another thread (optionally with a + /// requested stack size). + /// + /// See RunSafely(). + /// + /// On Darwin, if PRIO_DARWIN_BG is set on the calling thread, it will be + /// propagated to the new thread as well. + bool RunSafelyOnThread(function_ref<void()>, unsigned RequestedStackSize = 0); + bool RunSafelyOnThread(void (*Fn)(void*), void *UserData, + unsigned RequestedStackSize = 0) { + return RunSafelyOnThread([&]() { Fn(UserData); }, RequestedStackSize); + } + + /// Explicitly trigger a crash recovery in the current process, and + /// return failure from RunSafely(). This function does not return. + [[noreturn]] void HandleExit(int RetCode); + + /// Return true if RetCode indicates that a signal or an exception occurred. + static bool isCrash(int RetCode); + + /// Throw again a signal or an exception, after it was catched once by a + /// CrashRecoveryContext. + static bool throwIfCrash(int RetCode); + + /// In case of a crash, this is the crash identifier. + int RetCode = 0; + + /// Selects whether handling of failures should be done in the same way as + /// for regular crashes. When this is active, a crash would print the + /// callstack, clean-up any temporary files and create a coredump/minidump. + bool DumpStackAndCleanupOnFailure = false; +}; + +/// Abstract base class of cleanup handlers. +/// +/// Derived classes override method recoverResources, which makes actual work on +/// resource recovery. +/// +/// Cleanup handlers are stored in a double list, which is owned and managed by +/// a crash recovery context. +class CrashRecoveryContextCleanup { +protected: + CrashRecoveryContext *context = nullptr; + CrashRecoveryContextCleanup(CrashRecoveryContext *context) + : context(context) {} + +public: + bool cleanupFired = false; + + virtual ~CrashRecoveryContextCleanup(); + virtual void recoverResources() = 0; + + CrashRecoveryContext *getContext() const { + return context; + } + +private: + friend class CrashRecoveryContext; + CrashRecoveryContextCleanup *prev = nullptr, *next = nullptr; +}; + +/// Base class of cleanup handler that controls recovery of resources of the +/// given type. +/// +/// \tparam Derived Class that uses this class as a base. +/// \tparam T Type of controlled resource. +/// +/// This class serves as a base for its template parameter as implied by +/// Curiously Recurring Template Pattern. +/// +/// This class factors out creation of a cleanup handler. The latter requires +/// knowledge of the current recovery context, which is provided by this class. +template<typename Derived, typename T> +class CrashRecoveryContextCleanupBase : public CrashRecoveryContextCleanup { +protected: + T *resource; + CrashRecoveryContextCleanupBase(CrashRecoveryContext *context, T *resource) + : CrashRecoveryContextCleanup(context), resource(resource) {} + +public: + /// Creates cleanup handler. + /// \param x Pointer to the resource recovered by this handler. + /// \return New handler or null if the method was called outside a recovery + /// context. + static Derived *create(T *x) { + if (x) { + if (CrashRecoveryContext *context = CrashRecoveryContext::GetCurrent()) + return new Derived(context, x); + } + return nullptr; + } +}; + +/// Cleanup handler that reclaims resource by calling destructor on it. +template <typename T> +class CrashRecoveryContextDestructorCleanup : public + CrashRecoveryContextCleanupBase<CrashRecoveryContextDestructorCleanup<T>, T> { +public: + CrashRecoveryContextDestructorCleanup(CrashRecoveryContext *context, + T *resource) + : CrashRecoveryContextCleanupBase< + CrashRecoveryContextDestructorCleanup<T>, T>(context, resource) {} + + void recoverResources() override { + this->resource->~T(); + } +}; + +/// Cleanup handler that reclaims resource by calling 'delete' on it. +template <typename T> +class CrashRecoveryContextDeleteCleanup : public + CrashRecoveryContextCleanupBase<CrashRecoveryContextDeleteCleanup<T>, T> { +public: + CrashRecoveryContextDeleteCleanup(CrashRecoveryContext *context, T *resource) + : CrashRecoveryContextCleanupBase< + CrashRecoveryContextDeleteCleanup<T>, T>(context, resource) {} + + void recoverResources() override { delete this->resource; } +}; + +/// Cleanup handler that reclaims resource by calling its method 'Release'. +template <typename T> +class CrashRecoveryContextReleaseRefCleanup : public + CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, T> { +public: + CrashRecoveryContextReleaseRefCleanup(CrashRecoveryContext *context, + T *resource) + : CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, + T>(context, resource) {} + + void recoverResources() override { this->resource->Release(); } +}; + +/// Helper class for managing resource cleanups. +/// +/// \tparam T Type of resource been reclaimed. +/// \tparam Cleanup Class that defines how the resource is reclaimed. +/// +/// Clients create objects of this type in the code executed in a crash recovery +/// context to ensure that the resource will be reclaimed even in the case of +/// crash. For example: +/// +/// \code +/// void actual_work(void *) { +/// ... +/// std::unique_ptr<Resource> R(new Resource()); +/// CrashRecoveryContextCleanupRegistrar D(R.get()); +/// ... +/// } +/// +/// void foo() { +/// CrashRecoveryContext CRC; +/// +/// if (!CRC.RunSafely(actual_work, 0)) { +/// ... a crash was detected, report error to user ... +/// } +/// \endcode +/// +/// If the code of `actual_work` in the example above does not crash, the +/// destructor of CrashRecoveryContextCleanupRegistrar removes cleanup code from +/// the current CrashRecoveryContext and the resource is reclaimed by the +/// destructor of std::unique_ptr. If crash happens, destructors are not called +/// and the resource is reclaimed by cleanup object registered in the recovery +/// context by the constructor of CrashRecoveryContextCleanupRegistrar. +template <typename T, typename Cleanup = CrashRecoveryContextDeleteCleanup<T> > +class CrashRecoveryContextCleanupRegistrar { + CrashRecoveryContextCleanup *cleanup; + +public: + CrashRecoveryContextCleanupRegistrar(T *x) + : cleanup(Cleanup::create(x)) { + if (cleanup) + cleanup->getContext()->registerCleanup(cleanup); + } + + ~CrashRecoveryContextCleanupRegistrar() { unregister(); } + + void unregister() { + if (cleanup && !cleanup->cleanupFired) + cleanup->getContext()->unregisterCleanup(cleanup); + cleanup = nullptr; + } +}; +} // end namespace llvm + +#endif // LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DJB.h b/contrib/libs/llvm16/include/llvm/Support/DJB.h new file mode 100644 index 00000000000..ec0ad6214f3 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DJB.h @@ -0,0 +1,43 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/DJB.h ---DJB Hash --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains support for the DJ Bernstein hash function. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DJB_H +#define LLVM_SUPPORT_DJB_H + +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +/// The Bernstein hash function used by the DWARF accelerator tables. +inline uint32_t djbHash(StringRef Buffer, uint32_t H = 5381) { + for (unsigned char C : Buffer.bytes()) + H = (H << 5) + H + C; + return H; +} + +/// Computes the Bernstein hash after folding the input according to the Dwarf 5 +/// standard case folding rules. +uint32_t caseFoldingDjbHash(StringRef Buffer, uint32_t H = 5381); +} // namespace llvm + +#endif // LLVM_SUPPORT_DJB_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DOTGraphTraits.h b/contrib/libs/llvm16/include/llvm/Support/DOTGraphTraits.h new file mode 100644 index 00000000000..f893fc9d2b1 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DOTGraphTraits.h @@ -0,0 +1,183 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/DOTGraphTraits.h - Customize .dot output ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a template class that can be used to customize dot output +// graphs generated by the GraphWriter.h file. The default implementation of +// this file will produce a simple, but not very polished graph. By +// specializing this template, lots of customization opportunities are possible. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DOTGRAPHTRAITS_H +#define LLVM_SUPPORT_DOTGRAPHTRAITS_H + +#include <string> + +namespace llvm { + +/// DefaultDOTGraphTraits - This class provides the default implementations of +/// all of the DOTGraphTraits methods. If a specialization does not need to +/// override all methods here it should inherit so that it can get the default +/// implementations. +/// +struct DefaultDOTGraphTraits { +private: + bool IsSimple; + +protected: + bool isSimple() { + return IsSimple; + } + +public: + explicit DefaultDOTGraphTraits(bool simple=false) : IsSimple (simple) {} + + /// getGraphName - Return the label for the graph as a whole. Printed at the + /// top of the graph. + /// + template<typename GraphType> + static std::string getGraphName(const GraphType &) { return ""; } + + /// getGraphProperties - Return any custom properties that should be included + /// in the top level graph structure for dot. + /// + template<typename GraphType> + static std::string getGraphProperties(const GraphType &) { + return ""; + } + + /// renderGraphFromBottomUp - If this function returns true, the graph is + /// emitted bottom-up instead of top-down. This requires graphviz 2.0 to work + /// though. + static bool renderGraphFromBottomUp() { + return false; + } + + /// isNodeHidden - If the function returns true, the given node is not + /// displayed in the graph. + template <typename GraphType> + static bool isNodeHidden(const void *, const GraphType &) { + return false; + } + + // renderNodesUsingHTML - If the function returns true, nodes will be + // rendered using HTML-like labels which allows colors, etc in the nodes + // and the edge source labels. + static bool renderNodesUsingHTML() { return false; } + + /// getNodeLabel - Given a node and a pointer to the top level graph, return + /// the label to print in the node. + template<typename GraphType> + std::string getNodeLabel(const void *, const GraphType &) { + return ""; + } + + // getNodeIdentifierLabel - Returns a string representing the + // address or other unique identifier of the node. (Only used if + // non-empty.) + template <typename GraphType> + static std::string getNodeIdentifierLabel(const void *, const GraphType &) { + return ""; + } + + template<typename GraphType> + static std::string getNodeDescription(const void *, const GraphType &) { + return ""; + } + + /// If you want to specify custom node attributes, this is the place to do so + /// + template<typename GraphType> + static std::string getNodeAttributes(const void *, + const GraphType &) { + return ""; + } + + /// If you want to override the dot attributes printed for a particular edge, + /// override this method. + template<typename EdgeIter, typename GraphType> + static std::string getEdgeAttributes(const void *, EdgeIter, + const GraphType &) { + return ""; + } + + /// getEdgeSourceLabel - If you want to label the edge source itself, + /// implement this method. + template<typename EdgeIter> + static std::string getEdgeSourceLabel(const void *, EdgeIter) { + return ""; + } + + /// edgeTargetsEdgeSource - This method returns true if this outgoing edge + /// should actually target another edge source, not a node. If this method is + /// implemented, getEdgeTarget should be implemented. + template<typename EdgeIter> + static bool edgeTargetsEdgeSource(const void *, EdgeIter) { + return false; + } + + /// getEdgeTarget - If edgeTargetsEdgeSource returns true, this method is + /// called to determine which outgoing edge of Node is the target of this + /// edge. + template<typename EdgeIter> + static EdgeIter getEdgeTarget(const void *, EdgeIter I) { + return I; + } + + /// hasEdgeDestLabels - If this function returns true, the graph is able + /// to provide labels for edge destinations. + static bool hasEdgeDestLabels() { + return false; + } + + /// numEdgeDestLabels - If hasEdgeDestLabels, this function returns the + /// number of incoming edge labels the given node has. + static unsigned numEdgeDestLabels(const void *) { + return 0; + } + + /// getEdgeDestLabel - If hasEdgeDestLabels, this function returns the + /// incoming edge label with the given index in the given node. + static std::string getEdgeDestLabel(const void *, unsigned) { + return ""; + } + + /// addCustomGraphFeatures - If a graph is made up of more than just + /// straight-forward nodes and edges, this is the place to put all of the + /// custom stuff necessary. The GraphWriter object, instantiated with your + /// GraphType is passed in as an argument. You may call arbitrary methods on + /// it to add things to the output graph. + /// + template<typename GraphType, typename GraphWriter> + static void addCustomGraphFeatures(const GraphType &, GraphWriter &) {} +}; + + +/// DOTGraphTraits - Template class that can be specialized to customize how +/// graphs are converted to 'dot' graphs. When specializing, you may inherit +/// from DefaultDOTGraphTraits if you don't need to override everything. +/// +template <typename Ty> +struct DOTGraphTraits : public DefaultDOTGraphTraits { + DOTGraphTraits (bool simple=false) : DefaultDOTGraphTraits (simple) {} +}; + +} // End llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DXILOperationCommon.h b/contrib/libs/llvm16/include/llvm/Support/DXILOperationCommon.h new file mode 100644 index 00000000000..094124e2e2d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DXILOperationCommon.h @@ -0,0 +1,74 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- DXILOperationCommon.h - DXIL Operation ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is created to share common definitions used by both the +// DXILOpBuilder and the table +// generator. +// Documentation for DXIL can be found in +// https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DXILOPERATIONCOMMON_H +#define LLVM_SUPPORT_DXILOPERATIONCOMMON_H + +#include "llvm/ADT/StringSwitch.h" + +namespace llvm { +namespace dxil { + +enum class ParameterKind : uint8_t { + INVALID = 0, + VOID, + HALF, + FLOAT, + DOUBLE, + I1, + I8, + I16, + I32, + I64, + OVERLOAD, + CBUFFER_RET, + RESOURCE_RET, + DXIL_HANDLE, +}; + +inline ParameterKind parameterTypeNameToKind(StringRef Name) { + return StringSwitch<ParameterKind>(Name) + .Case("void", ParameterKind::VOID) + .Case("half", ParameterKind::HALF) + .Case("float", ParameterKind::FLOAT) + .Case("double", ParameterKind::DOUBLE) + .Case("i1", ParameterKind::I1) + .Case("i8", ParameterKind::I8) + .Case("i16", ParameterKind::I16) + .Case("i32", ParameterKind::I32) + .Case("i64", ParameterKind::I64) + .Case("$o", ParameterKind::OVERLOAD) + .Case("dx.types.Handle", ParameterKind::DXIL_HANDLE) + .Case("dx.types.CBufRet", ParameterKind::CBUFFER_RET) + .Case("dx.types.ResRet", ParameterKind::RESOURCE_RET) + .Default(ParameterKind::INVALID); +} + +} // namespace dxil +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DataExtractor.h b/contrib/libs/llvm16/include/llvm/Support/DataExtractor.h new file mode 100644 index 00000000000..db88ce0be8f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DataExtractor.h @@ -0,0 +1,720 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- DataExtractor.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DATAEXTRACTOR_H +#define LLVM_SUPPORT_DATAEXTRACTOR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +/// An auxiliary type to facilitate extraction of 3-byte entities. +struct Uint24 { + uint8_t Bytes[3]; + Uint24(uint8_t U) { + Bytes[0] = Bytes[1] = Bytes[2] = U; + } + Uint24(uint8_t U0, uint8_t U1, uint8_t U2) { + Bytes[0] = U0; Bytes[1] = U1; Bytes[2] = U2; + } + uint32_t getAsUint32(bool IsLittleEndian) const { + int LoIx = IsLittleEndian ? 0 : 2; + return Bytes[LoIx] + (Bytes[1] << 8) + (Bytes[2-LoIx] << 16); + } +}; + +using uint24_t = Uint24; +static_assert(sizeof(uint24_t) == 3, "sizeof(uint24_t) != 3"); + +/// Needed by swapByteOrder(). +inline uint24_t getSwappedBytes(uint24_t C) { + return uint24_t(C.Bytes[2], C.Bytes[1], C.Bytes[0]); +} + +class DataExtractor { + StringRef Data; + uint8_t IsLittleEndian; + uint8_t AddressSize; +public: + /// A class representing a position in a DataExtractor, as well as any error + /// encountered during extraction. It enables one to extract a sequence of + /// values without error-checking and then checking for errors in bulk at the + /// end. The class holds an Error object, so failing to check the result of + /// the parse will result in a runtime error. The error flag is sticky and + /// will cause all subsequent extraction functions to fail without even + /// attempting to parse and without updating the Cursor offset. After clearing + /// the error flag, one can again use the Cursor object for parsing. + class Cursor { + uint64_t Offset; + Error Err; + + friend class DataExtractor; + + public: + /// Construct a cursor for extraction from the given offset. + explicit Cursor(uint64_t Offset) : Offset(Offset), Err(Error::success()) {} + + /// Checks whether the cursor is valid (i.e. no errors were encountered). In + /// case of errors, this does not clear the error flag -- one must call + /// takeError() instead. + explicit operator bool() { return !Err; } + + /// Return the current position of this Cursor. In the error state this is + /// the position of the Cursor before the first error was encountered. + uint64_t tell() const { return Offset; } + + /// Set the cursor to the new offset. This does not impact the error state. + void seek(uint64_t NewOffSet) { Offset = NewOffSet; } + + /// Return error contained inside this Cursor, if any. Clears the internal + /// Cursor state. + Error takeError() { return std::move(Err); } + }; + + /// Construct with a buffer that is owned by the caller. + /// + /// This constructor allows us to use data that is owned by the + /// caller. The data must stay around as long as this object is + /// valid. + DataExtractor(StringRef Data, bool IsLittleEndian, uint8_t AddressSize) + : Data(Data), IsLittleEndian(IsLittleEndian), AddressSize(AddressSize) {} + DataExtractor(ArrayRef<uint8_t> Data, bool IsLittleEndian, + uint8_t AddressSize) + : Data(StringRef(reinterpret_cast<const char *>(Data.data()), + Data.size())), + IsLittleEndian(IsLittleEndian), AddressSize(AddressSize) {} + + /// Get the data pointed to by this extractor. + StringRef getData() const { return Data; } + /// Get the endianness for this extractor. + bool isLittleEndian() const { return IsLittleEndian; } + /// Get the address size for this extractor. + uint8_t getAddressSize() const { return AddressSize; } + /// Set the address size for this extractor. + void setAddressSize(uint8_t Size) { AddressSize = Size; } + + /// Extract a C string from \a *offset_ptr. + /// + /// Returns a pointer to a C String from the data at the offset + /// pointed to by \a offset_ptr. A variable length NULL terminated C + /// string will be extracted and the \a offset_ptr will be + /// updated with the offset of the byte that follows the NULL + /// terminator byte. + /// + /// @param[in,out] OffsetPtr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// NULL will be returned. + const char *getCStr(uint64_t *OffsetPtr, Error *Err = nullptr) const { + return getCStrRef(OffsetPtr, Err).data(); + } + + /// Extract a C string from the location given by the cursor. In case of an + /// extraction error, or if the cursor is already in an error state, a + /// nullptr is returned. + const char *getCStr(Cursor &C) const { return getCStrRef(C).data(); } + + /// Extract a C string from \a *offset_ptr. + /// + /// Returns a StringRef for the C String from the data at the offset + /// pointed to by \a offset_ptr. A variable length NULL terminated C + /// string will be extracted and the \a offset_ptr will be + /// updated with the offset of the byte that follows the NULL + /// terminator byte. + /// + /// \param[in,out] OffsetPtr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// \return + /// A StringRef for the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// a default-initialized StringRef will be returned. + StringRef getCStrRef(uint64_t *OffsetPtr, Error *Err = nullptr) const; + + /// Extract a C string (as a StringRef) from the location given by the cursor. + /// In case of an extraction error, or if the cursor is already in an error + /// state, a default-initialized StringRef is returned. + StringRef getCStrRef(Cursor &C) const { + return getCStrRef(&C.Offset, &C.Err); + } + + /// Extract a fixed length string from \a *OffsetPtr and consume \a Length + /// bytes. + /// + /// Returns a StringRef for the string from the data at the offset + /// pointed to by \a OffsetPtr. A fixed length C string will be extracted + /// and the \a OffsetPtr will be advanced by \a Length bytes. + /// + /// \param[in,out] OffsetPtr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// \param[in] Length + /// The length of the fixed length string to extract. If there are not + /// enough bytes in the data to extract the full string, the offset will + /// be left unmodified. + /// + /// \param[in] TrimChars + /// A set of characters to trim from the end of the string. Fixed length + /// strings are commonly either NULL terminated by one or more zero + /// bytes. Some clients have one or more spaces at the end of the string, + /// but a good default is to trim the NULL characters. + /// + /// \return + /// A StringRef for the C string value in the data. If the offset + /// pointed to by \a OffsetPtr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// a default-initialized StringRef will be returned. + StringRef getFixedLengthString(uint64_t *OffsetPtr, + uint64_t Length, StringRef TrimChars = {"\0", 1}) const; + + /// Extract a fixed number of bytes from the specified offset. + /// + /// Returns a StringRef for the bytes from the data at the offset + /// pointed to by \a OffsetPtr. A fixed length C string will be extracted + /// and the \a OffsetPtr will be advanced by \a Length bytes. + /// + /// \param[in,out] OffsetPtr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// \param[in] Length + /// The number of bytes to extract. If there are not enough bytes in the + /// data to extract all of the bytes, the offset will be left unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// \return + /// A StringRef for the extracted bytes. If the offset pointed to by + /// \a OffsetPtr is out of bounds, or if the offset plus the length + /// is out of bounds, a default-initialized StringRef will be returned. + StringRef getBytes(uint64_t *OffsetPtr, uint64_t Length, + Error *Err = nullptr) const; + + /// Extract a fixed number of bytes from the location given by the cursor. In + /// case of an extraction error, or if the cursor is already in an error + /// state, a default-initialized StringRef is returned. + StringRef getBytes(Cursor &C, uint64_t Length) { + return getBytes(&C.Offset, Length, &C.Err); + } + + /// Extract an unsigned integer of size \a byte_size from \a + /// *offset_ptr. + /// + /// Extract a single unsigned integer value and update the offset + /// pointed to by \a offset_ptr. The size of the extracted integer + /// is specified by the \a byte_size argument. \a byte_size should + /// have a value greater than or equal to one and less than or equal + /// to eight since the return value is 64 bits wide. Any + /// \a byte_size values less than 1 or greater than 8 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The unsigned integer value that was extracted, or zero on + /// failure. + uint64_t getUnsigned(uint64_t *offset_ptr, uint32_t byte_size, + Error *Err = nullptr) const; + + /// Extract an unsigned integer of the given size from the location given by + /// the cursor. In case of an extraction error, or if the cursor is already in + /// an error state, zero is returned. + uint64_t getUnsigned(Cursor &C, uint32_t Size) const { + return getUnsigned(&C.Offset, Size, &C.Err); + } + + /// Extract an signed integer of size \a byte_size from \a *offset_ptr. + /// + /// Extract a single signed integer value (sign extending if required) + /// and update the offset pointed to by \a offset_ptr. The size of + /// the extracted integer is specified by the \a byte_size argument. + /// \a byte_size should have a value greater than or equal to one + /// and less than or equal to eight since the return value is 64 + /// bits wide. Any \a byte_size values less than 1 or greater than + /// 8 will result in nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] size + /// The size in bytes of the integer to extract. + /// + /// @return + /// The sign extended signed integer value that was extracted, + /// or zero on failure. + int64_t getSigned(uint64_t *offset_ptr, uint32_t size) const; + + //------------------------------------------------------------------ + /// Extract an pointer from \a *offset_ptr. + /// + /// Extract a single pointer from the data and update the offset + /// pointed to by \a offset_ptr. The size of the extracted pointer + /// is \a getAddressSize(), so the address size has to be + /// set correctly prior to extracting any pointer values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted pointer value as a 64 integer. + uint64_t getAddress(uint64_t *offset_ptr) const { + return getUnsigned(offset_ptr, AddressSize); + } + + /// Extract a pointer-sized unsigned integer from the location given by the + /// cursor. In case of an extraction error, or if the cursor is already in + /// an error state, zero is returned. + uint64_t getAddress(Cursor &C) const { return getUnsigned(C, AddressSize); } + + /// Extract a uint8_t value from \a *offset_ptr. + /// + /// Extract a single uint8_t from the binary data at the offset + /// pointed to by \a offset_ptr, and advance the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted uint8_t value. + uint8_t getU8(uint64_t *offset_ptr, Error *Err = nullptr) const; + + /// Extract a single uint8_t value from the location given by the cursor. In + /// case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + uint8_t getU8(Cursor &C) const { return getU8(&C.Offset, &C.Err); } + + /// Extract \a count uint8_t values from \a *offset_ptr. + /// + /// Extract \a count uint8_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint8_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint8_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + uint8_t *getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const; + + /// Extract \a Count uint8_t values from the location given by the cursor and + /// store them into the destination buffer. In case of an extraction error, or + /// if the cursor is already in an error state, a nullptr is returned and the + /// destination buffer is left unchanged. + uint8_t *getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const; + + /// Extract \a Count uint8_t values from the location given by the cursor and + /// store them into the destination vector. The vector is resized to fit the + /// extracted data. In case of an extraction error, or if the cursor is + /// already in an error state, the destination vector is left unchanged and + /// cursor is placed into an error state. + void getU8(Cursor &C, SmallVectorImpl<uint8_t> &Dst, uint32_t Count) const { + if (isValidOffsetForDataOfSize(C.Offset, Count)) + Dst.resize(Count); + + // This relies on the fact that getU8 will not attempt to write to the + // buffer if isValidOffsetForDataOfSize(C.Offset, Count) is false. + getU8(C, Dst.data(), Count); + } + + //------------------------------------------------------------------ + /// Extract a uint16_t value from \a *offset_ptr. + /// + /// Extract a single uint16_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted uint16_t value. + //------------------------------------------------------------------ + uint16_t getU16(uint64_t *offset_ptr, Error *Err = nullptr) const; + + /// Extract a single uint16_t value from the location given by the cursor. In + /// case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + uint16_t getU16(Cursor &C) const { return getU16(&C.Offset, &C.Err); } + + /// Extract \a count uint16_t values from \a *offset_ptr. + /// + /// Extract \a count uint16_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint16_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint16_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + uint16_t *getU16(uint64_t *offset_ptr, uint16_t *dst, uint32_t count) const; + + /// Extract a 24-bit unsigned value from \a *offset_ptr and return it + /// in a uint32_t. + /// + /// Extract 3 bytes from the binary data at the offset pointed to by + /// \a offset_ptr, construct a uint32_t from them and update the offset + /// on success. + /// + /// @param[in,out] OffsetPtr + /// A pointer to an offset within the data that will be advanced + /// by the 3 bytes if the value is extracted correctly. If the offset + /// is out of bounds or there are not enough bytes to extract this value, + /// the offset will be left unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted 24-bit value represented in a uint32_t. + uint32_t getU24(uint64_t *OffsetPtr, Error *Err = nullptr) const; + + /// Extract a single 24-bit unsigned value from the location given by the + /// cursor. In case of an extraction error, or if the cursor is already in an + /// error state, zero is returned. + uint32_t getU24(Cursor &C) const { return getU24(&C.Offset, &C.Err); } + + /// Extract a uint32_t value from \a *offset_ptr. + /// + /// Extract a single uint32_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted uint32_t value. + uint32_t getU32(uint64_t *offset_ptr, Error *Err = nullptr) const; + + /// Extract a single uint32_t value from the location given by the cursor. In + /// case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + uint32_t getU32(Cursor &C) const { return getU32(&C.Offset, &C.Err); } + + /// Extract \a count uint32_t values from \a *offset_ptr. + /// + /// Extract \a count uint32_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint32_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint32_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + uint32_t *getU32(uint64_t *offset_ptr, uint32_t *dst, uint32_t count) const; + + /// Extract a uint64_t value from \a *offset_ptr. + /// + /// Extract a single uint64_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted uint64_t value. + uint64_t getU64(uint64_t *offset_ptr, Error *Err = nullptr) const; + + /// Extract a single uint64_t value from the location given by the cursor. In + /// case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + uint64_t getU64(Cursor &C) const { return getU64(&C.Offset, &C.Err); } + + /// Extract \a count uint64_t values from \a *offset_ptr. + /// + /// Extract \a count uint64_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint64_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint64_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + uint64_t *getU64(uint64_t *offset_ptr, uint64_t *dst, uint32_t count) const; + + /// Extract a signed LEB128 value from \a *offset_ptr. + /// + /// Extracts an signed LEB128 number from this object's data + /// starting at the offset pointed to by \a offset_ptr. The offset + /// pointed to by \a offset_ptr will be updated with the offset of + /// the byte following the last extracted byte. + /// + /// @param[in,out] OffsetPtr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted signed integer value. + int64_t getSLEB128(uint64_t *OffsetPtr, Error *Err = nullptr) const; + + /// Extract an signed LEB128 value from the location given by the cursor. + /// In case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + int64_t getSLEB128(Cursor &C) const { return getSLEB128(&C.Offset, &C.Err); } + + /// Extract a unsigned LEB128 value from \a *offset_ptr. + /// + /// Extracts an unsigned LEB128 number from this object's data + /// starting at the offset pointed to by \a offset_ptr. The offset + /// pointed to by \a offset_ptr will be updated with the offset of + /// the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// + /// @return + /// The extracted unsigned integer value. + uint64_t getULEB128(uint64_t *offset_ptr, llvm::Error *Err = nullptr) const; + + /// Extract an unsigned LEB128 value from the location given by the cursor. + /// In case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + uint64_t getULEB128(Cursor &C) const { return getULEB128(&C.Offset, &C.Err); } + + /// Advance the Cursor position by the given number of bytes. No-op if the + /// cursor is in an error state. + void skip(Cursor &C, uint64_t Length) const; + + /// Return true iff the cursor is at the end of the buffer, regardless of the + /// error state of the cursor. The only way both eof and error states can be + /// true is if one attempts a read while the cursor is at the very end of the + /// data buffer. + bool eof(const Cursor &C) const { return size() == C.Offset; } + + /// Test the validity of \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset into the data in this + /// object, \b false otherwise. + bool isValidOffset(uint64_t offset) const { return size() > offset; } + + /// Test the availability of \a length bytes of data from \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset and there are \a + /// length bytes available at that offset, \b false otherwise. + bool isValidOffsetForDataOfSize(uint64_t offset, uint64_t length) const { + return offset + length >= offset && isValidOffset(offset + length - 1); + } + + /// Test the availability of enough bytes of data for a pointer from + /// \a offset. The size of a pointer is \a getAddressSize(). + /// + /// @return + /// \b true if \a offset is a valid offset and there are enough + /// bytes for a pointer available at that offset, \b false + /// otherwise. + bool isValidOffsetForAddress(uint64_t offset) const { + return isValidOffsetForDataOfSize(offset, AddressSize); + } + + /// Return the number of bytes in the underlying buffer. + size_t size() const { return Data.size(); } + +protected: + // Make it possible for subclasses to access these fields without making them + // public. + static uint64_t &getOffset(Cursor &C) { return C.Offset; } + static Error &getError(Cursor &C) { return C.Err; } + +private: + /// If it is possible to read \a Size bytes at offset \a Offset, returns \b + /// true. Otherwise, returns \b false. If \a E is not nullptr, also sets the + /// error object to indicate an error. + bool prepareRead(uint64_t Offset, uint64_t Size, Error *E) const; + + template <typename T> T getU(uint64_t *OffsetPtr, Error *Err) const; + template <typename T> + T *getUs(uint64_t *OffsetPtr, T *Dst, uint32_t Count, Error *Err) const; +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DataTypes.h b/contrib/libs/llvm16/include/llvm/Support/DataTypes.h new file mode 100644 index 00000000000..a825219bb70 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DataTypes.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/DataTypes.h - Define fixed size types ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Due to layering constraints (Support depends on llvm-c) this is a thin +// wrapper around the implementation that lives in llvm-c, though most clients +// can/should think of this as being provided by Support for simplicity (not +// many clients are aware of their dependency on llvm-c). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DATATYPES_H +#define LLVM_SUPPORT_DATATYPES_H + +#include "llvm-c/DataTypes.h" + +#endif // LLVM_SUPPORT_DATATYPES_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Debug.h b/contrib/libs/llvm16/include/llvm/Support/Debug.h new file mode 100644 index 00000000000..7c2a2139e6f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Debug.h @@ -0,0 +1,116 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Debug.h - Easy way to add debug output ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a handy way of adding debugging information to your +// code, without it being enabled all of the time, and without having to add +// command line options to enable it. +// +// In particular, just wrap your code with the LLVM_DEBUG() macro, and it will +// be enabled automatically if you specify '-debug' on the command-line. +// LLVM_DEBUG() requires the DEBUG_TYPE macro to be defined. Set it to "foo" +// specify that your debug code belongs to class "foo". Be careful that you only +// do this after including Debug.h and not around any #include of headers. +// Headers should define and undef the macro acround the code that needs to use +// the LLVM_DEBUG() macro. Then, on the command line, you can specify +// '-debug-only=foo' to enable JUST the debug information for the foo class. +// +// When compiling without assertions, the -debug-* options and all code in +// LLVM_DEBUG() statements disappears, so it does not affect the runtime of the +// code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DEBUG_H +#define LLVM_SUPPORT_DEBUG_H + +namespace llvm { + +class raw_ostream; + +#ifndef NDEBUG + +/// isCurrentDebugType - Return true if the specified string is the debug type +/// specified on the command line, or if none was specified on the command line +/// with the -debug-only=X option. +/// +bool isCurrentDebugType(const char *Type); + +/// setCurrentDebugType - Set the current debug type, as if the -debug-only=X +/// option were specified. Note that DebugFlag also needs to be set to true for +/// debug output to be produced. +/// +void setCurrentDebugType(const char *Type); + +/// setCurrentDebugTypes - Set the current debug type, as if the +/// -debug-only=X,Y,Z option were specified. Note that DebugFlag +/// also needs to be set to true for debug output to be produced. +/// +void setCurrentDebugTypes(const char **Types, unsigned Count); + +/// DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug +/// information. In the '-debug' option is specified on the commandline, and if +/// this is a debug build, then the code specified as the option to the macro +/// will be executed. Otherwise it will not be. Example: +/// +/// DEBUG_WITH_TYPE("bitset", dbgs() << "Bitset contains: " << Bitset << "\n"); +/// +/// This will emit the debug information if -debug is present, and -debug-only +/// is not specified, or is specified as "bitset". +#define DEBUG_WITH_TYPE(TYPE, X) \ + do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE)) { X; } \ + } while (false) + +#else +#define isCurrentDebugType(X) (false) +#define setCurrentDebugType(X) do { (void)(X); } while (false) +#define setCurrentDebugTypes(X, N) do { (void)(X); (void)(N); } while (false) +#define DEBUG_WITH_TYPE(TYPE, X) do { } while (false) +#endif + +/// This boolean is set to true if the '-debug' command line option +/// is specified. This should probably not be referenced directly, instead, use +/// the DEBUG macro below. +/// +extern bool DebugFlag; + +/// EnableDebugBuffering - This defaults to false. If true, the debug +/// stream will install signal handlers to dump any buffered debug +/// output. It allows clients to selectively allow the debug stream +/// to install signal handlers if they are certain there will be no +/// conflict. +/// +extern bool EnableDebugBuffering; + +/// dbgs() - This returns a reference to a raw_ostream for debugging +/// messages. If debugging is disabled it returns errs(). Use it +/// like: dbgs() << "foo" << "bar"; +raw_ostream &dbgs(); + +// DEBUG macro - This macro should be used by passes to emit debug information. +// In the '-debug' option is specified on the commandline, and if this is a +// debug build, then the code specified as the option to the macro will be +// executed. Otherwise it will not be. Example: +// +// LLVM_DEBUG(dbgs() << "Bitset contains: " << Bitset << "\n"); +// +#define LLVM_DEBUG(X) DEBUG_WITH_TYPE(DEBUG_TYPE, X) + +} // end namespace llvm + +#endif // LLVM_SUPPORT_DEBUG_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DebugCounter.h b/contrib/libs/llvm16/include/llvm/Support/DebugCounter.h new file mode 100644 index 00000000000..f37163af050 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DebugCounter.h @@ -0,0 +1,198 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/DebugCounter.h - Debug counter support ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file provides an implementation of debug counters. Debug +/// counters are a tool that let you narrow down a miscompilation to a specific +/// thing happening. +/// +/// To give a use case: Imagine you have a file, very large, and you +/// are trying to understand the minimal transformation that breaks it. Bugpoint +/// and bisection is often helpful here in narrowing it down to a specific pass, +/// but it's still a very large file, and a very complicated pass to try to +/// debug. That is where debug counting steps in. You can instrument the pass +/// with a debug counter before it does a certain thing, and depending on the +/// counts, it will either execute that thing or not. The debug counter itself +/// consists of a skip and a count. Skip is the number of times shouldExecute +/// needs to be called before it returns true. Count is the number of times to +/// return true once Skip is 0. So a skip=47, count=2 ,would skip the first 47 +/// executions by returning false from shouldExecute, then execute twice, and +/// then return false again. +/// Note that a counter set to a negative number will always execute. +/// For a concrete example, during predicateinfo creation, the renaming pass +/// replaces each use with a renamed use. +//// +/// If I use DEBUG_COUNTER to create a counter called "predicateinfo", and +/// variable name RenameCounter, and then instrument this renaming with a debug +/// counter, like so: +/// +/// if (!DebugCounter::shouldExecute(RenameCounter) +/// <continue or return or whatever not executing looks like> +/// +/// Now I can, from the command line, make it rename or not rename certain uses +/// by setting the skip and count. +/// So for example +/// bin/opt -debug-counter=predicateinfo-skip=47,predicateinfo-count=1 +/// will skip renaming the first 47 uses, then rename one, then skip the rest. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DEBUGCOUNTER_H +#define LLVM_SUPPORT_DEBUGCOUNTER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/UniqueVector.h" +#include "llvm/Support/Debug.h" +#include <string> + +namespace llvm { + +class raw_ostream; + +class DebugCounter { +public: + /// Returns a reference to the singleton instance. + static DebugCounter &instance(); + + // Used by the command line option parser to push a new value it parsed. + void push_back(const std::string &); + + // Register a counter with the specified name. + // + // FIXME: Currently, counter registration is required to happen before command + // line option parsing. The main reason to register counters is to produce a + // nice list of them on the command line, but i'm not sure this is worth it. + static unsigned registerCounter(StringRef Name, StringRef Desc) { + return instance().addCounter(std::string(Name), std::string(Desc)); + } + inline static bool shouldExecute(unsigned CounterName) { + if (!isCountingEnabled()) + return true; + + auto &Us = instance(); + auto Result = Us.Counters.find(CounterName); + if (Result != Us.Counters.end()) { + auto &CounterInfo = Result->second; + ++CounterInfo.Count; + + // We only execute while the Skip is not smaller than Count, + // and the StopAfter + Skip is larger than Count. + // Negative counters always execute. + if (CounterInfo.Skip < 0) + return true; + if (CounterInfo.Skip >= CounterInfo.Count) + return false; + if (CounterInfo.StopAfter < 0) + return true; + return CounterInfo.StopAfter + CounterInfo.Skip >= CounterInfo.Count; + } + // Didn't find the counter, should we warn? + return true; + } + + // Return true if a given counter had values set (either programatically or on + // the command line). This will return true even if those values are + // currently in a state where the counter will always execute. + static bool isCounterSet(unsigned ID) { + return instance().Counters[ID].IsSet; + } + + // Return the Count for a counter. This only works for set counters. + static int64_t getCounterValue(unsigned ID) { + auto &Us = instance(); + auto Result = Us.Counters.find(ID); + assert(Result != Us.Counters.end() && "Asking about a non-set counter"); + return Result->second.Count; + } + + // Set a registered counter to a given Count value. + static void setCounterValue(unsigned ID, int64_t Count) { + auto &Us = instance(); + Us.Counters[ID].Count = Count; + } + + // Dump or print the current counter set into llvm::dbgs(). + LLVM_DUMP_METHOD void dump() const; + + void print(raw_ostream &OS) const; + + // Get the counter ID for a given named counter, or return 0 if none is found. + unsigned getCounterId(const std::string &Name) const { + return RegisteredCounters.idFor(Name); + } + + // Return the number of registered counters. + unsigned int getNumCounters() const { return RegisteredCounters.size(); } + + // Return the name and description of the counter with the given ID. + std::pair<std::string, std::string> getCounterInfo(unsigned ID) const { + return std::make_pair(RegisteredCounters[ID], Counters.lookup(ID).Desc); + } + + // Iterate through the registered counters + typedef UniqueVector<std::string> CounterVector; + CounterVector::const_iterator begin() const { + return RegisteredCounters.begin(); + } + CounterVector::const_iterator end() const { return RegisteredCounters.end(); } + + // Force-enables counting all DebugCounters. + // + // Since DebugCounters are incompatible with threading (not only do they not + // make sense, but we'll also see data races), this should only be used in + // contexts where we're certain we won't spawn threads. + static void enableAllCounters() { instance().Enabled = true; } + + static bool isCountingEnabled() { +// Compile to nothing when debugging is off +#ifdef NDEBUG + return false; +#else + return instance().Enabled; +#endif + } + +private: + unsigned addCounter(const std::string &Name, const std::string &Desc) { + unsigned Result = RegisteredCounters.insert(Name); + Counters[Result] = {}; + Counters[Result].Desc = Desc; + return Result; + } + // Struct to store counter info. + struct CounterInfo { + int64_t Count = 0; + int64_t Skip = 0; + int64_t StopAfter = -1; + bool IsSet = false; + std::string Desc; + }; + DenseMap<unsigned, CounterInfo> Counters; + CounterVector RegisteredCounters; + + // Whether we should do DebugCounting at all. DebugCounters aren't + // thread-safe, so this should always be false in multithreaded scenarios. + bool Enabled = false; +}; + +#define DEBUG_COUNTER(VARNAME, COUNTERNAME, DESC) \ + static const unsigned VARNAME = \ + DebugCounter::registerCounter(COUNTERNAME, DESC) + +} // namespace llvm +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Discriminator.h b/contrib/libs/llvm16/include/llvm/Support/Discriminator.h new file mode 100644 index 00000000000..8f957f7ea8f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Discriminator.h @@ -0,0 +1,144 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===---- llvm/Support/Discriminator.h -- Discriminator Utils ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the constants and utility functions for discriminators. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DISCRIMINATOR_H +#define LLVM_SUPPORT_DISCRIMINATOR_H + +#include "llvm/Support/Error.h" +#include <assert.h> + +// Utility functions for encoding / decoding discriminators. +/// With a given unsigned int \p U, use up to 13 bits to represent it. +/// old_bit 1~5 --> new_bit 1~5 +/// old_bit 6~12 --> new_bit 7~13 +/// new_bit_6 is 0 if higher bits (7~13) are all 0 +static inline unsigned getPrefixEncodingFromUnsigned(unsigned U) { + U &= 0xfff; + return U > 0x1f ? (((U & 0xfe0) << 1) | (U & 0x1f) | 0x20) : U; +} + +/// Reverse transformation as getPrefixEncodingFromUnsigned. +static inline unsigned getUnsignedFromPrefixEncoding(unsigned U) { + if (U & 1) + return 0; + U >>= 1; + return (U & 0x20) ? (((U >> 1) & 0xfe0) | (U & 0x1f)) : (U & 0x1f); +} + +/// Returns the next component stored in discriminator. +static inline unsigned getNextComponentInDiscriminator(unsigned D) { + if ((D & 1) == 0) + return D >> ((D & 0x40) ? 14 : 7); + else + return D >> 1; +} + +static inline unsigned encodeComponent(unsigned C) { + return (C == 0) ? 1U : (getPrefixEncodingFromUnsigned(C) << 1); +} + +static inline unsigned encodingBits(unsigned C) { + return (C == 0) ? 1 : (C > 0x1f ? 14 : 7); +} + +// Some constants used in FS Discriminators. +// +namespace llvm { +namespace sampleprof { +enum FSDiscriminatorPass { + Base = 0, + Pass0 = 0, + Pass1 = 1, + Pass2 = 2, + Pass3 = 3, + Pass4 = 4, + PassLast = 4, +}; +} // namespace sampleprof + +using namespace sampleprof; + +// The number of bits reserved for the base discrimininator. The base +// discriminaitor starts from bit 0. +static const unsigned BaseDiscriminatorBitWidth = 8; + +// The number of bits reserved for each FS discriminator pass. +static const unsigned FSDiscriminatorBitWidth = 6; + +// Return the number of FS passes, excluding the pass adding the base +// discriminators. +// The number of passes for FS discriminators. Note that the total +// number of discriminaitor bits, i.e. +// BaseDiscriminatorBitWidth +// + FSDiscriminatorBitWidth * getNumFSPasses() +// needs to fit in an unsigned int type. +static inline unsigned getNumFSPasses() { + return static_cast<unsigned>(FSDiscriminatorPass::PassLast); +} + +// Return the ending bit for FSPass P. +static inline unsigned getFSPassBitEnd(FSDiscriminatorPass P) { + unsigned I = static_cast<unsigned>(P); + assert(I <= getNumFSPasses() && "Invalid FS discriminator pass number."); + return BaseDiscriminatorBitWidth + I * FSDiscriminatorBitWidth - 1; +} + +// Return the begining bit for FSPass P. +static inline unsigned getFSPassBitBegin(FSDiscriminatorPass P) { + if (P == FSDiscriminatorPass::Base) + return 0; + unsigned I = static_cast<unsigned>(P); + assert(I <= getNumFSPasses() && "Invalid FS discriminator pass number."); + return getFSPassBitEnd(static_cast<FSDiscriminatorPass>(I - 1)) + 1; +} + +// Return the beginning bit for the last FSPass. +static inline int getLastFSPassBitBegin() { + return getFSPassBitBegin(static_cast<FSDiscriminatorPass>(getNumFSPasses())); +} + +// Return the ending bit for the last FSPass. +static inline unsigned getLastFSPassBitEnd() { + return getFSPassBitEnd(static_cast<FSDiscriminatorPass>(getNumFSPasses())); +} + +// Return the beginning bit for the base (first) FSPass. +static inline unsigned getBaseFSBitBegin() { return 0; } + +// Return the ending bit for the base (first) FSPass. +static inline unsigned getBaseFSBitEnd() { + return BaseDiscriminatorBitWidth - 1; +} + +// Set bits in range of [0 .. n] to 1. Used in FS Discriminators. +static inline unsigned getN1Bits(int N) { + // Work around the g++ bug that folding "(1U << (N + 1)) - 1" to 0. + if (N == 31) + return 0xFFFFFFFF; + assert((N < 32) && "N is invalid"); + return (1U << (N + 1)) - 1; +} + +} // namespace llvm + +#endif /* LLVM_SUPPORT_DISCRIMINATOR_H */ + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DivisionByConstantInfo.h b/contrib/libs/llvm16/include/llvm/Support/DivisionByConstantInfo.h new file mode 100644 index 00000000000..698eda798cc --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DivisionByConstantInfo.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/DivisionByConstantInfo.h ---------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// This file implements support for optimizing divisions by a constant +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DIVISIONBYCONSTANTINFO_H +#define LLVM_SUPPORT_DIVISIONBYCONSTANTINFO_H + +#include "llvm/ADT/APInt.h" + +namespace llvm { + +/// Magic data for optimising signed division by a constant. +struct SignedDivisionByConstantInfo { + static SignedDivisionByConstantInfo get(const APInt &D); + APInt Magic; ///< magic number + unsigned ShiftAmount; ///< shift amount +}; + +/// Magic data for optimising unsigned division by a constant. +struct UnsignedDivisionByConstantInfo { + static UnsignedDivisionByConstantInfo + get(const APInt &D, unsigned LeadingZeros = 0, + bool AllowEvenDivisorOptimization = true); + APInt Magic; ///< magic number + bool IsAdd; ///< add indicator + unsigned PostShift; ///< post-shift amount + unsigned PreShift; ///< pre-shift amount +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Duration.h b/contrib/libs/llvm16/include/llvm/Support/Duration.h new file mode 100644 index 00000000000..6a8372a4d86 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Duration.h @@ -0,0 +1,39 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- Duration.h - wrapper around std::chrono::Duration ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The sole purpose of this file is to avoid the dependency on <chrono> in +// raw_ostream. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DURATION_H +#define LLVM_SUPPORT_DURATION_H + +#include <chrono> + +namespace llvm { +class Duration { + std::chrono::milliseconds Value; + public: + Duration(std::chrono::milliseconds Value) : Value(Value) {} + std::chrono::milliseconds getDuration() const { return Value; } +}; +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/DynamicLibrary.h b/contrib/libs/llvm16/include/llvm/Support/DynamicLibrary.h new file mode 100644 index 00000000000..98ca1ae44fa --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/DynamicLibrary.h @@ -0,0 +1,165 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/DynamicLibrary.h - Portable Dynamic Library -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the sys::DynamicLibrary class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_DYNAMICLIBRARY_H +#define LLVM_SUPPORT_DYNAMICLIBRARY_H + +#include <string> + +namespace llvm { + +class StringRef; + +namespace sys { + +/// This class provides a portable interface to dynamic libraries which also +/// might be known as shared libraries, shared objects, dynamic shared +/// objects, or dynamic link libraries. Regardless of the terminology or the +/// operating system interface, this class provides a portable interface that +/// allows dynamic libraries to be loaded and searched for externally +/// defined symbols. This is typically used to provide "plug-in" support. +/// It also allows for symbols to be defined which don't live in any library, +/// but rather the main program itself, useful on Windows where the main +/// executable cannot be searched. +class DynamicLibrary { + // Placeholder whose address represents an invalid library. + // We use this instead of NULL or a pointer-int pair because the OS library + // might define 0 or 1 to be "special" handles, such as "search all". + static char Invalid; + + // Opaque data used to interface with OS-specific dynamic library handling. + void *Data; + +public: + explicit DynamicLibrary(void *data = &Invalid) : Data(data) {} + + /// Return the OS specific handle value. + void *getOSSpecificHandle() const { return Data; } + + /// Returns true if the object refers to a valid library. + bool isValid() const { return Data != &Invalid; } + + /// Searches through the library for the symbol \p symbolName. If it is + /// found, the address of that symbol is returned. If not, NULL is returned. + /// Note that NULL will also be returned if the library failed to load. + /// Use isValid() to distinguish these cases if it is important. + /// Note that this will \e not search symbols explicitly registered by + /// AddSymbol(). + void *getAddressOfSymbol(const char *symbolName); + + /// This function permanently loads the dynamic library at the given path + /// using the library load operation from the host operating system. The + /// library instance will only be closed when global destructors run, and + /// there is no guarantee when the library will be unloaded. + /// + /// This returns a valid DynamicLibrary instance on success and an invalid + /// instance on failure (see isValid()). \p *errMsg will only be modified if + /// the library fails to load. + /// + /// It is safe to call this function multiple times for the same library. + /// Open a dynamic library permanently. + static DynamicLibrary getPermanentLibrary(const char *filename, + std::string *errMsg = nullptr); + + /// Registers an externally loaded library. The library will be unloaded + /// when the program terminates. + /// + /// It is safe to call this function multiple times for the same library, + /// though ownership is only taken if there was no error. + static DynamicLibrary addPermanentLibrary(void *handle, + std::string *errMsg = nullptr); + + /// This function permanently loads the dynamic library at the given path. + /// Use this instead of getPermanentLibrary() when you won't need to get + /// symbols from the library itself. + /// + /// It is safe to call this function multiple times for the same library. + static bool LoadLibraryPermanently(const char *Filename, + std::string *ErrMsg = nullptr) { + return !getPermanentLibrary(Filename, ErrMsg).isValid(); + } + + /// This function loads the dynamic library at the given path, using the + /// library load operation from the host operating system. The library + /// instance will be closed when closeLibrary is called or global destructors + /// are run, but there is no guarantee when the library will be unloaded. + /// + /// This returns a valid DynamicLibrary instance on success and an invalid + /// instance on failure (see isValid()). \p *Err will only be modified if the + /// library fails to load. + /// + /// It is safe to call this function multiple times for the same library. + static DynamicLibrary getLibrary(const char *FileName, + std::string *Err = nullptr); + + /// This function closes the dynamic library at the given path, using the + /// library close operation of the host operating system, and there is no + /// guarantee if or when this will cause the the library to be unloaded. + /// + /// This function should be called only if the library was loaded using the + /// getLibrary() function. + static void closeLibrary(DynamicLibrary &Lib); + + enum SearchOrdering { + /// SO_Linker - Search as a call to dlsym(dlopen(NULL)) would when + /// DynamicLibrary::getPermanentLibrary(NULL) has been called or + /// search the list of explcitly loaded symbols if not. + SO_Linker, + /// SO_LoadedFirst - Search all loaded libraries, then as SO_Linker would. + SO_LoadedFirst, + /// SO_LoadedLast - Search as SO_Linker would, then loaded libraries. + /// Only useful to search if libraries with RTLD_LOCAL have been added. + SO_LoadedLast, + /// SO_LoadOrder - Or this in to search libraries in the ordered loaded. + /// The default bahaviour is to search loaded libraries in reverse. + SO_LoadOrder = 4 + }; + static SearchOrdering SearchOrder; // = SO_Linker + + /// This function will search through all previously loaded dynamic + /// libraries for the symbol \p symbolName. If it is found, the address of + /// that symbol is returned. If not, null is returned. Note that this will + /// search permanently loaded libraries (getPermanentLibrary()) as well + /// as explicitly registered symbols (AddSymbol()). + /// @throws std::string on error. + /// Search through libraries for address of a symbol + static void *SearchForAddressOfSymbol(const char *symbolName); + + /// Convenience function for C++ophiles. + static void *SearchForAddressOfSymbol(const std::string &symbolName) { + return SearchForAddressOfSymbol(symbolName.c_str()); + } + + /// This functions permanently adds the symbol \p symbolName with the + /// value \p symbolValue. These symbols are searched before any + /// libraries. + /// Add searchable symbol/value pair. + static void AddSymbol(StringRef symbolName, void *symbolValue); + + class HandleSet; +}; + +} // End sys namespace +} // End llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ELFAttributeParser.h b/contrib/libs/llvm16/include/llvm/Support/ELFAttributeParser.h new file mode 100644 index 00000000000..5905108334f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ELFAttributeParser.h @@ -0,0 +1,89 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- ELF AttributeParser.h - ELF Attribute Parser -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ELFATTRIBUTEPARSER_H +#define LLVM_SUPPORT_ELFATTRIBUTEPARSER_H + +#include "ELFAttributes.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <optional> +#include <unordered_map> + +namespace llvm { +class StringRef; +class ScopedPrinter; + +class ELFAttributeParser { + StringRef vendor; + std::unordered_map<unsigned, unsigned> attributes; + std::unordered_map<unsigned, StringRef> attributesStr; + + virtual Error handler(uint64_t tag, bool &handled) = 0; + +protected: + ScopedPrinter *sw; + TagNameMap tagToStringMap; + DataExtractor de{ArrayRef<uint8_t>{}, true, 0}; + DataExtractor::Cursor cursor{0}; + + void printAttribute(unsigned tag, unsigned value, StringRef valueDesc); + + Error parseStringAttribute(const char *name, unsigned tag, + ArrayRef<const char *> strings); + Error parseAttributeList(uint32_t length); + void parseIndexList(SmallVectorImpl<uint8_t> &indexList); + Error parseSubsection(uint32_t length); + + void setAttributeString(unsigned tag, StringRef value) { + attributesStr.emplace(tag, value); + } + +public: + virtual ~ELFAttributeParser() { static_cast<void>(!cursor.takeError()); } + Error integerAttribute(unsigned tag); + Error stringAttribute(unsigned tag); + + ELFAttributeParser(ScopedPrinter *sw, TagNameMap tagNameMap, StringRef vendor) + : vendor(vendor), sw(sw), tagToStringMap(tagNameMap) {} + + ELFAttributeParser(TagNameMap tagNameMap, StringRef vendor) + : vendor(vendor), sw(nullptr), tagToStringMap(tagNameMap) {} + + Error parse(ArrayRef<uint8_t> section, support::endianness endian); + + std::optional<unsigned> getAttributeValue(unsigned tag) const { + auto I = attributes.find(tag); + if (I == attributes.end()) + return std::nullopt; + return I->second; + } + std::optional<StringRef> getAttributeString(unsigned tag) const { + auto I = attributesStr.find(tag); + if (I == attributesStr.end()) + return std::nullopt; + return I->second; + } +}; + +} // namespace llvm +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ELFAttributes.h b/contrib/libs/llvm16/include/llvm/Support/ELFAttributes.h new file mode 100644 index 00000000000..b392c7d8a46 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ELFAttributes.h @@ -0,0 +1,49 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- ELFAttributes.h - ELF Attributes ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ELFATTRIBUTES_H +#define LLVM_SUPPORT_ELFATTRIBUTES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include <optional> + +namespace llvm { + +struct TagNameItem { + unsigned attr; + StringRef tagName; +}; + +using TagNameMap = ArrayRef<TagNameItem>; + +namespace ELFAttrs { + +enum AttrType : unsigned { File = 1, Section = 2, Symbol = 3 }; + +StringRef attrTypeAsString(unsigned attr, TagNameMap tagNameMap, + bool hasTagPrefix = true); +std::optional<unsigned> attrTypeFromString(StringRef tag, TagNameMap tagNameMap); + +// Magic numbers for ELF attributes. +enum AttrMagic { Format_Version = 0x41 }; + +} // namespace ELFAttrs +} // namespace llvm +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Endian.h b/contrib/libs/llvm16/include/llvm/Support/Endian.h new file mode 100644 index 00000000000..6f0e54a2274 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Endian.h @@ -0,0 +1,438 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares generic functions to read and write endian specific data. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ENDIAN_H +#define LLVM_SUPPORT_ENDIAN_H + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/SwapByteOrder.h" +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <type_traits> + +namespace llvm { +namespace support { + +enum endianness {big, little, native}; + +// These are named values for common alignments. +enum {aligned = 0, unaligned = 1}; + +namespace detail { + +/// ::value is either alignment, or alignof(T) if alignment is 0. +template<class T, int alignment> +struct PickAlignment { + enum { value = alignment == 0 ? alignof(T) : alignment }; +}; + +} // end namespace detail + +namespace endian { + +constexpr endianness system_endianness() { + return sys::IsBigEndianHost ? big : little; +} + +template <typename value_type> +inline value_type byte_swap(value_type value, endianness endian) { + if ((endian != native) && (endian != system_endianness())) + sys::swapByteOrder(value); + return value; +} + +/// Swap the bytes of value to match the given endianness. +template<typename value_type, endianness endian> +inline value_type byte_swap(value_type value) { + return byte_swap(value, endian); +} + +/// Read a value of a particular endianness from memory. +template <typename value_type, std::size_t alignment> +inline value_type read(const void *memory, endianness endian) { + value_type ret; + + memcpy(&ret, + LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment<value_type, alignment>::value)), + sizeof(value_type)); + return byte_swap<value_type>(ret, endian); +} + +template<typename value_type, + endianness endian, + std::size_t alignment> +inline value_type read(const void *memory) { + return read<value_type, alignment>(memory, endian); +} + +/// Read a value of a particular endianness from a buffer, and increment the +/// buffer past that value. +template <typename value_type, std::size_t alignment, typename CharT> +inline value_type readNext(const CharT *&memory, endianness endian) { + value_type ret = read<value_type, alignment>(memory, endian); + memory += sizeof(value_type); + return ret; +} + +template<typename value_type, endianness endian, std::size_t alignment, + typename CharT> +inline value_type readNext(const CharT *&memory) { + return readNext<value_type, alignment, CharT>(memory, endian); +} + +/// Write a value to memory with a particular endianness. +template <typename value_type, std::size_t alignment> +inline void write(void *memory, value_type value, endianness endian) { + value = byte_swap<value_type>(value, endian); + memcpy(LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment<value_type, alignment>::value)), + &value, sizeof(value_type)); +} + +template<typename value_type, + endianness endian, + std::size_t alignment> +inline void write(void *memory, value_type value) { + write<value_type, alignment>(memory, value, endian); +} + +template <typename value_type> +using make_unsigned_t = std::make_unsigned_t<value_type>; + +/// Read a value of a particular endianness from memory, for a location +/// that starts at the given bit offset within the first byte. +template <typename value_type, endianness endian, std::size_t alignment> +inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) { + assert(startBit < 8); + if (startBit == 0) + return read<value_type, endian, alignment>(memory); + else { + // Read two values and compose the result from them. + value_type val[2]; + memcpy(&val[0], + LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment<value_type, alignment>::value)), + sizeof(value_type) * 2); + val[0] = byte_swap<value_type, endian>(val[0]); + val[1] = byte_swap<value_type, endian>(val[1]); + + // Shift bits from the lower value into place. + make_unsigned_t<value_type> lowerVal = val[0] >> startBit; + // Mask off upper bits after right shift in case of signed type. + make_unsigned_t<value_type> numBitsFirstVal = + (sizeof(value_type) * 8) - startBit; + lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1; + + // Get the bits from the upper value. + make_unsigned_t<value_type> upperVal = + val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1); + // Shift them in to place. + upperVal <<= numBitsFirstVal; + + return lowerVal | upperVal; + } +} + +/// Write a value to memory with a particular endianness, for a location +/// that starts at the given bit offset within the first byte. +template <typename value_type, endianness endian, std::size_t alignment> +inline void writeAtBitAlignment(void *memory, value_type value, + uint64_t startBit) { + assert(startBit < 8); + if (startBit == 0) + write<value_type, endian, alignment>(memory, value); + else { + // Read two values and shift the result into them. + value_type val[2]; + memcpy(&val[0], + LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment<value_type, alignment>::value)), + sizeof(value_type) * 2); + val[0] = byte_swap<value_type, endian>(val[0]); + val[1] = byte_swap<value_type, endian>(val[1]); + + // Mask off any existing bits in the upper part of the lower value that + // we want to replace. + val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1; + make_unsigned_t<value_type> numBitsFirstVal = + (sizeof(value_type) * 8) - startBit; + make_unsigned_t<value_type> lowerVal = value; + if (startBit > 0) { + // Mask off the upper bits in the new value that are not going to go into + // the lower value. This avoids a left shift of a negative value, which + // is undefined behavior. + lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1); + // Now shift the new bits into place + lowerVal <<= startBit; + } + val[0] |= lowerVal; + + // Mask off any existing bits in the lower part of the upper value that + // we want to replace. + val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1); + // Next shift the bits that go into the upper value into position. + make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal; + // Mask off upper bits after right shift in case of signed type. + upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1; + val[1] |= upperVal; + + // Finally, rewrite values. + val[0] = byte_swap<value_type, endian>(val[0]); + val[1] = byte_swap<value_type, endian>(val[1]); + memcpy(LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment<value_type, alignment>::value)), + &val[0], sizeof(value_type) * 2); + } +} + +} // end namespace endian + +namespace detail { + +template <typename ValueType, endianness Endian, std::size_t Alignment, + std::size_t ALIGN = PickAlignment<ValueType, Alignment>::value> +struct packed_endian_specific_integral { + using value_type = ValueType; + static constexpr endianness endian = Endian; + static constexpr std::size_t alignment = Alignment; + + packed_endian_specific_integral() = default; + + explicit packed_endian_specific_integral(value_type val) { *this = val; } + + operator value_type() const { + return endian::read<value_type, endian, alignment>( + (const void*)Value.buffer); + } + + void operator=(value_type newValue) { + endian::write<value_type, endian, alignment>( + (void*)Value.buffer, newValue); + } + + packed_endian_specific_integral &operator+=(value_type newValue) { + *this = *this + newValue; + return *this; + } + + packed_endian_specific_integral &operator-=(value_type newValue) { + *this = *this - newValue; + return *this; + } + + packed_endian_specific_integral &operator|=(value_type newValue) { + *this = *this | newValue; + return *this; + } + + packed_endian_specific_integral &operator&=(value_type newValue) { + *this = *this & newValue; + return *this; + } + +private: + struct { + alignas(ALIGN) char buffer[sizeof(value_type)]; + } Value; + +public: + struct ref { + explicit ref(void *Ptr) : Ptr(Ptr) {} + + operator value_type() const { + return endian::read<value_type, endian, alignment>(Ptr); + } + + void operator=(value_type NewValue) { + endian::write<value_type, endian, alignment>(Ptr, NewValue); + } + + private: + void *Ptr; + }; +}; + +} // end namespace detail + +using ulittle16_t = + detail::packed_endian_specific_integral<uint16_t, little, unaligned>; +using ulittle32_t = + detail::packed_endian_specific_integral<uint32_t, little, unaligned>; +using ulittle64_t = + detail::packed_endian_specific_integral<uint64_t, little, unaligned>; + +using little16_t = + detail::packed_endian_specific_integral<int16_t, little, unaligned>; +using little32_t = + detail::packed_endian_specific_integral<int32_t, little, unaligned>; +using little64_t = + detail::packed_endian_specific_integral<int64_t, little, unaligned>; + +using aligned_ulittle16_t = + detail::packed_endian_specific_integral<uint16_t, little, aligned>; +using aligned_ulittle32_t = + detail::packed_endian_specific_integral<uint32_t, little, aligned>; +using aligned_ulittle64_t = + detail::packed_endian_specific_integral<uint64_t, little, aligned>; + +using aligned_little16_t = + detail::packed_endian_specific_integral<int16_t, little, aligned>; +using aligned_little32_t = + detail::packed_endian_specific_integral<int32_t, little, aligned>; +using aligned_little64_t = + detail::packed_endian_specific_integral<int64_t, little, aligned>; + +using ubig16_t = + detail::packed_endian_specific_integral<uint16_t, big, unaligned>; +using ubig32_t = + detail::packed_endian_specific_integral<uint32_t, big, unaligned>; +using ubig64_t = + detail::packed_endian_specific_integral<uint64_t, big, unaligned>; + +using big16_t = + detail::packed_endian_specific_integral<int16_t, big, unaligned>; +using big32_t = + detail::packed_endian_specific_integral<int32_t, big, unaligned>; +using big64_t = + detail::packed_endian_specific_integral<int64_t, big, unaligned>; + +using aligned_ubig16_t = + detail::packed_endian_specific_integral<uint16_t, big, aligned>; +using aligned_ubig32_t = + detail::packed_endian_specific_integral<uint32_t, big, aligned>; +using aligned_ubig64_t = + detail::packed_endian_specific_integral<uint64_t, big, aligned>; + +using aligned_big16_t = + detail::packed_endian_specific_integral<int16_t, big, aligned>; +using aligned_big32_t = + detail::packed_endian_specific_integral<int32_t, big, aligned>; +using aligned_big64_t = + detail::packed_endian_specific_integral<int64_t, big, aligned>; + +using unaligned_uint16_t = + detail::packed_endian_specific_integral<uint16_t, native, unaligned>; +using unaligned_uint32_t = + detail::packed_endian_specific_integral<uint32_t, native, unaligned>; +using unaligned_uint64_t = + detail::packed_endian_specific_integral<uint64_t, native, unaligned>; + +using unaligned_int16_t = + detail::packed_endian_specific_integral<int16_t, native, unaligned>; +using unaligned_int32_t = + detail::packed_endian_specific_integral<int32_t, native, unaligned>; +using unaligned_int64_t = + detail::packed_endian_specific_integral<int64_t, native, unaligned>; + +template <typename T> +using little_t = detail::packed_endian_specific_integral<T, little, unaligned>; +template <typename T> +using big_t = detail::packed_endian_specific_integral<T, big, unaligned>; + +template <typename T> +using aligned_little_t = + detail::packed_endian_specific_integral<T, little, aligned>; +template <typename T> +using aligned_big_t = detail::packed_endian_specific_integral<T, big, aligned>; + +namespace endian { + +template <typename T> inline T read(const void *P, endianness E) { + return read<T, unaligned>(P, E); +} + +template <typename T, endianness E> inline T read(const void *P) { + return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P; +} + +inline uint16_t read16(const void *P, endianness E) { + return read<uint16_t>(P, E); +} +inline uint32_t read32(const void *P, endianness E) { + return read<uint32_t>(P, E); +} +inline uint64_t read64(const void *P, endianness E) { + return read<uint64_t>(P, E); +} + +template <endianness E> inline uint16_t read16(const void *P) { + return read<uint16_t, E>(P); +} +template <endianness E> inline uint32_t read32(const void *P) { + return read<uint32_t, E>(P); +} +template <endianness E> inline uint64_t read64(const void *P) { + return read<uint64_t, E>(P); +} + +inline uint16_t read16le(const void *P) { return read16<little>(P); } +inline uint32_t read32le(const void *P) { return read32<little>(P); } +inline uint64_t read64le(const void *P) { return read64<little>(P); } +inline uint16_t read16be(const void *P) { return read16<big>(P); } +inline uint32_t read32be(const void *P) { return read32<big>(P); } +inline uint64_t read64be(const void *P) { return read64<big>(P); } + +template <typename T> inline void write(void *P, T V, endianness E) { + write<T, unaligned>(P, V, E); +} + +template <typename T, endianness E> inline void write(void *P, T V) { + *(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V; +} + +inline void write16(void *P, uint16_t V, endianness E) { + write<uint16_t>(P, V, E); +} +inline void write32(void *P, uint32_t V, endianness E) { + write<uint32_t>(P, V, E); +} +inline void write64(void *P, uint64_t V, endianness E) { + write<uint64_t>(P, V, E); +} + +template <endianness E> inline void write16(void *P, uint16_t V) { + write<uint16_t, E>(P, V); +} +template <endianness E> inline void write32(void *P, uint32_t V) { + write<uint32_t, E>(P, V); +} +template <endianness E> inline void write64(void *P, uint64_t V) { + write<uint64_t, E>(P, V); +} + +inline void write16le(void *P, uint16_t V) { write16<little>(P, V); } +inline void write32le(void *P, uint32_t V) { write32<little>(P, V); } +inline void write64le(void *P, uint64_t V) { write64<little>(P, V); } +inline void write16be(void *P, uint16_t V) { write16<big>(P, V); } +inline void write32be(void *P, uint32_t V) { write32<big>(P, V); } +inline void write64be(void *P, uint64_t V) { write64<big>(P, V); } + +} // end namespace endian + +} // end namespace support +} // end namespace llvm + +#endif // LLVM_SUPPORT_ENDIAN_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/EndianStream.h b/contrib/libs/llvm16/include/llvm/Support/EndianStream.h new file mode 100644 index 00000000000..f21049677e9 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/EndianStream.h @@ -0,0 +1,80 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- EndianStream.h - Stream ops with endian specific data ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines utilities for operating on streams that have endian +// specific data. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ENDIANSTREAM_H +#define LLVM_SUPPORT_ENDIANSTREAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace support { + +namespace endian { + +template <typename value_type> +inline void write(raw_ostream &os, value_type value, endianness endian) { + value = byte_swap<value_type>(value, endian); + os.write((const char *)&value, sizeof(value_type)); +} + +template <> +inline void write<float>(raw_ostream &os, float value, endianness endian) { + write(os, FloatToBits(value), endian); +} + +template <> +inline void write<double>(raw_ostream &os, double value, + endianness endian) { + write(os, DoubleToBits(value), endian); +} + +template <typename value_type> +inline void write(raw_ostream &os, ArrayRef<value_type> vals, + endianness endian) { + for (value_type v : vals) + write(os, v, endian); +} + +/// Adapter to write values to a stream in a particular byte order. +struct Writer { + raw_ostream &OS; + endianness Endian; + Writer(raw_ostream &OS, endianness Endian) : OS(OS), Endian(Endian) {} + template <typename value_type> void write(ArrayRef<value_type> Val) { + endian::write(OS, Val, Endian); + } + template <typename value_type> void write(value_type Val) { + endian::write(OS, Val, Endian); + } +}; + +} // end namespace endian + +} // end namespace support +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Errc.h b/contrib/libs/llvm16/include/llvm/Support/Errc.h new file mode 100644 index 00000000000..dc8351073fe --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Errc.h @@ -0,0 +1,97 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Errc.h - Defines the llvm::errc enum --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// While std::error_code works OK on all platforms we use, there are some +// some problems with std::errc that can be avoided by using our own +// enumeration: +// +// * std::errc is a namespace in some implementations. That means that ADL +// doesn't work and it is sometimes necessary to write std::make_error_code +// or in templates: +// using std::make_error_code; +// make_error_code(...); +// +// with this enum it is safe to always just use make_error_code. +// +// * Some implementations define fewer names than others. This header has +// the intersection of all the ones we support. +// +// * std::errc is just marked with is_error_condition_enum. This means that +// common patterns like AnErrorCode == errc::no_such_file_or_directory take +// 4 virtual calls instead of two comparisons. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ERRC_H +#define LLVM_SUPPORT_ERRC_H + +#include <system_error> + +namespace llvm { +enum class errc { + argument_list_too_long = int(std::errc::argument_list_too_long), + argument_out_of_domain = int(std::errc::argument_out_of_domain), + bad_address = int(std::errc::bad_address), + bad_file_descriptor = int(std::errc::bad_file_descriptor), + broken_pipe = int(std::errc::broken_pipe), + device_or_resource_busy = int(std::errc::device_or_resource_busy), + directory_not_empty = int(std::errc::directory_not_empty), + executable_format_error = int(std::errc::executable_format_error), + file_exists = int(std::errc::file_exists), + file_too_large = int(std::errc::file_too_large), + filename_too_long = int(std::errc::filename_too_long), + function_not_supported = int(std::errc::function_not_supported), + illegal_byte_sequence = int(std::errc::illegal_byte_sequence), + inappropriate_io_control_operation = + int(std::errc::inappropriate_io_control_operation), + interrupted = int(std::errc::interrupted), + invalid_argument = int(std::errc::invalid_argument), + invalid_seek = int(std::errc::invalid_seek), + io_error = int(std::errc::io_error), + is_a_directory = int(std::errc::is_a_directory), + no_child_process = int(std::errc::no_child_process), + no_lock_available = int(std::errc::no_lock_available), + no_space_on_device = int(std::errc::no_space_on_device), + no_such_device_or_address = int(std::errc::no_such_device_or_address), + no_such_device = int(std::errc::no_such_device), + no_such_file_or_directory = int(std::errc::no_such_file_or_directory), + no_such_process = int(std::errc::no_such_process), + not_a_directory = int(std::errc::not_a_directory), + not_enough_memory = int(std::errc::not_enough_memory), + not_supported = int(std::errc::not_supported), + operation_not_permitted = int(std::errc::operation_not_permitted), + permission_denied = int(std::errc::permission_denied), + read_only_file_system = int(std::errc::read_only_file_system), + resource_deadlock_would_occur = int(std::errc::resource_deadlock_would_occur), + resource_unavailable_try_again = + int(std::errc::resource_unavailable_try_again), + result_out_of_range = int(std::errc::result_out_of_range), + too_many_files_open_in_system = int(std::errc::too_many_files_open_in_system), + too_many_files_open = int(std::errc::too_many_files_open), + too_many_links = int(std::errc::too_many_links) +}; + +inline std::error_code make_error_code(errc E) { + return std::error_code(static_cast<int>(E), std::generic_category()); +} +} + +namespace std { +template <> struct is_error_code_enum<llvm::errc> : std::true_type {}; +} +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Errno.h b/contrib/libs/llvm16/include/llvm/Support/Errno.h new file mode 100644 index 00000000000..6e7fd797585 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Errno.h @@ -0,0 +1,56 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Errno.h - Portable+convenient errno handling -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares some portable and convenient functions to deal with errno. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ERRNO_H +#define LLVM_SUPPORT_ERRNO_H + +#include <cerrno> +#include <string> + +namespace llvm { +namespace sys { + +/// Returns a string representation of the errno value, using whatever +/// thread-safe variant of strerror() is available. Be sure to call this +/// immediately after the function that set errno, or errno may have been +/// overwritten by an intervening call. +std::string StrError(); + +/// Like the no-argument version above, but uses \p errnum instead of errno. +std::string StrError(int errnum); + +template <typename FailT, typename Fun, typename... Args> +inline decltype(auto) RetryAfterSignal(const FailT &Fail, const Fun &F, + const Args &... As) { + decltype(F(As...)) Res; + do { + errno = 0; + Res = F(As...); + } while (Res == Fail && errno == EINTR); + return Res; +} + +} // namespace sys +} // namespace llvm + +#endif // LLVM_SUPPORT_ERRNO_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Error.h b/contrib/libs/llvm16/include/llvm/Support/Error.h new file mode 100644 index 00000000000..cc31f10df50 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Error.h @@ -0,0 +1,1424 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Error.h - Recoverable error handling --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines an API used to report recoverable errors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ERROR_H +#define LLVM_SUPPORT_ERROR_H + +#include "llvm-c/Error.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Config/abi-breaking.h" +#include "llvm/Support/AlignOf.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <functional> +#include <memory> +#include <new> +#include <optional> +#include <string> +#include <system_error> +#include <type_traits> +#include <utility> +#include <vector> + +namespace llvm { + +class ErrorSuccess; + +/// Base class for error info classes. Do not extend this directly: Extend +/// the ErrorInfo template subclass instead. +class ErrorInfoBase { +public: + virtual ~ErrorInfoBase() = default; + + /// Print an error message to an output stream. + virtual void log(raw_ostream &OS) const = 0; + + /// Return the error message as a string. + virtual std::string message() const { + std::string Msg; + raw_string_ostream OS(Msg); + log(OS); + return OS.str(); + } + + /// Convert this error to a std::error_code. + /// + /// This is a temporary crutch to enable interaction with code still + /// using std::error_code. It will be removed in the future. + virtual std::error_code convertToErrorCode() const = 0; + + // Returns the class ID for this type. + static const void *classID() { return &ID; } + + // Returns the class ID for the dynamic type of this ErrorInfoBase instance. + virtual const void *dynamicClassID() const = 0; + + // Check whether this instance is a subclass of the class identified by + // ClassID. + virtual bool isA(const void *const ClassID) const { + return ClassID == classID(); + } + + // Check whether this instance is a subclass of ErrorInfoT. + template <typename ErrorInfoT> bool isA() const { + return isA(ErrorInfoT::classID()); + } + +private: + virtual void anchor(); + + static char ID; +}; + +/// Lightweight error class with error context and mandatory checking. +/// +/// Instances of this class wrap a ErrorInfoBase pointer. Failure states +/// are represented by setting the pointer to a ErrorInfoBase subclass +/// instance containing information describing the failure. Success is +/// represented by a null pointer value. +/// +/// Instances of Error also contains a 'Checked' flag, which must be set +/// before the destructor is called, otherwise the destructor will trigger a +/// runtime error. This enforces at runtime the requirement that all Error +/// instances be checked or returned to the caller. +/// +/// There are two ways to set the checked flag, depending on what state the +/// Error instance is in. For Error instances indicating success, it +/// is sufficient to invoke the boolean conversion operator. E.g.: +/// +/// @code{.cpp} +/// Error foo(<...>); +/// +/// if (auto E = foo(<...>)) +/// return E; // <- Return E if it is in the error state. +/// // We have verified that E was in the success state. It can now be safely +/// // destroyed. +/// @endcode +/// +/// A success value *can not* be dropped. For example, just calling 'foo(<...>)' +/// without testing the return value will raise a runtime error, even if foo +/// returns success. +/// +/// For Error instances representing failure, you must use either the +/// handleErrors or handleAllErrors function with a typed handler. E.g.: +/// +/// @code{.cpp} +/// class MyErrorInfo : public ErrorInfo<MyErrorInfo> { +/// // Custom error info. +/// }; +/// +/// Error foo(<...>) { return make_error<MyErrorInfo>(...); } +/// +/// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo. +/// auto NewE = +/// handleErrors(E, +/// [](const MyErrorInfo &M) { +/// // Deal with the error. +/// }, +/// [](std::unique_ptr<OtherError> M) -> Error { +/// if (canHandle(*M)) { +/// // handle error. +/// return Error::success(); +/// } +/// // Couldn't handle this error instance. Pass it up the stack. +/// return Error(std::move(M)); +/// ); +/// // Note - we must check or return NewE in case any of the handlers +/// // returned a new error. +/// @endcode +/// +/// The handleAllErrors function is identical to handleErrors, except +/// that it has a void return type, and requires all errors to be handled and +/// no new errors be returned. It prevents errors (assuming they can all be +/// handled) from having to be bubbled all the way to the top-level. +/// +/// *All* Error instances must be checked before destruction, even if +/// they're moved-assigned or constructed from Success values that have already +/// been checked. This enforces checking through all levels of the call stack. +class [[nodiscard]] Error { + // ErrorList needs to be able to yank ErrorInfoBase pointers out of Errors + // to add to the error list. It can't rely on handleErrors for this, since + // handleErrors does not support ErrorList handlers. + friend class ErrorList; + + // handleErrors needs to be able to set the Checked flag. + template <typename... HandlerTs> + friend Error handleErrors(Error E, HandlerTs &&... Handlers); + + // Expected<T> needs to be able to steal the payload when constructed from an + // error. + template <typename T> friend class Expected; + + // wrap needs to be able to steal the payload. + friend LLVMErrorRef wrap(Error); + +protected: + /// Create a success value. Prefer using 'Error::success()' for readability + Error() { + setPtr(nullptr); + setChecked(false); + } + +public: + /// Create a success value. + static ErrorSuccess success(); + + // Errors are not copy-constructable. + Error(const Error &Other) = delete; + + /// Move-construct an error value. The newly constructed error is considered + /// unchecked, even if the source error had been checked. The original error + /// becomes a checked Success value, regardless of its original state. + Error(Error &&Other) { + setChecked(true); + *this = std::move(Other); + } + + /// Create an error value. Prefer using the 'make_error' function, but + /// this constructor can be useful when "re-throwing" errors from handlers. + Error(std::unique_ptr<ErrorInfoBase> Payload) { + setPtr(Payload.release()); + setChecked(false); + } + + // Errors are not copy-assignable. + Error &operator=(const Error &Other) = delete; + + /// Move-assign an error value. The current error must represent success, you + /// you cannot overwrite an unhandled error. The current error is then + /// considered unchecked. The source error becomes a checked success value, + /// regardless of its original state. + Error &operator=(Error &&Other) { + // Don't allow overwriting of unchecked values. + assertIsChecked(); + setPtr(Other.getPtr()); + + // This Error is unchecked, even if the source error was checked. + setChecked(false); + + // Null out Other's payload and set its checked bit. + Other.setPtr(nullptr); + Other.setChecked(true); + + return *this; + } + + /// Destroy a Error. Fails with a call to abort() if the error is + /// unchecked. + ~Error() { + assertIsChecked(); + delete getPtr(); + } + + /// Bool conversion. Returns true if this Error is in a failure state, + /// and false if it is in an accept state. If the error is in a Success state + /// it will be considered checked. + explicit operator bool() { + setChecked(getPtr() == nullptr); + return getPtr() != nullptr; + } + + /// Check whether one error is a subclass of another. + template <typename ErrT> bool isA() const { + return getPtr() && getPtr()->isA(ErrT::classID()); + } + + /// Returns the dynamic class id of this error, or null if this is a success + /// value. + const void* dynamicClassID() const { + if (!getPtr()) + return nullptr; + return getPtr()->dynamicClassID(); + } + +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. + [[noreturn]] void fatalUncheckedError() const; +#endif + + void assertIsChecked() { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + if (LLVM_UNLIKELY(!getChecked() || getPtr())) + fatalUncheckedError(); +#endif + } + + ErrorInfoBase *getPtr() const { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + return reinterpret_cast<ErrorInfoBase*>( + reinterpret_cast<uintptr_t>(Payload) & + ~static_cast<uintptr_t>(0x1)); +#else + return Payload; +#endif + } + + void setPtr(ErrorInfoBase *EI) { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + Payload = reinterpret_cast<ErrorInfoBase*>( + (reinterpret_cast<uintptr_t>(EI) & + ~static_cast<uintptr_t>(0x1)) | + (reinterpret_cast<uintptr_t>(Payload) & 0x1)); +#else + Payload = EI; +#endif + } + + bool getChecked() const { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + return (reinterpret_cast<uintptr_t>(Payload) & 0x1) == 0; +#else + return true; +#endif + } + + void setChecked(bool V) { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + Payload = reinterpret_cast<ErrorInfoBase*>( + (reinterpret_cast<uintptr_t>(Payload) & + ~static_cast<uintptr_t>(0x1)) | + (V ? 0 : 1)); +#endif + } + + std::unique_ptr<ErrorInfoBase> takePayload() { + std::unique_ptr<ErrorInfoBase> Tmp(getPtr()); + setPtr(nullptr); + setChecked(true); + return Tmp; + } + + friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) { + if (auto *P = E.getPtr()) + P->log(OS); + else + OS << "success"; + return OS; + } + + ErrorInfoBase *Payload = nullptr; +}; + +/// Subclass of Error for the sole purpose of identifying the success path in +/// the type system. This allows to catch invalid conversion to Expected<T> at +/// compile time. +class ErrorSuccess final : public Error {}; + +inline ErrorSuccess Error::success() { return ErrorSuccess(); } + +/// Make a Error instance representing failure using the given error info +/// type. +template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) { + return Error(std::make_unique<ErrT>(std::forward<ArgTs>(Args)...)); +} + +/// Base class for user error types. Users should declare their error types +/// like: +/// +/// class MyError : public ErrorInfo<MyError> { +/// .... +/// }; +/// +/// This class provides an implementation of the ErrorInfoBase::kind +/// method, which is used by the Error RTTI system. +template <typename ThisErrT, typename ParentErrT = ErrorInfoBase> +class ErrorInfo : public ParentErrT { +public: + using ParentErrT::ParentErrT; // inherit constructors + + static const void *classID() { return &ThisErrT::ID; } + + const void *dynamicClassID() const override { return &ThisErrT::ID; } + + bool isA(const void *const ClassID) const override { + return ClassID == classID() || ParentErrT::isA(ClassID); + } +}; + +/// Special ErrorInfo subclass representing a list of ErrorInfos. +/// Instances of this class are constructed by joinError. +class ErrorList final : public ErrorInfo<ErrorList> { + // handleErrors needs to be able to iterate the payload list of an + // ErrorList. + template <typename... HandlerTs> + friend Error handleErrors(Error E, HandlerTs &&... Handlers); + + // joinErrors is implemented in terms of join. + friend Error joinErrors(Error, Error); + +public: + void log(raw_ostream &OS) const override { + OS << "Multiple errors:\n"; + for (const auto &ErrPayload : Payloads) { + ErrPayload->log(OS); + OS << "\n"; + } + } + + std::error_code convertToErrorCode() const override; + + // Used by ErrorInfo::classID. + static char ID; + +private: + ErrorList(std::unique_ptr<ErrorInfoBase> Payload1, + std::unique_ptr<ErrorInfoBase> Payload2) { + assert(!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() && + "ErrorList constructor payloads should be singleton errors"); + Payloads.push_back(std::move(Payload1)); + Payloads.push_back(std::move(Payload2)); + } + + static Error join(Error E1, Error E2) { + if (!E1) + return E2; + if (!E2) + return E1; + if (E1.isA<ErrorList>()) { + auto &E1List = static_cast<ErrorList &>(*E1.getPtr()); + if (E2.isA<ErrorList>()) { + auto E2Payload = E2.takePayload(); + auto &E2List = static_cast<ErrorList &>(*E2Payload); + for (auto &Payload : E2List.Payloads) + E1List.Payloads.push_back(std::move(Payload)); + } else + E1List.Payloads.push_back(E2.takePayload()); + + return E1; + } + if (E2.isA<ErrorList>()) { + auto &E2List = static_cast<ErrorList &>(*E2.getPtr()); + E2List.Payloads.insert(E2List.Payloads.begin(), E1.takePayload()); + return E2; + } + return Error(std::unique_ptr<ErrorList>( + new ErrorList(E1.takePayload(), E2.takePayload()))); + } + + std::vector<std::unique_ptr<ErrorInfoBase>> Payloads; +}; + +/// Concatenate errors. The resulting Error is unchecked, and contains the +/// ErrorInfo(s), if any, contained in E1, followed by the +/// ErrorInfo(s), if any, contained in E2. +inline Error joinErrors(Error E1, Error E2) { + return ErrorList::join(std::move(E1), std::move(E2)); +} + +/// Tagged union holding either a T or a Error. +/// +/// This class parallels ErrorOr, but replaces error_code with Error. Since +/// Error cannot be copied, this class replaces getError() with +/// takeError(). It also adds an bool errorIsA<ErrT>() method for testing the +/// error class type. +/// +/// Example usage of 'Expected<T>' as a function return type: +/// +/// @code{.cpp} +/// Expected<int> myDivide(int A, int B) { +/// if (B == 0) { +/// // return an Error +/// return createStringError(inconvertibleErrorCode(), +/// "B must not be zero!"); +/// } +/// // return an integer +/// return A / B; +/// } +/// @endcode +/// +/// Checking the results of to a function returning 'Expected<T>': +/// @code{.cpp} +/// if (auto E = Result.takeError()) { +/// // We must consume the error. Typically one of: +/// // - return the error to our caller +/// // - toString(), when logging +/// // - consumeError(), to silently swallow the error +/// // - handleErrors(), to distinguish error types +/// errs() << "Problem with division " << toString(std::move(E)) << "\n"; +/// return; +/// } +/// // use the result +/// outs() << "The answer is " << *Result << "\n"; +/// @endcode +/// +/// For unit-testing a function returning an 'Expected<T>', see the +/// 'EXPECT_THAT_EXPECTED' macros in llvm/Testing/Support/Error.h + +template <class T> class [[nodiscard]] Expected { + template <class T1> friend class ExpectedAsOutParameter; + template <class OtherT> friend class Expected; + + static constexpr bool isRef = std::is_reference<T>::value; + + using wrap = std::reference_wrapper<std::remove_reference_t<T>>; + + using error_type = std::unique_ptr<ErrorInfoBase>; + +public: + using storage_type = std::conditional_t<isRef, wrap, T>; + using value_type = T; + +private: + using reference = std::remove_reference_t<T> &; + using const_reference = const std::remove_reference_t<T> &; + using pointer = std::remove_reference_t<T> *; + using const_pointer = const std::remove_reference_t<T> *; + +public: + /// Create an Expected<T> error value from the given Error. + Expected(Error Err) + : HasError(true) +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + // Expected is unchecked upon construction in Debug builds. + , Unchecked(true) +#endif + { + assert(Err && "Cannot create Expected<T> from Error success value."); + new (getErrorStorage()) error_type(Err.takePayload()); + } + + /// Forbid to convert from Error::success() implicitly, this avoids having + /// Expected<T> foo() { return Error::success(); } which compiles otherwise + /// but triggers the assertion above. + Expected(ErrorSuccess) = delete; + + /// Create an Expected<T> success value from the given OtherT value, which + /// must be convertible to T. + template <typename OtherT> + Expected(OtherT &&Val, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) + : HasError(false) +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + // Expected is unchecked upon construction in Debug builds. + , + Unchecked(true) +#endif + { + new (getStorage()) storage_type(std::forward<OtherT>(Val)); + } + + /// Move construct an Expected<T> value. + Expected(Expected &&Other) { moveConstruct(std::move(Other)); } + + /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT + /// must be convertible to T. + template <class OtherT> + Expected(Expected<OtherT> &&Other, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) { + moveConstruct(std::move(Other)); + } + + /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT + /// isn't convertible to T. + template <class OtherT> + explicit Expected( + Expected<OtherT> &&Other, + std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) { + moveConstruct(std::move(Other)); + } + + /// Move-assign from another Expected<T>. + Expected &operator=(Expected &&Other) { + moveAssign(std::move(Other)); + return *this; + } + + /// Destroy an Expected<T>. + ~Expected() { + assertIsChecked(); + if (!HasError) + getStorage()->~storage_type(); + else + getErrorStorage()->~error_type(); + } + + /// Return false if there is an error. + explicit operator bool() { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + Unchecked = HasError; +#endif + return !HasError; + } + + /// Returns a reference to the stored T value. + reference get() { + assertIsChecked(); + return *getStorage(); + } + + /// Returns a const reference to the stored T value. + const_reference get() const { + assertIsChecked(); + return const_cast<Expected<T> *>(this)->get(); + } + + /// Returns \a takeError() after moving the held T (if any) into \p V. + template <class OtherT> + Error moveInto(OtherT &Value, + std::enable_if_t<std::is_assignable<OtherT &, T &&>::value> * = + nullptr) && { + if (*this) + Value = std::move(get()); + return takeError(); + } + + /// Check that this Expected<T> is an error of type ErrT. + template <typename ErrT> bool errorIsA() const { + return HasError && (*getErrorStorage())->template isA<ErrT>(); + } + + /// Take ownership of the stored error. + /// After calling this the Expected<T> is in an indeterminate state that can + /// only be safely destructed. No further calls (beside the destructor) should + /// be made on the Expected<T> value. + Error takeError() { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + Unchecked = false; +#endif + return HasError ? Error(std::move(*getErrorStorage())) : Error::success(); + } + + /// Returns a pointer to the stored T value. + pointer operator->() { + assertIsChecked(); + return toPointer(getStorage()); + } + + /// Returns a const pointer to the stored T value. + const_pointer operator->() const { + assertIsChecked(); + return toPointer(getStorage()); + } + + /// Returns a reference to the stored T value. + reference operator*() { + assertIsChecked(); + return *getStorage(); + } + + /// Returns a const reference to the stored T value. + const_reference operator*() const { + assertIsChecked(); + return *getStorage(); + } + +private: + template <class T1> + static bool compareThisIfSameType(const T1 &a, const T1 &b) { + return &a == &b; + } + + template <class T1, class T2> + static bool compareThisIfSameType(const T1 &, const T2 &) { + return false; + } + + template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) { + HasError = Other.HasError; +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + Unchecked = true; + Other.Unchecked = false; +#endif + + if (!HasError) + new (getStorage()) storage_type(std::move(*Other.getStorage())); + else + new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage())); + } + + template <class OtherT> void moveAssign(Expected<OtherT> &&Other) { + assertIsChecked(); + + if (compareThisIfSameType(*this, Other)) + return; + + this->~Expected(); + new (this) Expected(std::move(Other)); + } + + pointer toPointer(pointer Val) { return Val; } + + const_pointer toPointer(const_pointer Val) const { return Val; } + + pointer toPointer(wrap *Val) { return &Val->get(); } + + const_pointer toPointer(const wrap *Val) const { return &Val->get(); } + + storage_type *getStorage() { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast<storage_type *>(&TStorage); + } + + const storage_type *getStorage() const { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast<const storage_type *>(&TStorage); + } + + error_type *getErrorStorage() { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast<error_type *>(&ErrorStorage); + } + + const error_type *getErrorStorage() const { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast<const error_type *>(&ErrorStorage); + } + + // Used by ExpectedAsOutParameter to reset the checked flag. + void setUnchecked() { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + Unchecked = true; +#endif + } + +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + [[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() const { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + if (LLVM_UNLIKELY(Unchecked)) + fatalUncheckedExpected(); +#endif + } + + union { + AlignedCharArrayUnion<storage_type> TStorage; + AlignedCharArrayUnion<error_type> ErrorStorage; + }; + bool HasError : 1; +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + bool Unchecked : 1; +#endif +}; + +/// Report a serious error, calling any installed error handler. See +/// ErrorHandling.h. +[[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"; +#ifndef NDEBUG + std::string Str; + raw_string_ostream OS(Str); + OS << Msg << "\n" << Err; + Msg = OS.str().c_str(); +#endif + 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"; +#ifndef NDEBUG + std::string Str; + raw_string_ostream OS(Str); + auto E = ValOrErr.takeError(); + OS << Msg << "\n" << E; + Msg = OS.str().c_str(); +#endif + 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"; +#ifndef NDEBUG + std::string Str; + raw_string_ostream OS(Str); + auto E = ValOrErr.takeError(); + OS << Msg << "\n" << E; + Msg = OS.str().c_str(); +#endif + 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_t<HandlerT>::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 by contract all errors +/// *must* be handled by the given handlers (i.e. there must be no remaining +/// errors after running the handlers, or llvm_unreachable is 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, llvm_unreachable 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 function is compatible with the helpers from Support/WithColor.h. You +/// can pass any of them as the OS. Please consider using them instead of +/// including 'error: ' in the ErrorBanner. +/// +/// 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 std::optional<T>. +inline void consumeError(Error Err) { + handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); +} + +/// Convert an Expected to an Optional 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 problems: perhaps the +/// error should be propagated further, or the error-producer should just +/// return an Optional in the first place. +template <typename T> std::optional<T> expectedToOptional(Expected<T> &&E) { + if (E) + return std::move(*E); + consumeError(E.takeError()); + return std::nullopt; +} + +template <typename T> std::optional<T> expectedToStdOptional(Expected<T> &&E) { + if (E) + return std::move(*E); + consumeError(E.takeError()); + return std::nullopt; +} + +/// Helper for converting an Error to a bool. +/// +/// This method returns true if Err is in an error state, or false if it is +/// in a success state. Puts Err in a checked state in both cases (unlike +/// Error::operator bool(), which only does this for success states). +inline bool errorToBool(Error Err) { + bool IsError = static_cast<bool>(Err); + if (IsError) + consumeError(std::move(Err)); + return IsError; +} + +/// 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. +template <typename T> +class ExpectedAsOutParameter { +public: + ExpectedAsOutParameter(Expected<T> *ValOrErr) + : ValOrErr(ValOrErr) { + if (ValOrErr) + (void)!!*ValOrErr; + } + + ~ExpectedAsOutParameter() { + if (ValOrErr) + ValOrErr->setUnchecked(); + } + +private: + Expected<T> *ValOrErr; +}; + +/// This class wraps a std::error_code in a Error. +/// +/// This is useful if you're writing an interface that returns a Error +/// (or Expected) and you want to call code that still returns +/// std::error_codes. +class ECError : public ErrorInfo<ECError> { + friend Error errorCodeToError(std::error_code); + + void anchor() override; + +public: + void setErrorCode(std::error_code EC) { this->EC = EC; } + std::error_code convertToErrorCode() const override { return EC; } + void log(raw_ostream &OS) const override { OS << EC.message(); } + + // Used by ErrorInfo::classID. + static char ID; + +protected: + ECError() = default; + ECError(std::error_code EC) : EC(EC) {} + + std::error_code EC; +}; + +/// The value returned by this function can be returned from convertToErrorCode +/// for Error values where no sensible translation to std::error_code exists. +/// It should only be used in this situation, and should never be used where a +/// sensible conversion to std::error_code is available, as attempts to convert +/// to/from this error will result in a fatal error. (i.e. it is a programmatic +/// error to try to convert such a value). +std::error_code inconvertibleErrorCode(); + +/// Helper for converting an std::error_code to a Error. +Error errorCodeToError(std::error_code EC); + +/// Helper for converting an ECError to a std::error_code. +/// +/// This method requires that Err be Error() or an ECError, otherwise it +/// will trigger a call to abort(). +std::error_code errorToErrorCode(Error Err); + +/// Convert an ErrorOr<T> to an Expected<T>. +template <typename T> Expected<T> errorOrToExpected(ErrorOr<T> &&EO) { + if (auto EC = EO.getError()) + return errorCodeToError(EC); + return std::move(*EO); +} + +/// Convert an Expected<T> to an ErrorOr<T>. +template <typename T> ErrorOr<T> expectedToErrorOr(Expected<T> &&E) { + if (auto Err = E.takeError()) + return errorToErrorCode(std::move(Err)); + return std::move(*E); +} + +/// This class wraps a string in an Error. +/// +/// StringError is useful in cases where the client is not expected to be able +/// to consume the specific error message programmatically (for example, if the +/// error message is to be presented to the user). +/// +/// StringError can also be used when additional information is to be printed +/// along with a error_code message. Depending on the constructor called, this +/// class can either display: +/// 1. the error_code message (ECError behavior) +/// 2. a string +/// 3. the error_code message and a string +/// +/// These behaviors are useful when subtyping is required; for example, when a +/// specific library needs an explicit error type. In the example below, +/// PDBError is derived from StringError: +/// +/// @code{.cpp} +/// Expected<int> foo() { +/// return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading, +/// "Additional information"); +/// } +/// @endcode +/// +class StringError : public ErrorInfo<StringError> { +public: + static char ID; + + // Prints EC + S and converts to EC + StringError(std::error_code EC, const Twine &S = Twine()); + + // Prints S and converts to EC + StringError(const Twine &S, std::error_code EC); + + void log(raw_ostream &OS) const override; + std::error_code convertToErrorCode() const override; + + const std::string &getMessage() const { return Msg; } + +private: + std::string Msg; + std::error_code EC; + const bool PrintMsgOnly = false; +}; + +/// Create formatted StringError object. +template <typename... Ts> +inline Error createStringError(std::error_code EC, char const *Fmt, + const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error<StringError>(Stream.str(), EC); +} + +Error createStringError(std::error_code EC, char const *Msg); + +inline Error createStringError(std::error_code EC, const Twine &S) { + return createStringError(EC, S.str().c_str()); +} + +template <typename... Ts> +inline Error createStringError(std::errc EC, char const *Fmt, + const Ts &... Vals) { + return createStringError(std::make_error_code(EC), Fmt, Vals...); +} + +/// This class wraps a filename and another Error. +/// +/// In some cases, an error needs to live along a 'source' name, in order to +/// show more detailed information to the user. +class FileError final : public ErrorInfo<FileError> { + + friend Error createFileError(const Twine &, Error); + friend Error createFileError(const Twine &, size_t, Error); + +public: + void log(raw_ostream &OS) const override { + assert(Err && "Trying to log after takeError()."); + OS << "'" << FileName << "': "; + if (Line) + OS << "line " << *Line << ": "; + Err->log(OS); + } + + std::string messageWithoutFileInfo() const { + std::string Msg; + raw_string_ostream OS(Msg); + Err->log(OS); + return OS.str(); + } + + StringRef getFileName() const { return FileName; } + + Error takeError() { return Error(std::move(Err)); } + + std::error_code convertToErrorCode() const override; + + // Used by ErrorInfo::classID. + static char ID; + +private: + FileError(const Twine &F, std::optional<size_t> LineNum, + std::unique_ptr<ErrorInfoBase> E) { + assert(E && "Cannot create FileError from Error success value."); + FileName = F.str(); + Err = std::move(E); + Line = std::move(LineNum); + } + + static Error build(const Twine &F, std::optional<size_t> Line, Error E) { + std::unique_ptr<ErrorInfoBase> Payload; + handleAllErrors(std::move(E), + [&](std::unique_ptr<ErrorInfoBase> EIB) -> Error { + Payload = std::move(EIB); + return Error::success(); + }); + return Error( + std::unique_ptr<FileError>(new FileError(F, Line, std::move(Payload)))); + } + + std::string FileName; + std::optional<size_t> Line; + std::unique_ptr<ErrorInfoBase> Err; +}; + +/// Concatenate a source file path and/or name with an Error. The resulting +/// Error is unchecked. +inline Error createFileError(const Twine &F, Error E) { + return FileError::build(F, std::optional<size_t>(), std::move(E)); +} + +/// Concatenate a source file path and/or name with line number and an Error. +/// The resulting Error is unchecked. +inline Error createFileError(const Twine &F, size_t Line, Error E) { + return FileError::build(F, std::optional<size_t>(Line), std::move(E)); +} + +/// Concatenate a source file path and/or name with a std::error_code +/// to form an Error object. +inline Error createFileError(const Twine &F, std::error_code EC) { + return createFileError(F, errorCodeToError(EC)); +} + +/// Concatenate a source file path and/or name with line number and +/// std::error_code to form an Error object. +inline Error createFileError(const Twine &F, size_t Line, std::error_code EC) { + return createFileError(F, Line, errorCodeToError(EC)); +} + +Error createFileError(const Twine &F, ErrorSuccess) = delete; + +/// Helper for check-and-exit error handling. +/// +/// For tool use only. NOT FOR USE IN LIBRARY CODE. +/// +class ExitOnError { +public: + /// Create an error on exit helper. + ExitOnError(std::string Banner = "", int DefaultErrorExitCode = 1) + : Banner(std::move(Banner)), + GetExitCode([=](const Error &) { return DefaultErrorExitCode; }) {} + + /// Set the banner string for any errors caught by operator(). + void setBanner(std::string Banner) { this->Banner = std::move(Banner); } + + /// Set the exit-code mapper function. + void setExitCodeMapper(std::function<int(const Error &)> GetExitCode) { + this->GetExitCode = std::move(GetExitCode); + } + + /// Check Err. If it's in a failure state log the error(s) and exit. + void operator()(Error Err) const { checkError(std::move(Err)); } + + /// Check E. If it's in a success state then return the contained value. If + /// it's in a failure state log the error(s) and exit. + template <typename T> T operator()(Expected<T> &&E) const { + checkError(E.takeError()); + return std::move(*E); + } + + /// Check E. If it's in a success state then return the contained reference. If + /// it's in a failure state log the error(s) and exit. + template <typename T> T& operator()(Expected<T&> &&E) const { + checkError(E.takeError()); + return *E; + } + +private: + void checkError(Error Err) const { + if (Err) { + int ExitCode = GetExitCode(Err); + logAllUnhandledErrors(std::move(Err), errs(), Banner); + exit(ExitCode); + } + } + + std::string Banner; + std::function<int(const Error &)> GetExitCode; +}; + +/// Conversion from Error to LLVMErrorRef for C error bindings. +inline LLVMErrorRef wrap(Error Err) { + return reinterpret_cast<LLVMErrorRef>(Err.takePayload().release()); +} + +/// Conversion from LLVMErrorRef to Error for C error bindings. +inline Error unwrap(LLVMErrorRef ErrRef) { + return Error(std::unique_ptr<ErrorInfoBase>( + reinterpret_cast<ErrorInfoBase *>(ErrRef))); +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_ERROR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ErrorHandling.h b/contrib/libs/llvm16/include/llvm/Support/ErrorHandling.h new file mode 100644 index 00000000000..f1292b31178 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ErrorHandling.h @@ -0,0 +1,168 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/ErrorHandling.h - Fatal error handling ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines an API used to indicate fatal error conditions. Non-fatal +// errors (most of them) should be handled through LLVMContext. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ERRORHANDLING_H +#define LLVM_SUPPORT_ERRORHANDLING_H + +#include "llvm/Support/Compiler.h" + +namespace llvm { + class StringRef; + class Twine; + + /// An error handler callback. + typedef void (*fatal_error_handler_t)(void *user_data, + const char *reason, + bool gen_crash_diag); + + /// install_fatal_error_handler - Installs a new error handler to be used + /// whenever a serious (non-recoverable) error is encountered by LLVM. + /// + /// If no error handler is installed the default is to print the error message + /// to stderr, and call exit(1). If an error handler is installed then it is + /// the handler's responsibility to log the message, it will no longer be + /// printed to stderr. If the error handler returns, then exit(1) will be + /// called. + /// + /// It is dangerous to naively use an error handler which throws an exception. + /// Even though some applications desire to gracefully recover from arbitrary + /// faults, blindly throwing exceptions through unfamiliar code isn't a way to + /// achieve this. + /// + /// \param user_data - An argument which will be passed to the install error + /// handler. + void install_fatal_error_handler(fatal_error_handler_t handler, + void *user_data = nullptr); + + /// Restores default error handling behaviour. + void remove_fatal_error_handler(); + + /// ScopedFatalErrorHandler - This is a simple helper class which just + /// calls install_fatal_error_handler in its constructor and + /// remove_fatal_error_handler in its destructor. + struct ScopedFatalErrorHandler { + explicit ScopedFatalErrorHandler(fatal_error_handler_t handler, + void *user_data = nullptr) { + install_fatal_error_handler(handler, user_data); + } + + ~ScopedFatalErrorHandler() { remove_fatal_error_handler(); } + }; + +/// Reports a serious error, calling any installed error handler. These +/// functions are intended to be used for error conditions which are outside +/// the control of the compiler (I/O errors, invalid user input, etc.) +/// +/// If no error handler is installed the default is to print the message to +/// standard error, followed by a newline. +/// After the error handler is called this function will call abort(), it +/// does not return. +/// NOTE: The std::string variant was removed to avoid a <string> dependency. +[[noreturn]] void report_fatal_error(const char *reason, + bool gen_crash_diag = true); +[[noreturn]] void report_fatal_error(StringRef reason, + bool gen_crash_diag = true); +[[noreturn]] void report_fatal_error(const Twine &reason, + bool gen_crash_diag = true); + +/// Installs a new bad alloc error handler that should be used whenever a +/// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM. +/// +/// The user can install a bad alloc handler, in order to define the behavior +/// in case of failing allocations, e.g. throwing an exception. Note that this +/// handler must not trigger any additional allocations itself. +/// +/// If no error handler is installed the default is to print the error message +/// to stderr, and call exit(1). If an error handler is installed then it is +/// the handler's responsibility to log the message, it will no longer be +/// printed to stderr. If the error handler returns, then exit(1) will be +/// called. +/// +/// +/// \param user_data - An argument which will be passed to the installed error +/// handler. +void install_bad_alloc_error_handler(fatal_error_handler_t handler, + void *user_data = nullptr); + +/// Restores default bad alloc error handling behavior. +void remove_bad_alloc_error_handler(); + +void install_out_of_memory_new_handler(); + +/// Reports a bad alloc error, calling any user defined bad alloc +/// error handler. In contrast to the generic 'report_fatal_error' +/// functions, this function might not terminate, e.g. the user +/// defined error handler throws an exception, but it won't return. +/// +/// Note: When throwing an exception in the bad alloc handler, make sure that +/// the following unwind succeeds, e.g. do not trigger additional allocations +/// in the unwind chain. +/// +/// If no error handler is installed (default), throws a bad_alloc exception +/// if LLVM is compiled with exception support. Otherwise prints the error +/// to standard error and calls abort(). +[[noreturn]] void report_bad_alloc_error(const char *Reason, + bool GenCrashDiag = true); + +/// This function calls abort(), and prints the optional message to stderr. +/// Use the llvm_unreachable macro (that adds location info), instead of +/// calling this function directly. +[[noreturn]] void +llvm_unreachable_internal(const char *msg = nullptr, const char *file = nullptr, + unsigned line = 0); +} + +/// Marks that the current location is not supposed to be reachable. +/// In !NDEBUG builds, prints the message and location info to stderr. +/// In NDEBUG builds, if the platform does not support a builtin unreachable +/// then we call an internal LLVM runtime function. Otherwise the behavior is +/// controlled by the CMake flag +/// -DLLVM_UNREACHABLE_OPTIMIZE +/// * When "ON" (default) llvm_unreachable() becomes an optimizer hint +/// that the current location is not supposed to be reachable: the hint +/// turns such code path into undefined behavior. On compilers that don't +/// support such hints, prints a reduced message instead and aborts the +/// program. +/// * When "OFF", a builtin_trap is emitted instead of an +// optimizer hint or printing a reduced message. +/// +/// Use this instead of assert(0). It conveys intent more clearly, suppresses +/// diagnostics for unreachable code paths, and allows compilers to omit +/// unnecessary code. +#ifndef NDEBUG +#define llvm_unreachable(msg) \ + ::llvm::llvm_unreachable_internal(msg, __FILE__, __LINE__) +#elif !defined(LLVM_BUILTIN_UNREACHABLE) +#define llvm_unreachable(msg) ::llvm::llvm_unreachable_internal() +#elif LLVM_UNREACHABLE_OPTIMIZE +#define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE +#else +#define llvm_unreachable(msg) \ + do { \ + LLVM_BUILTIN_TRAP; \ + LLVM_BUILTIN_UNREACHABLE; \ + } while (false) +#endif + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ErrorOr.h b/contrib/libs/llvm16/include/llvm/Support/ErrorOr.h new file mode 100644 index 00000000000..67d9a081267 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ErrorOr.h @@ -0,0 +1,283 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Provides ErrorOr<T> smart pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ERROROR_H +#define LLVM_SUPPORT_ERROROR_H + +#include "llvm/Support/AlignOf.h" +#include <cassert> +#include <system_error> +#include <type_traits> +#include <utility> + +namespace llvm { + +/// Represents either an error or a value T. +/// +/// ErrorOr<T> is a pointer-like class that represents the result of an +/// operation. The result is either an error, or a value of type T. This is +/// designed to emulate the usage of returning a pointer where nullptr indicates +/// failure. However instead of just knowing that the operation failed, we also +/// have an error_code and optional user data that describes why it failed. +/// +/// It is used like the following. +/// \code +/// ErrorOr<Buffer> getBuffer(); +/// +/// auto buffer = getBuffer(); +/// if (error_code ec = buffer.getError()) +/// return ec; +/// buffer->write("adena"); +/// \endcode +/// +/// +/// Implicit conversion to bool returns true if there is a usable value. The +/// unary * and -> operators provide pointer like access to the value. Accessing +/// the value when there is an error has undefined behavior. +/// +/// When T is a reference type the behavior is slightly different. The reference +/// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and +/// there is special handling to make operator -> work as if T was not a +/// reference. +/// +/// T cannot be a rvalue reference. +template<class T> +class ErrorOr { + template <class OtherT> friend class ErrorOr; + + static constexpr bool isRef = std::is_reference<T>::value; + + using wrap = std::reference_wrapper<std::remove_reference_t<T>>; + +public: + using storage_type = std::conditional_t<isRef, wrap, T>; + +private: + using reference = std::remove_reference_t<T> &; + using const_reference = const std::remove_reference_t<T> &; + using pointer = std::remove_reference_t<T> *; + using const_pointer = const std::remove_reference_t<T> *; + +public: + template <class E> + ErrorOr(E ErrorCode, + std::enable_if_t<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + void *> = nullptr) + : HasError(true) { + new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); + } + + ErrorOr(std::error_code EC) : HasError(true) { + new (getErrorStorage()) std::error_code(EC); + } + + template <class OtherT> + ErrorOr(OtherT &&Val, + std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) + : HasError(false) { + new (getStorage()) storage_type(std::forward<OtherT>(Val)); + } + + ErrorOr(const ErrorOr &Other) { + copyConstruct(Other); + } + + template <class OtherT> + ErrorOr(const ErrorOr<OtherT> &Other, + std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) { + copyConstruct(Other); + } + + template <class OtherT> + explicit ErrorOr( + const ErrorOr<OtherT> &Other, + std::enable_if_t<!std::is_convertible<OtherT, const T &>::value> * = + nullptr) { + copyConstruct(Other); + } + + ErrorOr(ErrorOr &&Other) { + moveConstruct(std::move(Other)); + } + + template <class OtherT> + ErrorOr(ErrorOr<OtherT> &&Other, + std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) { + moveConstruct(std::move(Other)); + } + + // This might eventually need SFINAE but it's more complex than is_convertible + // & I'm too lazy to write it right now. + template <class OtherT> + explicit ErrorOr( + ErrorOr<OtherT> &&Other, + std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) { + moveConstruct(std::move(Other)); + } + + ErrorOr &operator=(const ErrorOr &Other) { + copyAssign(Other); + return *this; + } + + ErrorOr &operator=(ErrorOr &&Other) { + moveAssign(std::move(Other)); + return *this; + } + + ~ErrorOr() { + if (!HasError) + getStorage()->~storage_type(); + } + + /// Return false if there is an error. + explicit operator bool() const { + return !HasError; + } + + reference get() { return *getStorage(); } + const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); } + + std::error_code getError() const { + return HasError ? *getErrorStorage() : std::error_code(); + } + + pointer operator ->() { + return toPointer(getStorage()); + } + + const_pointer operator->() const { return toPointer(getStorage()); } + + reference operator *() { + return *getStorage(); + } + + const_reference operator*() const { return *getStorage(); } + +private: + template <class OtherT> + void copyConstruct(const ErrorOr<OtherT> &Other) { + if (!Other.HasError) { + // Get the other value. + HasError = false; + new (getStorage()) storage_type(*Other.getStorage()); + } else { + // Get other's error. + HasError = true; + new (getErrorStorage()) std::error_code(Other.getError()); + } + } + + template <class T1> + static bool compareThisIfSameType(const T1 &a, const T1 &b) { + return &a == &b; + } + + template <class T1, class T2> + static bool compareThisIfSameType(const T1 &a, const T2 &b) { + return false; + } + + template <class OtherT> + void copyAssign(const ErrorOr<OtherT> &Other) { + if (compareThisIfSameType(*this, Other)) + return; + + this->~ErrorOr(); + new (this) ErrorOr(Other); + } + + template <class OtherT> + void moveConstruct(ErrorOr<OtherT> &&Other) { + if (!Other.HasError) { + // Get the other value. + HasError = false; + new (getStorage()) storage_type(std::move(*Other.getStorage())); + } else { + // Get other's error. + HasError = true; + new (getErrorStorage()) std::error_code(Other.getError()); + } + } + + template <class OtherT> + void moveAssign(ErrorOr<OtherT> &&Other) { + if (compareThisIfSameType(*this, Other)) + return; + + this->~ErrorOr(); + new (this) ErrorOr(std::move(Other)); + } + + pointer toPointer(pointer Val) { + return Val; + } + + const_pointer toPointer(const_pointer Val) const { return Val; } + + pointer toPointer(wrap *Val) { + return &Val->get(); + } + + const_pointer toPointer(const wrap *Val) const { return &Val->get(); } + + storage_type *getStorage() { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast<storage_type *>(&TStorage); + } + + const storage_type *getStorage() const { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast<const storage_type *>(&TStorage); + } + + std::error_code *getErrorStorage() { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast<std::error_code *>(&ErrorStorage); + } + + const std::error_code *getErrorStorage() const { + return const_cast<ErrorOr<T> *>(this)->getErrorStorage(); + } + + union { + AlignedCharArrayUnion<storage_type> TStorage; + AlignedCharArrayUnion<std::error_code> ErrorStorage; + }; + bool HasError : 1; +}; + +template <class T, class E> +std::enable_if_t<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + bool> +operator==(const ErrorOr<T> &Err, E Code) { + return Err.getError() == Code; +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_ERROROR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ExitCodes.h b/contrib/libs/llvm16/include/llvm/Support/ExitCodes.h new file mode 100644 index 00000000000..336cab190dd --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ExitCodes.h @@ -0,0 +1,44 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ExitCodes.h - Exit codes for exit() -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains definitions of exit codes for exit() function. They are +/// either defined by sysexits.h if it is supported, or defined here if +/// sysexits.h is not supported. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_EXITCODES_H +#define LLVM_SUPPORT_EXITCODES_H + +#include "llvm/Config/llvm-config.h" + +#if HAVE_SYSEXITS_H +#include <sysexits.h> +#elif __MVS__ || defined(_WIN32) +// <sysexits.h> does not exist on z/OS and Windows. The only value used in LLVM +// is EX_IOERR, which is used to signal a special error condition (broken pipe). +// Define the macro with its usual value from BSD systems, which is chosen to +// not clash with more standard exit codes like 1. +#define EX_IOERR 74 +#elif LLVM_ON_UNIX +#error Exit code EX_IOERR not available +#endif + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ExtensibleRTTI.h b/contrib/libs/llvm16/include/llvm/Support/ExtensibleRTTI.h new file mode 100644 index 00000000000..952d95ef31a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ExtensibleRTTI.h @@ -0,0 +1,143 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ExtensibleRTTI.h - ExtensibleRTTI support --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// \file +// +// Defines an extensible RTTI mechanism designed to work with Casting.h. +// +// Extensible RTTI differs from LLVM's primary RTTI mechanism (see +// llvm.org/docs/HowToSetUpLLVMStyleRTTI.html) by supporting open type +// hierarchies, where new types can be added from outside libraries without +// needing to change existing code. LLVM's primary RTTI mechanism should be +// preferred where possible, but where open hierarchies are needed this system +// can be used. +// +// The RTTIRoot class defines methods for comparing type ids. Implementations +// of these methods can be injected into new classes using the RTTIExtends +// class template. +// +// E.g. +// +// @code{.cpp} +// class MyBaseClass : public RTTIExtends<MyBaseClass, RTTIRoot> { +// public: +// static char ID; +// virtual void foo() = 0; +// }; +// +// class MyDerivedClass1 : public RTTIExtends<MyDerivedClass1, MyBaseClass> { +// public: +// static char ID; +// void foo() override {} +// }; +// +// class MyDerivedClass2 : public RTTIExtends<MyDerivedClass2, MyBaseClass> { +// public: +// static char ID; +// void foo() override {} +// }; +// +// char MyBaseClass::ID = 0; +// char MyDerivedClass1::ID = 0; +// char MyDerivedClass2:: ID = 0; +// +// void fn() { +// std::unique_ptr<MyBaseClass> B = llvm::make_unique<MyDerivedClass1>(); +// llvm::outs() << isa<MyBaseClass>(B) << "\n"; // Outputs "1". +// llvm::outs() << isa<MyDerivedClass1>(B) << "\n"; // Outputs "1". +// llvm::outs() << isa<MyDerivedClass2>(B) << "\n"; // Outputs "0'. +// } +// +// @endcode +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_EXTENSIBLERTTI_H +#define LLVM_SUPPORT_EXTENSIBLERTTI_H + +namespace llvm { + +/// Base class for the extensible RTTI hierarchy. +/// +/// This class defines virtual methods, dynamicClassID and isA, that enable +/// type comparisons. +class RTTIRoot { +public: + virtual ~RTTIRoot() = default; + + /// Returns the class ID for this type. + static const void *classID() { return &ID; } + + /// Returns the class ID for the dynamic type of this RTTIRoot instance. + virtual const void *dynamicClassID() const = 0; + + /// Returns true if this class's ID matches the given class ID. + virtual bool isA(const void *const ClassID) const { + return ClassID == classID(); + } + + /// Check whether this instance is a subclass of QueryT. + template <typename QueryT> + bool isA() const { return isA(QueryT::classID()); } + +private: + virtual void anchor(); + + static char ID; +}; + +/// Inheritance utility for extensible RTTI. +/// +/// Supports single inheritance only: A class can only have one +/// ExtensibleRTTI-parent (i.e. a parent for which the isa<> test will work), +/// though it can have many non-ExtensibleRTTI parents. +/// +/// RTTIExtents uses CRTP so the first template argument to RTTIExtends is the +/// newly introduced type, and the *second* argument is the parent class. +/// +/// class MyType : public RTTIExtends<MyType, RTTIRoot> { +/// public: +/// static char ID; +/// }; +/// +/// class MyDerivedType : public RTTIExtends<MyDerivedType, MyType> { +/// public: +/// static char ID; +/// }; +/// +template <typename ThisT, typename ParentT> +class RTTIExtends : public ParentT { +public: + // Inherit constructors from ParentT. + using ParentT::ParentT; + + static const void *classID() { return &ThisT::ID; } + + const void *dynamicClassID() const override { return &ThisT::ID; } + + bool isA(const void *const ClassID) const override { + return ClassID == classID() || ParentT::isA(ClassID); + } + + static bool classof(const RTTIRoot *R) { return R->isA<ThisT>(); } +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_EXTENSIBLERTTI_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FileCollector.h b/contrib/libs/llvm16/include/llvm/Support/FileCollector.h new file mode 100644 index 00000000000..7db57261517 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FileCollector.h @@ -0,0 +1,157 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- FileCollector.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FILECOLLECTOR_H +#define LLVM_SUPPORT_FILECOLLECTOR_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/VirtualFileSystem.h" +#include <mutex> +#include <string> + +namespace llvm { +class FileCollectorFileSystem; +class Twine; + +class FileCollectorBase { +public: + FileCollectorBase(); + virtual ~FileCollectorBase(); + + void addFile(const Twine &file); + void addDirectory(const Twine &Dir); + +protected: + bool markAsSeen(StringRef Path) { + if (Path.empty()) + return false; + return Seen.insert(Path).second; + } + + virtual void addFileImpl(StringRef SrcPath) = 0; + + virtual llvm::vfs::directory_iterator + addDirectoryImpl(const llvm::Twine &Dir, + IntrusiveRefCntPtr<vfs::FileSystem> FS, + std::error_code &EC) = 0; + + /// Synchronizes access to internal data structures. + std::mutex Mutex; + + /// Tracks already seen files so they can be skipped. + StringSet<> Seen; +}; + +/// Captures file system interaction and generates data to be later replayed +/// with the RedirectingFileSystem. +/// +/// For any file that gets accessed we eventually create: +/// - a copy of the file inside Root +/// - a record in RedirectingFileSystem mapping that maps: +/// current real path -> path to the copy in Root +/// +/// That intent is that later when the mapping is used by RedirectingFileSystem +/// it simulates the state of FS that we collected. +/// +/// We generate file copies and mapping lazily - see writeMapping and copyFiles. +/// We don't try to capture the state of the file at the exact time when it's +/// accessed. Files might get changed, deleted ... we record only the "final" +/// state. +/// +/// In order to preserve the relative topology of files we use their real paths +/// as relative paths inside of the Root. +class FileCollector : public FileCollectorBase { +public: + /// Helper utility that encapsulates the logic for canonicalizing a virtual + /// path and a path to copy from. + class PathCanonicalizer { + public: + struct PathStorage { + SmallString<256> CopyFrom; + SmallString<256> VirtualPath; + }; + + /// Canonicalize a pair of virtual and real paths. + PathStorage canonicalize(StringRef SrcPath); + + private: + /// Replace with a (mostly) real path, or don't modify. Resolves symlinks + /// in the directory, using \a CachedDirs to avoid redundant lookups, but + /// leaves the filename as a possible symlink. + void updateWithRealPath(SmallVectorImpl<char> &Path); + + StringMap<std::string> CachedDirs; + }; + + /// \p Root is the directory where collected files are will be stored. + /// \p OverlayRoot is VFS mapping root. + /// \p Root directory gets created in copyFiles unless it already exists. + FileCollector(std::string Root, std::string OverlayRoot); + + /// Write the yaml mapping (for the VFS) to the given file. + std::error_code writeMapping(StringRef MappingFile); + + /// Copy the files into the root directory. + /// + /// When StopOnError is true (the default) we abort as soon as one file + /// cannot be copied. This is relatively common, for example when a file was + /// removed after it was added to the mapping. + std::error_code copyFiles(bool StopOnError = true); + + /// Create a VFS that uses \p Collector to collect files accessed via \p + /// BaseFS. + static IntrusiveRefCntPtr<vfs::FileSystem> + createCollectorVFS(IntrusiveRefCntPtr<vfs::FileSystem> BaseFS, + std::shared_ptr<FileCollector> Collector); + +private: + friend FileCollectorFileSystem; + + void addFileToMapping(StringRef VirtualPath, StringRef RealPath) { + if (sys::fs::is_directory(VirtualPath)) + VFSWriter.addDirectoryMapping(VirtualPath, RealPath); + else + VFSWriter.addFileMapping(VirtualPath, RealPath); + } + +protected: + void addFileImpl(StringRef SrcPath) override; + + llvm::vfs::directory_iterator + addDirectoryImpl(const llvm::Twine &Dir, + IntrusiveRefCntPtr<vfs::FileSystem> FS, + std::error_code &EC) override; + + /// The directory where collected files are copied to in copyFiles(). + const std::string Root; + + /// The root directory where the VFS overlay lives. + const std::string OverlayRoot; + + /// The yaml mapping writer. + vfs::YAMLVFSWriter VFSWriter; + + /// Helper utility for canonicalizing paths. + PathCanonicalizer Canonicalizer; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_FILECOLLECTOR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FileOutputBuffer.h b/contrib/libs/llvm16/include/llvm/Support/FileOutputBuffer.h new file mode 100644 index 00000000000..f71785c09a6 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FileOutputBuffer.h @@ -0,0 +1,97 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//=== FileOutputBuffer.h - File Output Buffer -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Utility for creating a in-memory buffer that will be written to a file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FILEOUTPUTBUFFER_H +#define LLVM_SUPPORT_FILEOUTPUTBUFFER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Error.h" + +namespace llvm { +/// FileOutputBuffer - This interface provides simple way to create an in-memory +/// buffer which will be written to a file. During the lifetime of these +/// objects, the content or existence of the specified file is undefined. That +/// is, creating an OutputBuffer for a file may immediately remove the file. +/// If the FileOutputBuffer is committed, the target file's content will become +/// the buffer content at the time of the commit. If the FileOutputBuffer is +/// not committed, the file will be deleted in the FileOutputBuffer destructor. +class FileOutputBuffer { +public: + enum { + /// Set the 'x' bit on the resulting file. + F_executable = 1, + + /// Don't use mmap and instead write an in-memory buffer to a file when this + /// buffer is closed. + F_no_mmap = 2, + }; + + /// 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. + /// + /// When F_modify is specified and \p FilePath refers to an existing on-disk + /// file \p Size may be set to -1, in which case the entire file is used. + /// Otherwise, the file shrinks or grows as necessary based on the value of + /// \p Size. It is an error to specify F_modify and Size=-1 if \p FilePath + /// does not exist. + static Expected<std::unique_ptr<FileOutputBuffer>> + create(StringRef FilePath, size_t Size, unsigned Flags = 0); + + /// Returns a pointer to the start of the buffer. + virtual uint8_t *getBufferStart() const = 0; + + /// Returns a pointer to the end of the buffer. + virtual uint8_t *getBufferEnd() const = 0; + + /// Returns size of the buffer. + virtual size_t getBufferSize() const = 0; + + /// Returns path where file will show up if buffer is committed. + 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. + 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. + virtual ~FileOutputBuffer() = default; + + /// This removes the temporary file (unless it already was committed) + /// but keeps the memory mapping alive. + virtual void discard() {} + +protected: + FileOutputBuffer(StringRef Path) : FinalPath(Path) {} + + std::string FinalPath; +}; +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FileSystem.h b/contrib/libs/llvm16/include/llvm/Support/FileSystem.h new file mode 100644 index 00000000000..0fe6fbd86da --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FileSystem.h @@ -0,0 +1,1598 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/FileSystem.h - File System OS Concept -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::fs namespace. It is designed after +// TR2/boost filesystem (v3), but modified to remove exception handling and the +// path class. +// +// All functions return an error_code and their actual work via the last out +// argument. The out argument is defined if and only if errc::success is +// returned. A function may return any error code in the generic or system +// category. However, they shall be equivalent to any error conditions listed +// in each functions respective documentation if the condition applies. [ note: +// this does not guarantee that error_code will be in the set of explicitly +// listed codes, but it does guarantee that if any of the explicitly listed +// errors occur, the correct error_code will be used ]. All functions may +// return errc::not_enough_memory if there is not enough memory to complete the +// operation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FILESYSTEM_H +#define LLVM_SUPPORT_FILESYSTEM_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem/UniqueID.h" +#include "llvm/Support/MD5.h" +#include <cassert> +#include <cstdint> +#include <ctime> +#include <memory> +#include <stack> +#include <string> +#include <system_error> +#include <vector> + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +namespace llvm { +namespace sys { +namespace fs { + +#if defined(_WIN32) +// A Win32 HANDLE is a typedef of void* +using file_t = void *; +#else +using file_t = int; +#endif + +extern const file_t kInvalidFile; + +/// An enumeration for the file system's view of the type. +enum class file_type { + status_error, + file_not_found, + regular_file, + directory_file, + symlink_file, + block_file, + character_file, + fifo_file, + socket_file, + type_unknown +}; + +/// space_info - Self explanatory. +struct space_info { + uint64_t capacity; + uint64_t free; + uint64_t available; +}; + +enum perms { + no_perms = 0, + owner_read = 0400, + owner_write = 0200, + owner_exe = 0100, + owner_all = owner_read | owner_write | owner_exe, + group_read = 040, + group_write = 020, + group_exe = 010, + group_all = group_read | group_write | group_exe, + others_read = 04, + others_write = 02, + others_exe = 01, + others_all = others_read | others_write | others_exe, + all_read = owner_read | group_read | others_read, + all_write = owner_write | group_write | others_write, + all_exe = owner_exe | group_exe | others_exe, + all_all = owner_all | group_all | others_all, + set_uid_on_exe = 04000, + set_gid_on_exe = 02000, + sticky_bit = 01000, + all_perms = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, + perms_not_known = 0xFFFF +}; + +// Helper functions so that you can use & and | to manipulate perms bits: +inline perms operator|(perms l, perms r) { + return static_cast<perms>(static_cast<unsigned short>(l) | + static_cast<unsigned short>(r)); +} +inline perms operator&(perms l, perms r) { + return static_cast<perms>(static_cast<unsigned short>(l) & + static_cast<unsigned short>(r)); +} +inline perms &operator|=(perms &l, perms r) { + l = l | r; + return l; +} +inline perms &operator&=(perms &l, perms r) { + l = l & r; + return l; +} +inline perms operator~(perms x) { + // Avoid UB by explicitly truncating the (unsigned) ~ result. + return static_cast<perms>( + static_cast<unsigned short>(~static_cast<unsigned short>(x))); +} + +/// 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) + time_t fs_st_atime = 0; + time_t fs_st_mtime = 0; + uint32_t fs_st_atime_nsec = 0; + uint32_t fs_st_mtime_nsec = 0; + uid_t fs_st_uid = 0; + gid_t fs_st_gid = 0; + off_t fs_st_size = 0; + #elif defined (_WIN32) + uint32_t LastAccessedTimeHigh = 0; + uint32_t LastAccessedTimeLow = 0; + uint32_t LastWriteTimeHigh = 0; + uint32_t LastWriteTimeLow = 0; + uint32_t FileSizeHigh = 0; + uint32_t FileSizeLow = 0; + #endif + file_type Type = file_type::status_error; + perms Perms = perms_not_known; + +public: + basic_file_status() = default; + + explicit basic_file_status(file_type Type) : Type(Type) {} + + #if defined(LLVM_ON_UNIX) + basic_file_status(file_type Type, perms Perms, time_t ATime, + uint32_t ATimeNSec, time_t MTime, uint32_t MTimeNSec, + uid_t UID, gid_t GID, off_t Size) + : fs_st_atime(ATime), fs_st_mtime(MTime), + fs_st_atime_nsec(ATimeNSec), fs_st_mtime_nsec(MTimeNSec), + fs_st_uid(UID), fs_st_gid(GID), + fs_st_size(Size), Type(Type), Perms(Perms) {} +#elif defined(_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), FileSizeHigh(FileSizeHigh), + FileSizeLow(FileSizeLow), Type(Type), Perms(Perms) {} + #endif + + // getters + file_type type() const { return Type; } + perms permissions() const { return Perms; } + + /// The file access time as reported from the underlying file system. + /// + /// Also see comments on \c getLastModificationTime() related to the precision + /// of the returned value. + TimePoint<> getLastAccessedTime() const; + + /// The file modification time as reported from the underlying file system. + /// + /// The returned value allows for nanosecond precision but the actual + /// resolution is an implementation detail of the underlying file system. + /// There is no guarantee for what kind of resolution you can expect, the + /// resolution can differ across platforms and even across mountpoints on the + /// same machine. + TimePoint<> getLastModificationTime() const; + + #if defined(LLVM_ON_UNIX) + uint32_t getUser() const { return fs_st_uid; } + uint32_t getGroup() const { return fs_st_gid; } + uint64_t getSize() const { return fs_st_size; } + #elif defined (_WIN32) + uint32_t getUser() const { + return 9999; // Not applicable to Windows, so... + } + + uint32_t getGroup() const { + return 9999; // Not applicable to Windows, so... + } + + uint64_t getSize() const { + return (uint64_t(FileSizeHigh) << 32) + FileSizeLow; + } + #endif + + // setters + void type(file_type v) { Type = v; } + 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 (_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, uint32_t ATimeNSec, + time_t MTime, uint32_t MTimeNSec, + uid_t UID, gid_t GID, off_t Size) + : basic_file_status(Type, Perms, ATime, ATimeNSec, MTime, MTimeNSec, + UID, GID, Size), + fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino) {} + #elif defined(_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 +/// @{ + +/// Make \a path an absolute path. +/// +/// Makes \a path absolute using the \a current_directory if it is not already. +/// An empty \a path will result in the \a current_directory. +/// +/// /absolute/path => /absolute/path +/// relative/../path => <current-directory>/relative/../path +/// +/// @param path A path that is modified to be an absolute path. +void make_absolute(const Twine ¤t_directory, SmallVectorImpl<char> &path); + +/// Make \a path an absolute path. +/// +/// Makes \a path absolute using the current directory if it is not already. An +/// empty \a path will result in the current directory. +/// +/// /absolute/path => /absolute/path +/// relative/../path => <current-directory>/relative/../path +/// +/// @param path A path that is modified to be an absolute path. +/// @returns errc::success if \a path has been made absolute, otherwise a +/// platform-specific error_code. +std::error_code make_absolute(SmallVectorImpl<char> &path); + +/// Create all the non-existent directories in path. +/// +/// @param path Directories to create. +/// @returns errc::success if is_directory(path), otherwise a platform +/// specific error_code. If IgnoreExisting is false, also returns +/// error if the directory already existed. +std::error_code create_directories(const Twine &path, + bool IgnoreExisting = true, + perms Perms = owner_all | group_all); + +/// Create the directory in path. +/// +/// @param path Directory to create. +/// @returns errc::success if is_directory(path), otherwise a platform +/// specific error_code. If IgnoreExisting is false, also returns +/// error if the directory already existed. +std::error_code create_directory(const Twine &path, bool IgnoreExisting = true, + perms Perms = owner_all | group_all); + +/// Create a link from \a from to \a to. +/// +/// The link may be a soft or a hard link, depending on the platform. The caller +/// may not assume which one. Currently on windows it creates a hard link since +/// soft links require extra privileges. On unix, it creates a soft link since +/// hard links don't work on SMB file systems. +/// +/// @param to The path to hard link to. +/// @param from The path to hard link from. This is created. +/// @returns errc::success if the link was created, otherwise a platform +/// specific error_code. +std::error_code create_link(const Twine &to, const Twine &from); + +/// Create a hard link from \a from to \a to, or return an error. +/// +/// @param to The path to hard link to. +/// @param from The path to hard link from. This is created. +/// @returns errc::success if the link was created, otherwise a platform +/// specific error_code. +std::error_code create_hard_link(const Twine &to, const Twine &from); + +/// Collapse all . and .. patterns, resolve all symlinks, and optionally +/// expand ~ expressions to the user's home directory. +/// +/// @param path The path to resolve. +/// @param output The location to store the resolved path. +/// @param expand_tilde If true, resolves ~ expressions to the user's home +/// directory. +std::error_code real_path(const Twine &path, SmallVectorImpl<char> &output, + bool expand_tilde = false); + +/// Expands ~ expressions to the user's home directory. On Unix ~user +/// directories are resolved as well. +/// +/// @param path The path to resolve. +void expand_tilde(const Twine &path, SmallVectorImpl<char> &output); + +/// Get the current path. +/// +/// @param result Holds the current path on return. +/// @returns errc::success if the current path has been stored in result, +/// otherwise a platform-specific error_code. +std::error_code current_path(SmallVectorImpl<char> &result); + +/// Set the current path. +/// +/// @param path The path to set. +/// @returns errc::success if the current path was successfully set, +/// otherwise a platform-specific error_code. +std::error_code set_current_path(const Twine &path); + +/// Remove path. Equivalent to POSIX remove(). +/// +/// @param path Input path. +/// @returns errc::success if path has been removed or didn't exist, otherwise a +/// platform-specific error code. If IgnoreNonExisting is false, also +/// returns error if the file didn't exist. +std::error_code remove(const Twine &path, bool IgnoreNonExisting = true); + +/// Recursively delete a directory. +/// +/// @param path Input path. +/// @returns errc::success if path has been removed or didn't exist, otherwise a +/// platform-specific error code. +std::error_code remove_directories(const Twine &path, bool IgnoreErrors = true); + +/// 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. +std::error_code rename(const Twine &from, const Twine &to); + +/// Copy the contents of \a From to \a To. +/// +/// @param From The path to copy from. +/// @param To The path to copy to. This is created. +std::error_code copy_file(const Twine &From, const Twine &To); + +/// Copy the contents of \a From to \a To. +/// +/// @param From The path to copy from. +/// @param ToFD The open file descriptor of the destination file. +std::error_code copy_file(const Twine &From, int ToFD); + +/// Resize path to size. File is resized as if by POSIX truncate(). +/// +/// @param FD Input file descriptor. +/// @param Size Size to resize to. +/// @returns errc::success if \a path has been resized to \a size, otherwise a +/// platform-specific error_code. +std::error_code resize_file(int FD, uint64_t Size); + +/// Resize \p FD to \p Size before mapping \a mapped_file_region::readwrite. On +/// non-Windows, this calls \a resize_file(). On Windows, this is a no-op, +/// since the subsequent mapping (via \c CreateFileMapping) automatically +/// extends the file. +inline std::error_code resize_file_before_mapping_readwrite(int FD, + uint64_t Size) { +#ifdef _WIN32 + (void)FD; + (void)Size; + return std::error_code(); +#else + return resize_file(FD, Size); +#endif +} + +/// Compute an MD5 hash of a file's contents. +/// +/// @param FD Input file descriptor. +/// @returns An MD5Result with the hash computed, if successful, otherwise a +/// std::error_code. +ErrorOr<MD5::MD5Result> md5_contents(int FD); + +/// Version of compute_md5 that doesn't require an open file descriptor. +ErrorOr<MD5::MD5Result> md5_contents(const Twine &Path); + +/// @} +/// @name Physical Observers +/// @{ + +/// Does file exist? +/// +/// @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(const basic_file_status &status); + +enum class AccessMode { Exist, Write, Execute }; + +/// Can the file be accessed? +/// +/// @param Path Input path. +/// @returns errc::success if the path can be accessed, otherwise a +/// platform-specific error_code. +std::error_code access(const Twine &Path, AccessMode Mode); + +/// Does file exist? +/// +/// @param Path Input path. +/// @returns True if it exists, false otherwise. +inline bool exists(const Twine &Path) { + return !access(Path, AccessMode::Exist); +} + +/// Can we execute this file? +/// +/// @param Path Input path. +/// @returns True if we can execute it, false otherwise. +bool can_execute(const Twine &Path); + +/// Can we write this file? +/// +/// @param Path Input path. +/// @returns True if we can write to it, false otherwise. +inline bool can_write(const Twine &Path) { + return !access(Path, AccessMode::Write); +} + +/// Do file_status's represent the same thing? +/// +/// @param A Input file_status. +/// @param B Input file_status. +/// +/// assert(status_known(A) || status_known(B)); +/// +/// @returns True if A and B both represent the same file system entity, false +/// otherwise. +bool equivalent(file_status A, file_status B); + +/// Do paths represent the same thing? +/// +/// assert(status_known(A) || status_known(B)); +/// +/// @param A Input path A. +/// @param B Input path B. +/// @param result Set to true if stat(A) and stat(B) have the same device and +/// inode (or equivalent). +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code equivalent(const Twine &A, const Twine &B, bool &result); + +/// Simpler version of equivalent for clients that don't need to +/// differentiate between an error and false. +inline bool equivalent(const Twine &A, const Twine &B) { + bool result; + return !equivalent(A, B, result) && result; +} + +/// Is the file mounted on a local filesystem? +/// +/// @param path Input path. +/// @param result Set to true if \a path is on fixed media such as a hard disk, +/// false if it is not. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform specific error_code. +std::error_code is_local(const Twine &path, bool &result); + +/// Version of is_local accepting an open file descriptor. +std::error_code is_local(int FD, bool &result); + +/// Simpler version of is_local for clients that don't need to +/// differentiate between an error and false. +inline bool is_local(const Twine &Path) { + bool Result; + return !is_local(Path, Result) && Result; +} + +/// Simpler version of is_local accepting an open file descriptor for +/// clients that don't need to differentiate between an error and false. +inline bool is_local(int FD) { + bool Result; + return !is_local(FD, Result) && Result; +} + +/// Does status represent a directory? +/// +/// @param Path The path to get the type of. +/// @param Follow For symbolic links, indicates whether to return the file type +/// of the link itself, or of the target. +/// @returns A value from the file_type enumeration indicating the type of file. +file_type get_file_type(const Twine &Path, bool Follow = true); + +/// Does status represent a directory? +/// +/// @param status A basic_file_status previously returned from status. +/// @returns status.type() == file_type::directory_file. +bool is_directory(const basic_file_status &status); + +/// Is path a directory? +/// +/// @param path Input path. +/// @param result Set to true if \a path is a directory (after following +/// symlinks, false if it is not. Undefined otherwise. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code is_directory(const Twine &path, bool &result); + +/// Simpler version of is_directory for clients that don't need to +/// differentiate between an error and false. +inline bool is_directory(const Twine &Path) { + bool Result; + return !is_directory(Path, Result) && Result; +} + +/// Does status represent a regular file? +/// +/// @param status A basic_file_status previously returned from status. +/// @returns status_known(status) && status.type() == file_type::regular_file. +bool is_regular_file(const basic_file_status &status); + +/// Is path a regular file? +/// +/// @param path Input path. +/// @param result Set to true if \a path is a regular file (after following +/// symlinks), false if it is not. Undefined otherwise. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code is_regular_file(const Twine &path, bool &result); + +/// Simpler version of is_regular_file for clients that don't need to +/// differentiate between an error and false. +inline bool is_regular_file(const Twine &Path) { + bool Result; + if (is_regular_file(Path, Result)) + return false; + return Result; +} + +/// Does status represent a symlink file? +/// +/// @param status A basic_file_status previously returned from status. +/// @returns status_known(status) && status.type() == file_type::symlink_file. +bool is_symlink_file(const basic_file_status &status); + +/// Is path a symlink file? +/// +/// @param path Input path. +/// @param result Set to true if \a path is a symlink file, false if it is not. +/// Undefined otherwise. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code is_symlink_file(const Twine &path, bool &result); + +/// Simpler version of is_symlink_file for clients that don't need to +/// differentiate between an error and false. +inline bool is_symlink_file(const Twine &Path) { + bool Result; + if (is_symlink_file(Path, Result)) + return false; + return Result; +} + +/// Does this status represent something that exists but is not a +/// directory or regular file? +/// +/// @param status A basic_file_status previously returned from status. +/// @returns exists(s) && !is_regular_file(s) && !is_directory(s) +bool is_other(const basic_file_status &status); + +/// Is path something that exists but is not a directory, +/// regular file, or symlink? +/// +/// @param path Input path. +/// @param result Set to true if \a path exists, but is not a directory, regular +/// file, or a symlink, false if it does not. Undefined otherwise. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code is_other(const Twine &path, bool &result); + +/// Get file status as if by POSIX stat(). +/// +/// @param path Input path. +/// @param result Set to the file status. +/// @param follow When true, follows symlinks. Otherwise, the symlink itself is +/// statted. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code status(const Twine &path, file_status &result, + bool follow = true); + +/// A version for when a file descriptor is already available. +std::error_code status(int FD, file_status &Result); + +#ifdef _WIN32 +/// A version for when a file descriptor is already available. +std::error_code status(file_t FD, file_status &Result); +#endif + +/// Get file creation mode mask of the process. +/// +/// @returns Mask reported by umask(2) +/// @note There is no umask on Windows. This function returns 0 always +/// on Windows. This function does not return an error_code because +/// umask(2) never fails. It is not thread safe. +unsigned getUmask(); + +/// Set file permissions. +/// +/// @param Path File to set permissions on. +/// @param Permissions New file permissions. +/// @returns errc::success if the permissions were successfully set, otherwise +/// a platform-specific error_code. +/// @note On Windows, all permissions except *_write are ignored. Using any of +/// owner_write, group_write, or all_write will make the file writable. +/// Otherwise, the file will be marked as read-only. +std::error_code setPermissions(const Twine &Path, perms Permissions); + +/// Vesion of setPermissions accepting a file descriptor. +/// TODO Delete the path based overload once we implement the FD based overload +/// on Windows. +std::error_code setPermissions(int FD, perms Permissions); + +/// Get file permissions. +/// +/// @param Path File to get permissions from. +/// @returns the permissions if they were successfully retrieved, otherwise a +/// platform-specific error_code. +/// @note On Windows, if the file does not have the FILE_ATTRIBUTE_READONLY +/// attribute, all_all will be returned. Otherwise, all_read | all_exe +/// will be returned. +ErrorOr<perms> getPermissions(const Twine &Path); + +/// Get file size. +/// +/// @param Path Input path. +/// @param Result Set to the size of the file in \a Path. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +inline std::error_code file_size(const Twine &Path, uint64_t &Result) { + file_status Status; + std::error_code EC = status(Path, Status); + if (EC) + return EC; + Result = Status.getSize(); + return std::error_code(); +} + +/// Set the file modification and access time. +/// +/// @returns errc::success if the file times were successfully set, otherwise a +/// platform-specific error_code or errc::function_not_supported on +/// platforms where the functionality isn't available. +std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, + TimePoint<> ModificationTime); + +/// Simpler version that sets both file modification and access time to the same +/// time. +inline std::error_code setLastAccessAndModificationTime(int FD, + TimePoint<> Time) { + return setLastAccessAndModificationTime(FD, Time, Time); +} + +/// Is status available? +/// +/// @param s Input file status. +/// @returns True if status() != status_error. +bool status_known(const basic_file_status &s); + +/// Is status available? +/// +/// @param path Input path. +/// @param result Set to true if status() != status_error. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code status_known(const Twine &path, bool &result); + +enum CreationDisposition : unsigned { + /// CD_CreateAlways - When opening a file: + /// * If it already exists, truncate it. + /// * If it does not already exist, create a new file. + CD_CreateAlways = 0, + + /// CD_CreateNew - When opening a file: + /// * If it already exists, fail. + /// * If it does not already exist, create a new file. + CD_CreateNew = 1, + + /// CD_OpenExisting - When opening a file: + /// * If it already exists, open the file with the offset set to 0. + /// * If it does not already exist, fail. + CD_OpenExisting = 2, + + /// CD_OpenAlways - When opening a file: + /// * If it already exists, open the file with the offset set to 0. + /// * If it does not already exist, create a new file. + CD_OpenAlways = 3, +}; + +enum FileAccess : unsigned { + FA_Read = 1, + FA_Write = 2, +}; + +enum OpenFlags : unsigned { + OF_None = 0, + + /// The file should be opened in text mode on platforms like z/OS that make + /// this distinction. + OF_Text = 1, + + /// The file should use a carriage linefeed '\r\n'. This flag should only be + /// used with OF_Text. Only makes a difference on Windows. + OF_CRLF = 2, + + /// The file should be opened in text mode and use a carriage linefeed '\r\n'. + /// This flag has the same functionality as OF_Text on z/OS but adds a + /// carriage linefeed on Windows. + OF_TextWithCRLF = OF_Text | OF_CRLF, + + /// The file should be opened in append mode. + OF_Append = 4, + + /// The returned handle can be used for deleting the file. Only makes a + /// difference on windows. + OF_Delete = 8, + + /// When a child process is launched, this file should remain open in the + /// child process. + OF_ChildInherit = 16, + + /// Force files Atime to be updated on access. Only makes a difference on + /// Windows. + OF_UpdateAtime = 32, +}; + +/// Create a potentially unique file name but does not create it. +/// +/// Generates a unique path suitable for a temporary file but does not +/// open or create the file. The name is based on \a Model with '%' +/// replaced by a random char in [0-9a-f]. If \a MakeAbsolute is true +/// then the system's temp directory is prepended first. If \a MakeAbsolute +/// is false the current directory will be used instead. +/// +/// This function does not check if the file exists. If you want to be sure +/// that the file does not yet exist, you should use use enough '%' characters +/// in your model to ensure this. Each '%' gives 4-bits of entropy so you can +/// use 32 of them to get 128 bits of entropy. +/// +/// Example: clang-%%-%%-%%-%%-%%.s => clang-a0-b1-c2-d3-e4.s +/// +/// @param Model Name to base unique path off of. +/// @param ResultPath Set to the file's path. +/// @param MakeAbsolute Whether to use the system temp directory. +void createUniquePath(const Twine &Model, SmallVectorImpl<char> &ResultPath, + bool MakeAbsolute); + +/// Create a uniquely named file. +/// +/// Generates a unique path suitable for a temporary file and then opens it as a +/// file. The name is based on \a Model with '%' replaced by a random char in +/// [0-9a-f]. If \a Model is not an absolute path, the temporary file will be +/// created in the current directory. +/// +/// Example: clang-%%-%%-%%-%%-%%.s => clang-a0-b1-c2-d3-e4.s +/// +/// This is an atomic operation. Either the file is created and opened, or the +/// file system is left untouched. +/// +/// The intended use is for files that are to be kept, possibly after +/// renaming them. For example, when running 'clang -c foo.o', the file can +/// be first created as foo-abc123.o and then renamed. +/// +/// @param Model Name to base unique path off of. +/// @param ResultFD Set to the opened file's file descriptor. +/// @param ResultPath Set to the opened file's absolute path. +/// @param Flags Set to the opened file's flags. +/// @param Mode Set to the opened file's permissions. +/// @returns errc::success if Result{FD,Path} have been successfully set, +/// otherwise a platform-specific error_code. +std::error_code createUniqueFile(const Twine &Model, int &ResultFD, + SmallVectorImpl<char> &ResultPath, + OpenFlags Flags = OF_None, + unsigned Mode = all_read | all_write); + +/// Simpler version for clients that don't want an open file. An empty +/// file will still be created. +std::error_code createUniqueFile(const Twine &Model, + SmallVectorImpl<char> &ResultPath, + unsigned Mode = all_read | all_write); + +/// 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, + OpenFlags ExtraFlags = OF_None); + TempFile(TempFile &&Other); + TempFile &operator=(TempFile &&Other); + + // Name of the temporary file. + std::string TmpName; + + // The open file descriptor. + int FD = -1; + +#ifdef _WIN32 + // Whether we need to manually remove the file on close. + bool RemoveOnClose = false; +#endif + + // 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(); +}; + +/// Create a file in the system temporary directory. +/// +/// The filename is of the form prefix-random_chars.suffix. Since the directory +/// is not know to the caller, Prefix and Suffix cannot have path separators. +/// The files are created with mode 0600. +/// +/// This should be used for things like a temporary .s that is removed after +/// running the assembler. +std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, + int &ResultFD, + SmallVectorImpl<char> &ResultPath, + OpenFlags Flags = OF_None); + +/// Simpler version for clients that don't want an open file. An empty +/// file will still be created. +std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, + SmallVectorImpl<char> &ResultPath, + OpenFlags Flags = OF_None); + +std::error_code createUniqueDirectory(const Twine &Prefix, + SmallVectorImpl<char> &ResultPath); + +/// Get a unique name, not currently exisiting in the filesystem. Subject +/// to race conditions, prefer to use createUniqueFile instead. +/// +/// Similar to createUniqueFile, but instead of creating a file only +/// checks if it exists. This function is subject to race conditions, if you +/// want to use the returned name to actually create a file, use +/// createUniqueFile instead. +std::error_code getPotentiallyUniqueFileName(const Twine &Model, + SmallVectorImpl<char> &ResultPath); + +/// Get a unique temporary file name, not currently exisiting in the +/// filesystem. Subject to race conditions, prefer to use createTemporaryFile +/// instead. +/// +/// Similar to createTemporaryFile, but instead of creating a file only +/// checks if it exists. This function is subject to race conditions, if you +/// want to use the returned name to actually create a file, use +/// createTemporaryFile instead. +std::error_code +getPotentiallyUniqueTempFileName(const Twine &Prefix, StringRef Suffix, + SmallVectorImpl<char> &ResultPath); + +inline OpenFlags operator|(OpenFlags A, OpenFlags B) { + return OpenFlags(unsigned(A) | unsigned(B)); +} + +inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) { + A = A | B; + return A; +} + +inline FileAccess operator|(FileAccess A, FileAccess B) { + return FileAccess(unsigned(A) | unsigned(B)); +} + +inline FileAccess &operator|=(FileAccess &A, FileAccess B) { + A = A | B; + return A; +} + +/// @brief Opens a file with the specified creation disposition, access mode, +/// and flags and returns a file descriptor. +/// +/// The caller is responsible for closing the file descriptor once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param ResultFD If the file could be opened successfully, its descriptor +/// is stored in this location. Otherwise, this is set to -1. +/// @param Disp Value specifying the existing-file behavior. +/// @param Access Value specifying whether to open the file in read, write, or +/// read-write mode. +/// @param Flags Additional flags. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. +std::error_code openFile(const Twine &Name, int &ResultFD, + CreationDisposition Disp, FileAccess Access, + OpenFlags Flags, unsigned Mode = 0666); + +/// @brief Opens a file with the specified creation disposition, access mode, +/// and flags and returns a platform-specific file object. +/// +/// The caller is responsible for closing the file object once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param Disp Value specifying the existing-file behavior. +/// @param Access Value specifying whether to open the file in read, write, or +/// read-write mode. +/// @param Flags Additional flags. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. +Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp, + FileAccess Access, OpenFlags Flags, + unsigned Mode = 0666); + +/// Converts from a Posix file descriptor number to a native file handle. +/// On Windows, this retreives the underlying handle. On non-Windows, this is a +/// no-op. +file_t convertFDToNativeFile(int FD); + +#ifndef _WIN32 +inline file_t convertFDToNativeFile(int FD) { return FD; } +#endif + +/// Return an open handle to standard in. On Unix, this is typically FD 0. +/// Returns kInvalidFile when the stream is closed. +file_t getStdinHandle(); + +/// Return an open handle to standard out. On Unix, this is typically FD 1. +/// Returns kInvalidFile when the stream is closed. +file_t getStdoutHandle(); + +/// Return an open handle to standard error. On Unix, this is typically FD 2. +/// Returns kInvalidFile when the stream is closed. +file_t getStderrHandle(); + +/// Reads \p Buf.size() bytes from \p FileHandle into \p Buf. Returns the number +/// of bytes actually read. On Unix, this is equivalent to `return ::read(FD, +/// Buf.data(), Buf.size())`, with error reporting. Returns 0 when reaching EOF. +/// +/// @param FileHandle File to read from. +/// @param Buf Buffer to read into. +/// @returns The number of bytes read, or error. +Expected<size_t> readNativeFile(file_t FileHandle, MutableArrayRef<char> Buf); + +/// Default chunk size for \a readNativeFileToEOF(). +enum : size_t { DefaultReadChunkSize = 4 * 4096 }; + +/// Reads from \p FileHandle until EOF, appending to \p Buffer in chunks of +/// size \p ChunkSize. +/// +/// This calls \a readNativeFile() in a loop. On Error, previous chunks that +/// were read successfully are left in \p Buffer and returned. +/// +/// Note: For reading the final chunk at EOF, \p Buffer's capacity needs extra +/// storage of \p ChunkSize. +/// +/// \param FileHandle File to read from. +/// \param Buffer Where to put the file content. +/// \param ChunkSize Size of chunks. +/// \returns The error if EOF was not found. +Error readNativeFileToEOF(file_t FileHandle, SmallVectorImpl<char> &Buffer, + ssize_t ChunkSize = DefaultReadChunkSize); + +/// Reads \p Buf.size() bytes from \p FileHandle at offset \p Offset into \p +/// Buf. If 'pread' is available, this will use that, otherwise it will use +/// 'lseek'. Returns the number of bytes actually read. Returns 0 when reaching +/// EOF. +/// +/// @param FileHandle File to read from. +/// @param Buf Buffer to read into. +/// @param Offset Offset into the file at which the read should occur. +/// @returns The number of bytes read, or error. +Expected<size_t> readNativeFileSlice(file_t FileHandle, + MutableArrayRef<char> Buf, + uint64_t Offset); + +/// @brief Opens the file with the given name in a write-only or read-write +/// mode, returning its open file descriptor. If the file does not exist, it +/// is created. +/// +/// The caller is responsible for closing the file descriptor once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param ResultFD If the file could be opened successfully, its descriptor +/// is stored in this location. Otherwise, this is set to -1. +/// @param Flags Additional flags used to determine whether the file should be +/// opened in, for example, read-write or in write-only mode. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. +inline std::error_code +openFileForWrite(const Twine &Name, int &ResultFD, + CreationDisposition Disp = CD_CreateAlways, + OpenFlags Flags = OF_None, unsigned Mode = 0666) { + return openFile(Name, ResultFD, Disp, FA_Write, Flags, Mode); +} + +/// @brief Opens the file with the given name in a write-only or read-write +/// mode, returning its open file descriptor. If the file does not exist, it +/// is created. +/// +/// The caller is responsible for closing the freeing the file once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param Flags Additional flags used to determine whether the file should be +/// opened in, for example, read-write or in write-only mode. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns a platform-specific file descriptor if \a Name has been opened, +/// otherwise an error object. +inline Expected<file_t> openNativeFileForWrite(const Twine &Name, + CreationDisposition Disp, + OpenFlags Flags, + unsigned Mode = 0666) { + return openNativeFile(Name, Disp, FA_Write, Flags, Mode); +} + +/// @brief Opens the file with the given name in a write-only or read-write +/// mode, returning its open file descriptor. If the file does not exist, it +/// is created. +/// +/// The caller is responsible for closing the file descriptor once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param ResultFD If the file could be opened successfully, its descriptor +/// is stored in this location. Otherwise, this is set to -1. +/// @param Flags Additional flags used to determine whether the file should be +/// opened in, for example, read-write or in write-only mode. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. +inline std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD, + CreationDisposition Disp, + OpenFlags Flags, + unsigned Mode = 0666) { + return openFile(Name, ResultFD, Disp, FA_Write | FA_Read, Flags, Mode); +} + +/// @brief Opens the file with the given name in a write-only or read-write +/// mode, returning its open file descriptor. If the file does not exist, it +/// is created. +/// +/// The caller is responsible for closing the freeing the file once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param Flags Additional flags used to determine whether the file should be +/// opened in, for example, read-write or in write-only mode. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns a platform-specific file descriptor if \a Name has been opened, +/// otherwise an error object. +inline Expected<file_t> openNativeFileForReadWrite(const Twine &Name, + CreationDisposition Disp, + OpenFlags Flags, + unsigned Mode = 0666) { + return openNativeFile(Name, Disp, FA_Write | FA_Read, Flags, Mode); +} + +/// @brief Opens the file with the given name in a read-only mode, returning +/// its open file descriptor. +/// +/// The caller is responsible for closing the file descriptor once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param ResultFD If the file could be opened successfully, its descriptor +/// is stored in this location. Otherwise, this is set to -1. +/// @param RealPath If nonnull, extra work is done to determine the real path +/// of the opened file, and that path is stored in this +/// location. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. +std::error_code openFileForRead(const Twine &Name, int &ResultFD, + OpenFlags Flags = OF_None, + SmallVectorImpl<char> *RealPath = nullptr); + +/// @brief Opens the file with the given name in a read-only mode, returning +/// its open file descriptor. +/// +/// The caller is responsible for closing the freeing the file once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param RealPath If nonnull, extra work is done to determine the real path +/// of the opened file, and that path is stored in this +/// location. +/// @returns a platform-specific file descriptor if \a Name has been opened, +/// otherwise an error object. +Expected<file_t> +openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None, + SmallVectorImpl<char> *RealPath = nullptr); + +/// Try to locks the file during the specified time. +/// +/// This function implements advisory locking on entire file. If it returns +/// <em>errc::success</em>, the file is locked by the calling process. Until the +/// process unlocks the file by calling \a unlockFile, all attempts to lock the +/// same file will fail/block. The process that locked the file may assume that +/// none of other processes read or write this file, provided that all processes +/// lock the file prior to accessing its content. +/// +/// @param FD The descriptor representing the file to lock. +/// @param Timeout Time in milliseconds that the process should wait before +/// reporting lock failure. Zero value means try to get lock only +/// once. +/// @returns errc::success if lock is successfully obtained, +/// errc::no_lock_available if the file cannot be locked, or platform-specific +/// error_code otherwise. +/// +/// @note Care should be taken when using this function in a multithreaded +/// context, as it may not prevent other threads in the same process from +/// obtaining a lock on the same file, even if they are using a different file +/// descriptor. +std::error_code +tryLockFile(int FD, + std::chrono::milliseconds Timeout = std::chrono::milliseconds(0)); + +/// Lock the file. +/// +/// This function acts as @ref tryLockFile but it waits infinitely. +std::error_code lockFile(int FD); + +/// Unlock the file. +/// +/// @param FD The descriptor representing the file to unlock. +/// @returns errc::success if lock is successfully released or platform-specific +/// error_code otherwise. +std::error_code unlockFile(int FD); + +/// @brief Close the file object. This should be used instead of ::close for +/// portability. On error, the caller should assume the file is closed, as is +/// the case for Process::SafelyCloseFileDescriptor +/// +/// @param F On input, this is the file to close. On output, the file is +/// set to kInvalidFile. +/// +/// @returns An error code if closing the file failed. Typically, an error here +/// means that the filesystem may have failed to perform some buffered writes. +std::error_code closeFile(file_t &F); + +#ifdef LLVM_ON_UNIX +/// @brief Change ownership of a file. +/// +/// @param Owner The owner of the file to change to. +/// @param Group The group of the file to change to. +/// @returns errc::success if successfully updated file ownership, otherwise an +/// error code is returned. +std::error_code changeFileOwnership(int FD, uint32_t Owner, uint32_t Group); +#endif + +/// RAII class that facilitates file locking. +class FileLocker { + int FD; ///< Locked file handle. + FileLocker(int FD) : FD(FD) {} + friend class llvm::raw_fd_ostream; + +public: + FileLocker(const FileLocker &L) = delete; + FileLocker(FileLocker &&L) : FD(L.FD) { L.FD = -1; } + ~FileLocker() { + if (FD != -1) + unlockFile(FD); + } + FileLocker &operator=(FileLocker &&L) { + FD = L.FD; + L.FD = -1; + return *this; + } + FileLocker &operator=(const FileLocker &L) = delete; + std::error_code unlock() { + if (FD != -1) { + std::error_code Result = unlockFile(FD); + FD = -1; + return Result; + } + return std::error_code(); + } +}; + +std::error_code getUniqueID(const Twine Path, UniqueID &Result); + +/// Get disk space usage information. +/// +/// Note: Users must be careful about "Time Of Check, Time Of Use" kind of bug. +/// Note: Windows reports results according to the quota allocated to the user. +/// +/// @param Path Input path. +/// @returns a space_info structure filled with the capacity, free, and +/// available space on the device \a Path is on. A platform specific error_code +/// is returned on error. +ErrorOr<space_info> disk_space(const Twine &Path); + +/// This class represents a memory mapped file. It is based on +/// boost::iostreams::mapped_file. +class mapped_file_region { +public: + enum mapmode { + readonly, ///< May only access map via const_data as read only. + readwrite, ///< May access map via data and modify it. Written to path. + priv ///< May modify via data, but changes are lost on destruction. + }; + +private: + /// Platform-specific mapping state. + size_t Size = 0; + void *Mapping = nullptr; +#ifdef _WIN32 + sys::fs::file_t FileHandle = nullptr; +#endif + mapmode Mode = readonly; + + void copyFrom(const mapped_file_region &Copied) { + Size = Copied.Size; + Mapping = Copied.Mapping; +#ifdef _WIN32 + FileHandle = Copied.FileHandle; +#endif + Mode = Copied.Mode; + } + + void moveFromImpl(mapped_file_region &Moved) { + copyFrom(Moved); + Moved.copyFrom(mapped_file_region()); + } + + void unmapImpl(); + void dontNeedImpl(); + + std::error_code init(sys::fs::file_t FD, uint64_t Offset, mapmode Mode); + +public: + mapped_file_region() = default; + mapped_file_region(mapped_file_region &&Moved) { moveFromImpl(Moved); } + mapped_file_region &operator=(mapped_file_region &&Moved) { + unmap(); + moveFromImpl(Moved); + return *this; + } + + mapped_file_region(const mapped_file_region &) = delete; + mapped_file_region &operator=(const mapped_file_region &) = delete; + + /// \param fd An open file descriptor to map. Does not take ownership of fd. + mapped_file_region(sys::fs::file_t fd, mapmode mode, size_t length, uint64_t offset, + std::error_code &ec); + + ~mapped_file_region() { unmapImpl(); } + + /// Check if this is a valid mapping. + explicit operator bool() const { return Mapping; } + + /// Unmap. + void unmap() { + unmapImpl(); + copyFrom(mapped_file_region()); + } + void dontNeed() { dontNeedImpl(); } + + size_t size() const; + char *data() const; + + /// Get a const view of the data. Modifying this memory has undefined + /// behavior. + const char *const_data() const; + + /// \returns The minimum alignment offset must be. + static int alignment(); +}; + +/// Return the path to the main executable, given the value of argv[0] from +/// program startup and the address of main itself. In extremis, this function +/// may fail and return an empty path. +std::string getMainExecutable(const char *argv0, void *MainExecAddr); + +/// @} +/// @name Iterators +/// @{ + +/// directory_entry - A single entry in a directory. +class directory_entry { + // FIXME: different platforms make different information available "for free" + // when traversing a directory. The design of this class wraps most of the + // information in basic_file_status, so on platforms where we can't populate + // that whole structure, callers end up paying for a stat(). + // std::filesystem::directory_entry may be a better model. + std::string Path; + file_type Type = file_type::type_unknown; // Most platforms can provide this. + bool FollowSymlinks = true; // Affects the behavior of status(). + basic_file_status Status; // If available. + +public: + explicit directory_entry(const Twine &Path, bool FollowSymlinks = true, + file_type Type = file_type::type_unknown, + basic_file_status Status = basic_file_status()) + : Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks), + Status(Status) {} + + directory_entry() = default; + + void replace_filename(const Twine &Filename, file_type Type, + basic_file_status Status = basic_file_status()); + + const std::string &path() const { return Path; } + // Get basic information about entry file (a subset of fs::status()). + // On most platforms this is a stat() call. + // On windows the information was already retrieved from the directory. + ErrorOr<basic_file_status> status() const; + // Get the type of this file. + // On most platforms (Linux/Mac/Windows/BSD), this was already retrieved. + // On some platforms (e.g. Solaris) this is a stat() call. + file_type type() const { + if (Type != file_type::type_unknown) + return Type; + auto S = status(); + return S ? S->type() : file_type::type_unknown; + } + + bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; } + bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); } + bool operator< (const directory_entry& RHS) const; + bool operator<=(const directory_entry& RHS) const; + bool operator> (const directory_entry& RHS) const; + bool operator>=(const directory_entry& RHS) const; +}; + +namespace detail { + + struct DirIterState; + + std::error_code directory_iterator_construct(DirIterState &, StringRef, bool); + std::error_code directory_iterator_increment(DirIterState &); + std::error_code directory_iterator_destruct(DirIterState &); + + /// Keeps state for the directory_iterator. + struct DirIterState { + ~DirIterState() { + directory_iterator_destruct(*this); + } + + intptr_t IterationHandle = 0; + directory_entry CurrentEntry; + }; + +} // end namespace detail + +/// directory_iterator - Iterates through the entries in path. There is no +/// operator++ because we need an error_code. If it's really needed we can make +/// it call report_fatal_error on error. +class directory_iterator { + std::shared_ptr<detail::DirIterState> State; + bool FollowSymlinks = true; + +public: + explicit directory_iterator(const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { + State = std::make_shared<detail::DirIterState>(); + SmallString<128> path_storage; + ec = detail::directory_iterator_construct( + *State, path.toStringRef(path_storage), FollowSymlinks); + } + + explicit directory_iterator(const directory_entry &de, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { + State = std::make_shared<detail::DirIterState>(); + ec = detail::directory_iterator_construct( + *State, de.path(), FollowSymlinks); + } + + /// Construct end iterator. + directory_iterator() = default; + + // No operator++ because we need error_code. + directory_iterator &increment(std::error_code &ec) { + ec = directory_iterator_increment(*State); + return *this; + } + + const directory_entry &operator*() const { return State->CurrentEntry; } + const directory_entry *operator->() const { return &State->CurrentEntry; } + + bool operator==(const directory_iterator &RHS) const { + if (State == RHS.State) + return true; + if (!RHS.State) + return State->CurrentEntry == directory_entry(); + if (!State) + return RHS.State->CurrentEntry == directory_entry(); + return State->CurrentEntry == RHS.State->CurrentEntry; + } + + bool operator!=(const directory_iterator &RHS) const { + return !(*this == RHS); + } +}; + +namespace detail { + + /// Keeps state for the recursive_directory_iterator. + struct RecDirIterState { + std::stack<directory_iterator, std::vector<directory_iterator>> Stack; + uint16_t Level = 0; + bool HasNoPushRequest = false; + }; + +} // end namespace detail + +/// recursive_directory_iterator - Same as directory_iterator except for it +/// recurses down into child directories. +class recursive_directory_iterator { + std::shared_ptr<detail::RecDirIterState> State; + bool Follow; + +public: + recursive_directory_iterator() = default; + explicit recursive_directory_iterator(const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : State(std::make_shared<detail::RecDirIterState>()), + Follow(follow_symlinks) { + State->Stack.push(directory_iterator(path, ec, Follow)); + if (State->Stack.top() == directory_iterator()) + State.reset(); + } + + // No operator++ because we need error_code. + recursive_directory_iterator &increment(std::error_code &ec) { + const directory_iterator end_itr = {}; + + if (State->HasNoPushRequest) + State->HasNoPushRequest = false; + else { + file_type type = State->Stack.top()->type(); + if (type == file_type::symlink_file && Follow) { + // Resolve the symlink: is it a directory to recurse into? + ErrorOr<basic_file_status> status = State->Stack.top()->status(); + if (status) + type = status->type(); + // Otherwise broken symlink, and we'll continue. + } + if (type == file_type::directory_file) { + State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow)); + if (State->Stack.top() != end_itr) { + ++State->Level; + return *this; + } + State->Stack.pop(); + } + } + + while (!State->Stack.empty() + && State->Stack.top().increment(ec) == end_itr) { + State->Stack.pop(); + --State->Level; + } + + // Check if we are done. If so, create an end iterator. + if (State->Stack.empty()) + State.reset(); + + return *this; + } + + const directory_entry &operator*() const { return *State->Stack.top(); } + const directory_entry *operator->() const { return &*State->Stack.top(); } + + // observers + /// Gets the current level. Starting path is at level 0. + int level() const { return State->Level; } + + /// Returns true if no_push has been called for this directory_entry. + bool no_push_request() const { return State->HasNoPushRequest; } + + // modifiers + /// Goes up one level if Level > 0. + void pop() { + assert(State && "Cannot pop an end iterator!"); + assert(State->Level > 0 && "Cannot pop an iterator with level < 1"); + + const directory_iterator end_itr = {}; + std::error_code ec; + do { + if (ec) + report_fatal_error("Error incrementing directory iterator."); + State->Stack.pop(); + --State->Level; + } while (!State->Stack.empty() + && State->Stack.top().increment(ec) == end_itr); + + // Check if we are done. If so, create an end iterator. + if (State->Stack.empty()) + State.reset(); + } + + /// Does not go down into the current directory_entry. + void no_push() { State->HasNoPushRequest = true; } + + bool operator==(const recursive_directory_iterator &RHS) const { + return State == RHS.State; + } + + bool operator!=(const recursive_directory_iterator &RHS) const { + return !(*this == RHS); + } +}; + +/// @} + +} // end namespace fs +} // end namespace sys +} // end namespace llvm + +#endif // LLVM_SUPPORT_FILESYSTEM_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FileSystem/UniqueID.h b/contrib/libs/llvm16/include/llvm/Support/FileSystem/UniqueID.h new file mode 100644 index 00000000000..ef2458284e4 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FileSystem/UniqueID.h @@ -0,0 +1,90 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/FileSystem/UniqueID.h - UniqueID for files --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is cut out of llvm/Support/FileSystem.h to allow UniqueID to be +// reused without bloating the includes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FILESYSTEM_UNIQUEID_H +#define LLVM_SUPPORT_FILESYSTEM_UNIQUEID_H + +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" +#include <cstdint> +#include <utility> + +namespace llvm { +namespace sys { +namespace fs { + +class UniqueID { + uint64_t Device; + uint64_t File; + +public: + UniqueID() = default; + UniqueID(uint64_t Device, uint64_t File) : Device(Device), File(File) {} + + bool operator==(const UniqueID &Other) const { + return Device == Other.Device && File == Other.File; + } + bool operator!=(const UniqueID &Other) const { return !(*this == Other); } + bool operator<(const UniqueID &Other) const { + /// Don't use std::tie since it bloats the compile time of this header. + if (Device < Other.Device) + return true; + if (Other.Device < Device) + return false; + return File < Other.File; + } + + uint64_t getDevice() const { return Device; } + uint64_t getFile() const { return File; } +}; + +} // end namespace fs +} // end namespace sys + +// Support UniqueIDs as DenseMap keys. +template <> struct DenseMapInfo<llvm::sys::fs::UniqueID> { + static inline llvm::sys::fs::UniqueID getEmptyKey() { + auto EmptyKey = DenseMapInfo<std::pair<uint64_t, uint64_t>>::getEmptyKey(); + return {EmptyKey.first, EmptyKey.second}; + } + + static inline llvm::sys::fs::UniqueID getTombstoneKey() { + auto TombstoneKey = + DenseMapInfo<std::pair<uint64_t, uint64_t>>::getTombstoneKey(); + return {TombstoneKey.first, TombstoneKey.second}; + } + + static hash_code getHashValue(const llvm::sys::fs::UniqueID &Tag) { + return hash_value(std::make_pair(Tag.getDevice(), Tag.getFile())); + } + + static bool isEqual(const llvm::sys::fs::UniqueID &LHS, + const llvm::sys::fs::UniqueID &RHS) { + return LHS == RHS; + } +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_FILESYSTEM_UNIQUEID_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FileUtilities.h b/contrib/libs/llvm16/include/llvm/Support/FileUtilities.h new file mode 100644 index 00000000000..38efd554cd8 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FileUtilities.h @@ -0,0 +1,148 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/FileUtilities.h - File System Utilities -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a family of utility functions which are useful for doing +// various things with files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FILEUTILITIES_H +#define LLVM_SUPPORT_FILEUTILITIES_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" + +#include <system_error> + +namespace llvm { + + /// DiffFilesWithTolerance - Compare the two files specified, returning 0 if + /// the files match, 1 if they are different, and 2 if there is a file error. + /// This function allows you to specify an absolute and relative FP error that + /// is allowed to exist. If you specify a string to fill in for the error + /// option, it will set the string to an error message if an error occurs, or + /// if the files are different. + /// + int DiffFilesWithTolerance(StringRef FileA, + StringRef FileB, + double AbsTol, double RelTol, + std::string *Error = nullptr); + + + /// FileRemover - This class is a simple object meant to be stack allocated. + /// If an exception is thrown from a region, the object removes the filename + /// specified (if deleteIt is true). + /// + class FileRemover { + SmallString<128> Filename; + bool DeleteIt; + public: + FileRemover() : DeleteIt(false) {} + + explicit FileRemover(const Twine& filename, bool deleteIt = true) + : DeleteIt(deleteIt) { + filename.toVector(Filename); + } + + ~FileRemover() { + if (DeleteIt) { + // Ignore problems deleting the file. + sys::fs::remove(Filename); + } + } + + /// setFile - Give ownership of the file to the FileRemover so it will + /// be removed when the object is destroyed. If the FileRemover already + /// had ownership of a file, remove it first. + void setFile(const Twine& filename, bool deleteIt = true) { + if (DeleteIt) { + // Ignore problems deleting the file. + sys::fs::remove(Filename); + } + + Filename.clear(); + filename.toVector(Filename); + DeleteIt = deleteIt; + } + + /// releaseFile - Take ownership of the file away from the FileRemover so it + /// will not be removed when the object is destroyed. + void releaseFile() { DeleteIt = false; } + }; + + enum class atomic_write_error { + failed_to_create_uniq_file = 0, + output_stream_error, + failed_to_rename_temp_file + }; + + class AtomicFileWriteError : public llvm::ErrorInfo<AtomicFileWriteError> { + public: + AtomicFileWriteError(atomic_write_error Error) : Error(Error) {} + + void log(raw_ostream &OS) const override; + + const atomic_write_error Error; + static char ID; + + private: + // Users are not expected to use error_code. + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + }; + + // atomic_write_error + whatever the Writer can return + + /// Creates a unique file with name according to the given \p TempPathModel, + /// writes content of \p Buffer to the file and renames it to \p FinalPath. + /// + /// \returns \c AtomicFileWriteError in case of error. + llvm::Error writeFileAtomically(StringRef TempPathModel, StringRef FinalPath, + StringRef Buffer); + + llvm::Error + writeFileAtomically(StringRef TempPathModel, StringRef FinalPath, + std::function<llvm::Error(llvm::raw_ostream &)> Writer); + + /// FilePermssionsApplier helps to copy permissions from an input file to + /// an output one. It memorizes the status of the input file and can apply + /// permissions and dates to the output file. + class FilePermissionsApplier { + public: + static Expected<FilePermissionsApplier> create(StringRef InputFilename); + + /// Apply stored permissions to the \p OutputFilename. + /// Copy LastAccess and ModificationTime if \p CopyDates is true. + /// Overwrite stored permissions if \p OverwritePermissions is specified. + Error + apply(StringRef OutputFilename, bool CopyDates = false, + std::optional<sys::fs::perms> OverwritePermissions = std::nullopt); + + private: + FilePermissionsApplier(StringRef InputFilename, sys::fs::file_status Status) + : InputFilename(InputFilename), InputStatus(Status) {} + + StringRef InputFilename; + sys::fs::file_status InputStatus; + }; +} // End llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Format.h b/contrib/libs/llvm16/include/llvm/Support/Format.h new file mode 100644 index 00000000000..53ce73f478e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Format.h @@ -0,0 +1,269 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- Format.h - Efficient printf-style formatting for streams -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the format() function, which can be used with other +// LLVM subsystems to provide printf-style formatting. This gives all the power +// and risk of printf. This can be used like this (with raw_ostreams as an +// example): +// +// OS << "mynumber: " << format("%4.5f", 1234.412) << '\n'; +// +// Or if you prefer: +// +// OS << format("mynumber: %4.5f\n", 1234.412); +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMAT_H +#define LLVM_SUPPORT_FORMAT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include <cassert> +#include <cstdio> +#include <tuple> +#include <utility> + +namespace llvm { + +/// This is a helper class used for handling formatted output. It is the +/// abstract base class of a templated derived class. +class format_object_base { +protected: + const char *Fmt; + ~format_object_base() = default; // Disallow polymorphic deletion. + format_object_base(const format_object_base &) = default; + virtual void home(); // Out of line virtual method. + + /// Call snprintf() for this object, on the given buffer and size. + virtual int snprint(char *Buffer, unsigned BufferSize) const = 0; + +public: + format_object_base(const char *fmt) : Fmt(fmt) {} + + /// Format the object into the specified buffer. On success, this returns + /// the length of the formatted string. If the buffer is too small, this + /// returns a length to retry with, which will be larger than BufferSize. + unsigned print(char *Buffer, unsigned BufferSize) const { + assert(BufferSize && "Invalid buffer size!"); + + // Print the string, leaving room for the terminating null. + int N = snprint(Buffer, BufferSize); + + // VC++ and old GlibC return negative on overflow, just double the size. + if (N < 0) + return BufferSize * 2; + + // Other implementations yield number of bytes needed, not including the + // final '\0'. + if (unsigned(N) >= BufferSize) + return N + 1; + + // Otherwise N is the length of output (not including the final '\0'). + return N; + } +}; + +/// These are templated helper classes used by the format function that +/// capture the object to be formatted and the format string. When actually +/// printed, this synthesizes the string into a temporary buffer provided and +/// returns whether or not it is big enough. + +// Helper to validate that format() parameters are scalars or pointers. +template <typename... Args> struct validate_format_parameters; +template <typename Arg, typename... Args> +struct validate_format_parameters<Arg, Args...> { + static_assert(std::is_scalar_v<Arg>, + "format can't be used with non fundamental / non pointer type"); + validate_format_parameters() { validate_format_parameters<Args...>(); } +}; +template <> struct validate_format_parameters<> {}; + +template <typename... Ts> +class format_object final : public format_object_base { + std::tuple<Ts...> Vals; + + template <std::size_t... Is> + int snprint_tuple(char *Buffer, unsigned BufferSize, + std::index_sequence<Is...>) const { +#ifdef _MSC_VER + return _snprintf(Buffer, BufferSize, Fmt, std::get<Is>(Vals)...); +#else + return snprintf(Buffer, BufferSize, Fmt, std::get<Is>(Vals)...); +#endif + } + +public: + format_object(const char *fmt, const Ts &... vals) + : format_object_base(fmt), Vals(vals...) { + validate_format_parameters<Ts...>(); + } + + int snprint(char *Buffer, unsigned BufferSize) const override { + return snprint_tuple(Buffer, BufferSize, std::index_sequence_for<Ts...>()); + } +}; + +/// These are helper functions used to produce formatted output. They use +/// template type deduction to construct the appropriate instance of the +/// format_object class to simplify their construction. +/// +/// This is typically used like: +/// \code +/// OS << format("%0.4f", myfloat) << '\n'; +/// \endcode + +template <typename... Ts> +inline format_object<Ts...> format(const char *Fmt, const Ts &... Vals) { + return format_object<Ts...>(Fmt, Vals...); +} + +/// This is a helper class for left_justify, right_justify, and center_justify. +class FormattedString { +public: + enum Justification { JustifyNone, JustifyLeft, JustifyRight, JustifyCenter }; + FormattedString(StringRef S, unsigned W, Justification J) + : Str(S), Width(W), Justify(J) {} + +private: + StringRef Str; + unsigned Width; + Justification Justify; + friend class raw_ostream; +}; + +/// left_justify - append spaces after string so total output is +/// \p Width characters. If \p Str is larger that \p Width, full string +/// is written with no padding. +inline FormattedString left_justify(StringRef Str, unsigned Width) { + return FormattedString(Str, Width, FormattedString::JustifyLeft); +} + +/// right_justify - add spaces before string so total output is +/// \p Width characters. If \p Str is larger that \p Width, full string +/// is written with no padding. +inline FormattedString right_justify(StringRef Str, unsigned Width) { + return FormattedString(Str, Width, FormattedString::JustifyRight); +} + +/// center_justify - add spaces before and after string so total output is +/// \p Width characters. If \p Str is larger that \p Width, full string +/// is written with no padding. +inline FormattedString center_justify(StringRef Str, unsigned Width) { + return FormattedString(Str, Width, FormattedString::JustifyCenter); +} + +/// This is a helper class used for format_hex() and format_decimal(). +class FormattedNumber { + uint64_t HexValue; + int64_t DecValue; + unsigned Width; + bool Hex; + bool Upper; + bool HexPrefix; + friend class raw_ostream; + +public: + FormattedNumber(uint64_t HV, int64_t DV, unsigned W, bool H, bool U, + bool Prefix) + : HexValue(HV), DecValue(DV), Width(W), Hex(H), Upper(U), + HexPrefix(Prefix) {} +}; + +/// format_hex - Output \p N as a fixed width hexadecimal. If number will not +/// fit in width, full number is still printed. Examples: +/// OS << format_hex(255, 4) => 0xff +/// OS << format_hex(255, 4, true) => 0xFF +/// OS << format_hex(255, 6) => 0x00ff +/// OS << format_hex(255, 2) => 0xff +inline FormattedNumber format_hex(uint64_t N, unsigned Width, + bool Upper = false) { + assert(Width <= 18 && "hex width must be <= 18"); + return FormattedNumber(N, 0, Width, true, Upper, true); +} + +/// format_hex_no_prefix - Output \p N as a fixed width hexadecimal. Does not +/// prepend '0x' to the outputted string. If number will not fit in width, +/// full number is still printed. Examples: +/// OS << format_hex_no_prefix(255, 2) => ff +/// OS << format_hex_no_prefix(255, 2, true) => FF +/// OS << format_hex_no_prefix(255, 4) => 00ff +/// OS << format_hex_no_prefix(255, 1) => ff +inline FormattedNumber format_hex_no_prefix(uint64_t N, unsigned Width, + bool Upper = false) { + assert(Width <= 16 && "hex width must be <= 16"); + return FormattedNumber(N, 0, Width, true, Upper, false); +} + +/// format_decimal - Output \p N as a right justified, fixed-width decimal. If +/// number will not fit in width, full number is still printed. Examples: +/// OS << format_decimal(0, 5) => " 0" +/// OS << format_decimal(255, 5) => " 255" +/// OS << format_decimal(-1, 3) => " -1" +/// OS << format_decimal(12345, 3) => "12345" +inline FormattedNumber format_decimal(int64_t N, unsigned Width) { + return FormattedNumber(0, N, Width, false, false, false); +} + +class FormattedBytes { + ArrayRef<uint8_t> Bytes; + + // If not None, display offsets for each line relative to starting value. + std::optional<uint64_t> FirstByteOffset; + uint32_t IndentLevel; // Number of characters to indent each line. + uint32_t NumPerLine; // Number of bytes to show per line. + uint8_t ByteGroupSize; // How many hex bytes are grouped without spaces + bool Upper; // Show offset and hex bytes as upper case. + bool ASCII; // Show the ASCII bytes for the hex bytes to the right. + friend class raw_ostream; + +public: + FormattedBytes(ArrayRef<uint8_t> B, uint32_t IL, std::optional<uint64_t> O, + uint32_t NPL, uint8_t BGS, bool U, bool A) + : Bytes(B), FirstByteOffset(O), IndentLevel(IL), NumPerLine(NPL), + ByteGroupSize(BGS), Upper(U), ASCII(A) { + + if (ByteGroupSize > NumPerLine) + ByteGroupSize = NumPerLine; + } +}; + +inline FormattedBytes +format_bytes(ArrayRef<uint8_t> Bytes, + std::optional<uint64_t> FirstByteOffset = std::nullopt, + uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4, + uint32_t IndentLevel = 0, bool Upper = false) { + return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine, + ByteGroupSize, Upper, false); +} + +inline FormattedBytes +format_bytes_with_ascii(ArrayRef<uint8_t> Bytes, + std::optional<uint64_t> FirstByteOffset = std::nullopt, + uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4, + uint32_t IndentLevel = 0, bool Upper = false) { + return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine, + ByteGroupSize, Upper, true); +} + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FormatAdapters.h b/contrib/libs/llvm16/include/llvm/Support/FormatAdapters.h new file mode 100644 index 00000000000..df8d719a451 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FormatAdapters.h @@ -0,0 +1,120 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- FormatAdapters.h - Formatters for common LLVM types -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATADAPTERS_H +#define LLVM_SUPPORT_FORMATADAPTERS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatCommon.h" +#include "llvm/Support/FormatVariadicDetails.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +template <typename T> class FormatAdapter : public detail::format_adapter { +protected: + explicit FormatAdapter(T &&Item) : Item(std::forward<T>(Item)) {} + + T Item; +}; + +namespace detail { +template <typename T> class AlignAdapter final : public FormatAdapter<T> { + AlignStyle Where; + size_t Amount; + char Fill; + +public: + AlignAdapter(T &&Item, AlignStyle Where, size_t Amount, char Fill) + : FormatAdapter<T>(std::forward<T>(Item)), Where(Where), Amount(Amount), + Fill(Fill) {} + + void format(llvm::raw_ostream &Stream, StringRef Style) override { + auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item)); + FmtAlign(Adapter, Where, Amount, Fill).format(Stream, Style); + } +}; + +template <typename T> class PadAdapter final : public FormatAdapter<T> { + size_t Left; + size_t Right; + +public: + PadAdapter(T &&Item, size_t Left, size_t Right) + : FormatAdapter<T>(std::forward<T>(Item)), Left(Left), Right(Right) {} + + void format(llvm::raw_ostream &Stream, StringRef Style) override { + auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item)); + Stream.indent(Left); + Adapter.format(Stream, Style); + Stream.indent(Right); + } +}; + +template <typename T> class RepeatAdapter final : public FormatAdapter<T> { + size_t Count; + +public: + RepeatAdapter(T &&Item, size_t Count) + : FormatAdapter<T>(std::forward<T>(Item)), Count(Count) {} + + void format(llvm::raw_ostream &Stream, StringRef Style) override { + auto Adapter = detail::build_format_adapter(std::forward<T>(this->Item)); + for (size_t I = 0; I < Count; ++I) { + Adapter.format(Stream, Style); + } + } +}; + +class ErrorAdapter : public FormatAdapter<Error> { +public: + ErrorAdapter(Error &&Item) : FormatAdapter(std::move(Item)) {} + ErrorAdapter(ErrorAdapter &&) = default; + ~ErrorAdapter() { consumeError(std::move(Item)); } + void format(llvm::raw_ostream &Stream, StringRef Style) override { + Stream << Item; + } +}; +} + +template <typename T> +detail::AlignAdapter<T> fmt_align(T &&Item, AlignStyle Where, size_t Amount, + char Fill = ' ') { + return detail::AlignAdapter<T>(std::forward<T>(Item), Where, Amount, Fill); +} + +template <typename T> +detail::PadAdapter<T> fmt_pad(T &&Item, size_t Left, size_t Right) { + return detail::PadAdapter<T>(std::forward<T>(Item), Left, Right); +} + +template <typename T> +detail::RepeatAdapter<T> fmt_repeat(T &&Item, size_t Count) { + return detail::RepeatAdapter<T>(std::forward<T>(Item), Count); +} + +// llvm::Error values must be consumed before being destroyed. +// Wrapping an error in fmt_consume explicitly indicates that the formatv_object +// should take ownership and consume it. +inline detail::ErrorAdapter fmt_consume(Error &&Item) { + return detail::ErrorAdapter(std::move(Item)); +} +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FormatCommon.h b/contrib/libs/llvm16/include/llvm/Support/FormatCommon.h new file mode 100644 index 00000000000..705905ed6de --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FormatCommon.h @@ -0,0 +1,87 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- FormatCommon.h - Formatters for common LLVM types --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATCOMMON_H +#define LLVM_SUPPORT_FORMATCOMMON_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FormatVariadicDetails.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +enum class AlignStyle { Left, Center, Right }; + +struct FmtAlign { + detail::format_adapter &Adapter; + AlignStyle Where; + size_t Amount; + char Fill; + + FmtAlign(detail::format_adapter &Adapter, AlignStyle Where, size_t Amount, + char Fill = ' ') + : Adapter(Adapter), Where(Where), Amount(Amount), Fill(Fill) {} + + void format(raw_ostream &S, StringRef Options) { + // If we don't need to align, we can format straight into the underlying + // stream. Otherwise we have to go through an intermediate stream first + // in order to calculate how long the output is so we can align it. + // TODO: Make the format method return the number of bytes written, that + // way we can also skip the intermediate stream for left-aligned output. + if (Amount == 0) { + Adapter.format(S, Options); + return; + } + SmallString<64> Item; + raw_svector_ostream Stream(Item); + + Adapter.format(Stream, Options); + if (Amount <= Item.size()) { + S << Item; + return; + } + + size_t PadAmount = Amount - Item.size(); + switch (Where) { + case AlignStyle::Left: + S << Item; + fill(S, PadAmount); + break; + case AlignStyle::Center: { + size_t X = PadAmount / 2; + fill(S, X); + S << Item; + fill(S, PadAmount - X); + break; + } + default: + fill(S, PadAmount); + S << Item; + break; + } + } + +private: + void fill(llvm::raw_ostream &S, uint32_t Count) { + for (uint32_t I = 0; I < Count; ++I) + S << Fill; + } +}; +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FormatProviders.h b/contrib/libs/llvm16/include/llvm/Support/FormatProviders.h new file mode 100644 index 00000000000..f50f355bb6e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FormatProviders.h @@ -0,0 +1,430 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- FormatProviders.h - Formatters for common LLVM types -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements format providers for many common LLVM types, for example +// allowing precision and width specifiers for scalar and string types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATPROVIDERS_H +#define LLVM_SUPPORT_FORMATPROVIDERS_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FormatVariadicDetails.h" +#include "llvm/Support/NativeFormatting.h" + +#include <array> +#include <optional> +#include <type_traits> + +namespace llvm { +namespace detail { +template <typename T> +struct use_integral_formatter + : public std::integral_constant< + bool, is_one_of<T, uint8_t, int16_t, uint16_t, int32_t, uint32_t, + int64_t, uint64_t, int, unsigned, long, unsigned long, + long long, unsigned long long>::value> {}; + +template <typename T> +struct use_char_formatter + : public std::integral_constant<bool, std::is_same<T, char>::value> {}; + +template <typename T> +struct is_cstring + : public std::integral_constant<bool, + is_one_of<T, char *, const char *>::value> { +}; + +template <typename T> +struct use_string_formatter + : public std::integral_constant<bool, + std::is_convertible<T, llvm::StringRef>::value> {}; + +template <typename T> +struct use_pointer_formatter + : public std::integral_constant<bool, std::is_pointer<T>::value && + !is_cstring<T>::value> {}; + +template <typename T> +struct use_double_formatter + : public std::integral_constant<bool, std::is_floating_point<T>::value> {}; + +class HelperFunctions { +protected: + static std::optional<size_t> parseNumericPrecision(StringRef Str) { + size_t Prec; + std::optional<size_t> Result; + if (Str.empty()) + Result = std::nullopt; + else if (Str.getAsInteger(10, Prec)) { + assert(false && "Invalid precision specifier"); + Result = std::nullopt; + } else { + assert(Prec < 100 && "Precision out of range"); + Result = std::min<size_t>(99u, Prec); + } + return Result; + } + + static bool consumeHexStyle(StringRef &Str, HexPrintStyle &Style) { + if (!Str.startswith_insensitive("x")) + return false; + + if (Str.consume_front("x-")) + Style = HexPrintStyle::Lower; + else if (Str.consume_front("X-")) + Style = HexPrintStyle::Upper; + else if (Str.consume_front("x+") || Str.consume_front("x")) + Style = HexPrintStyle::PrefixLower; + else if (Str.consume_front("X+") || Str.consume_front("X")) + Style = HexPrintStyle::PrefixUpper; + return true; + } + + static size_t consumeNumHexDigits(StringRef &Str, HexPrintStyle Style, + size_t Default) { + Str.consumeInteger(10, Default); + if (isPrefixedHexStyle(Style)) + Default += 2; + return Default; + } +}; +} + +/// Implementation of format_provider<T> for integral arithmetic types. +/// +/// The options string of an integral type has the grammar: +/// +/// integer_options :: [style][digits] +/// style :: <see table below> +/// digits :: <non-negative integer> 0-99 +/// +/// ========================================================================== +/// | style | Meaning | Example | Digits Meaning | +/// -------------------------------------------------------------------------- +/// | | | Input | Output | | +/// ========================================================================== +/// | x- | Hex no prefix, lower | 42 | 2a | Minimum # digits | +/// | X- | Hex no prefix, upper | 42 | 2A | Minimum # digits | +/// | x+ / x | Hex + prefix, lower | 42 | 0x2a | Minimum # digits | +/// | X+ / X | Hex + prefix, upper | 42 | 0x2A | Minimum # digits | +/// | N / n | Digit grouped number | 123456 | 123,456 | Ignored | +/// | D / d | Integer | 100000 | 100000 | Ignored | +/// | (empty) | Same as D / d | | | | +/// ========================================================================== +/// + +template <typename T> +struct format_provider< + T, std::enable_if_t<detail::use_integral_formatter<T>::value>> + : public detail::HelperFunctions { +private: +public: + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + HexPrintStyle HS; + size_t Digits = 0; + if (consumeHexStyle(Style, HS)) { + Digits = consumeNumHexDigits(Style, HS, 0); + write_hex(Stream, V, HS, Digits); + return; + } + + IntegerStyle IS = IntegerStyle::Integer; + if (Style.consume_front("N") || Style.consume_front("n")) + IS = IntegerStyle::Number; + else if (Style.consume_front("D") || Style.consume_front("d")) + IS = IntegerStyle::Integer; + + Style.consumeInteger(10, Digits); + assert(Style.empty() && "Invalid integral format style!"); + write_integer(Stream, V, Digits, IS); + } +}; + +/// Implementation of format_provider<T> for integral pointer types. +/// +/// The options string of a pointer type has the grammar: +/// +/// pointer_options :: [style][precision] +/// style :: <see table below> +/// digits :: <non-negative integer> 0-sizeof(void*) +/// +/// ========================================================================== +/// | S | Meaning | Example | +/// -------------------------------------------------------------------------- +/// | | | Input | Output | +/// ========================================================================== +/// | x- | Hex no prefix, lower | 0xDEADBEEF | deadbeef | +/// | X- | Hex no prefix, upper | 0xDEADBEEF | DEADBEEF | +/// | x+ / x | Hex + prefix, lower | 0xDEADBEEF | 0xdeadbeef | +/// | X+ / X | Hex + prefix, upper | 0xDEADBEEF | 0xDEADBEEF | +/// | (empty) | Same as X+ / X | | | +/// ========================================================================== +/// +/// The default precision is the number of nibbles in a machine word, and in all +/// cases indicates the minimum number of nibbles to print. +template <typename T> +struct format_provider< + T, std::enable_if_t<detail::use_pointer_formatter<T>::value>> + : public detail::HelperFunctions { +private: +public: + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + HexPrintStyle HS = HexPrintStyle::PrefixUpper; + consumeHexStyle(Style, HS); + size_t Digits = consumeNumHexDigits(Style, HS, sizeof(void *) * 2); + write_hex(Stream, reinterpret_cast<std::uintptr_t>(V), HS, Digits); + } +}; + +/// Implementation of format_provider<T> for c-style strings and string +/// objects such as std::string and llvm::StringRef. +/// +/// The options string of a string type has the grammar: +/// +/// string_options :: [length] +/// +/// where `length` is an optional integer specifying the maximum number of +/// characters in the string to print. If `length` is omitted, the string is +/// printed up to the null terminator. + +template <typename T> +struct format_provider< + T, std::enable_if_t<detail::use_string_formatter<T>::value>> { + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + size_t N = StringRef::npos; + if (!Style.empty() && Style.getAsInteger(10, N)) { + assert(false && "Style is not a valid integer"); + } + llvm::StringRef S = V; + Stream << S.substr(0, N); + } +}; + +/// Implementation of format_provider<T> for llvm::Twine. +/// +/// This follows the same rules as the string formatter. + +template <> struct format_provider<Twine> { + static void format(const Twine &V, llvm::raw_ostream &Stream, + StringRef Style) { + format_provider<std::string>::format(V.str(), Stream, Style); + } +}; + +/// Implementation of format_provider<T> for characters. +/// +/// The options string of a character type has the grammar: +/// +/// char_options :: (empty) | [integer_options] +/// +/// If `char_options` is empty, the character is displayed as an ASCII +/// character. Otherwise, it is treated as an integer options string. +/// +template <typename T> +struct format_provider<T, + std::enable_if_t<detail::use_char_formatter<T>::value>> { + static void format(const char &V, llvm::raw_ostream &Stream, + StringRef Style) { + if (Style.empty()) + Stream << V; + else { + int X = static_cast<int>(V); + format_provider<int>::format(X, Stream, Style); + } + } +}; + +/// Implementation of format_provider<T> for type `bool` +/// +/// The options string of a boolean type has the grammar: +/// +/// bool_options :: "" | "Y" | "y" | "D" | "d" | "T" | "t" +/// +/// ================================== +/// | C | Meaning | +/// ================================== +/// | Y | YES / NO | +/// | y | yes / no | +/// | D / d | Integer 0 or 1 | +/// | T | TRUE / FALSE | +/// | t | true / false | +/// | (empty) | Equivalent to 't' | +/// ================================== +template <> struct format_provider<bool> { + static void format(const bool &B, llvm::raw_ostream &Stream, + StringRef Style) { + Stream << StringSwitch<const char *>(Style) + .Case("Y", B ? "YES" : "NO") + .Case("y", B ? "yes" : "no") + .CaseLower("D", B ? "1" : "0") + .Case("T", B ? "TRUE" : "FALSE") + .Cases("t", "", B ? "true" : "false") + .Default(B ? "1" : "0"); + } +}; + +/// Implementation of format_provider<T> for floating point types. +/// +/// The options string of a floating point type has the format: +/// +/// float_options :: [style][precision] +/// style :: <see table below> +/// precision :: <non-negative integer> 0-99 +/// +/// ===================================================== +/// | style | Meaning | Example | +/// ----------------------------------------------------- +/// | | | Input | Output | +/// ===================================================== +/// | P / p | Percentage | 0.05 | 5.00% | +/// | F / f | Fixed point | 1.0 | 1.00 | +/// | E | Exponential with E | 100000 | 1.0E+05 | +/// | e | Exponential with e | 100000 | 1.0e+05 | +/// | (empty) | Same as F / f | | | +/// ===================================================== +/// +/// The default precision is 6 for exponential (E / e) and 2 for everything +/// else. + +template <typename T> +struct format_provider<T, + std::enable_if_t<detail::use_double_formatter<T>::value>> + : public detail::HelperFunctions { + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + FloatStyle S; + if (Style.consume_front("P") || Style.consume_front("p")) + S = FloatStyle::Percent; + else if (Style.consume_front("F") || Style.consume_front("f")) + S = FloatStyle::Fixed; + else if (Style.consume_front("E")) + S = FloatStyle::ExponentUpper; + else if (Style.consume_front("e")) + S = FloatStyle::Exponent; + else + S = FloatStyle::Fixed; + + std::optional<size_t> Precision = parseNumericPrecision(Style); + if (!Precision) + Precision = getDefaultPrecision(S); + + write_double(Stream, static_cast<double>(V), S, Precision); + } +}; + +namespace detail { +template <typename IterT> +using IterValue = typename std::iterator_traits<IterT>::value_type; + +template <typename IterT> +struct range_item_has_provider + : public std::integral_constant< + bool, !uses_missing_provider<IterValue<IterT>>::value> {}; +} + +/// Implementation of format_provider<T> for ranges. +/// +/// This will print an arbitrary range as a delimited sequence of items. +/// +/// The options string of a range type has the grammar: +/// +/// range_style ::= [separator] [element_style] +/// separator ::= "$" delimeted_expr +/// element_style ::= "@" delimeted_expr +/// delimeted_expr ::= "[" expr "]" | "(" expr ")" | "<" expr ">" +/// expr ::= <any string not containing delimeter> +/// +/// where the separator expression is the string to insert between consecutive +/// items in the range and the argument expression is the Style specification to +/// be used when formatting the underlying type. The default separator if +/// unspecified is ' ' (space). The syntax of the argument expression follows +/// whatever grammar is dictated by the format provider or format adapter used +/// to format the value type. +/// +/// Note that attempting to format an `iterator_range<T>` where no format +/// provider can be found for T will result in a compile error. +/// + +template <typename IterT> class format_provider<llvm::iterator_range<IterT>> { + using value = typename std::iterator_traits<IterT>::value_type; + + static StringRef consumeOneOption(StringRef &Style, char Indicator, + StringRef Default) { + if (Style.empty()) + return Default; + if (Style.front() != Indicator) + return Default; + Style = Style.drop_front(); + if (Style.empty()) { + assert(false && "Invalid range style"); + return Default; + } + + for (const char *D : std::array<const char *, 3>{"[]", "<>", "()"}) { + if (Style.front() != D[0]) + continue; + size_t End = Style.find_first_of(D[1]); + if (End == StringRef::npos) { + assert(false && "Missing range option end delimeter!"); + return Default; + } + StringRef Result = Style.slice(1, End); + Style = Style.drop_front(End + 1); + return Result; + } + assert(false && "Invalid range style!"); + return Default; + } + + static std::pair<StringRef, StringRef> parseOptions(StringRef Style) { + StringRef Sep = consumeOneOption(Style, '$', ", "); + StringRef Args = consumeOneOption(Style, '@', ""); + assert(Style.empty() && "Unexpected text in range option string!"); + return std::make_pair(Sep, Args); + } + +public: + static_assert(detail::range_item_has_provider<IterT>::value, + "Range value_type does not have a format provider!"); + static void format(const llvm::iterator_range<IterT> &V, + llvm::raw_ostream &Stream, StringRef Style) { + StringRef Sep; + StringRef ArgStyle; + std::tie(Sep, ArgStyle) = parseOptions(Style); + auto Begin = V.begin(); + auto End = V.end(); + if (Begin != End) { + auto Adapter = detail::build_format_adapter(*Begin); + Adapter.format(Stream, ArgStyle); + ++Begin; + } + while (Begin != End) { + Stream << Sep; + auto Adapter = detail::build_format_adapter(*Begin); + Adapter.format(Stream, ArgStyle); + ++Begin; + } + } +}; +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FormatVariadic.h b/contrib/libs/llvm16/include/llvm/Support/FormatVariadic.h new file mode 100644 index 00000000000..2be48f15c1b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FormatVariadic.h @@ -0,0 +1,273 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- FormatVariadic.h - Efficient type-safe string formatting --*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the formatv() function which can be used with other LLVM +// subsystems to provide printf-like formatting, but with improved safety and +// flexibility. The result of `formatv` is an object which can be streamed to +// a raw_ostream or converted to a std::string or llvm::SmallString. +// +// // Convert to std::string. +// std::string S = formatv("{0} {1}", 1234.412, "test").str(); +// +// // Convert to llvm::SmallString +// SmallString<8> S = formatv("{0} {1}", 1234.412, "test").sstr<8>(); +// +// // Stream to an existing raw_ostream. +// OS << formatv("{0} {1}", 1234.412, "test"); +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATVARIADIC_H +#define LLVM_SUPPORT_FORMATVARIADIC_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatCommon.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadicDetails.h" +#include "llvm/Support/raw_ostream.h" +#include <array> +#include <cstddef> +#include <optional> +#include <string> +#include <tuple> +#include <utility> + +namespace llvm { + +enum class ReplacementType { Empty, Format, Literal }; + +struct ReplacementItem { + ReplacementItem() = default; + explicit ReplacementItem(StringRef Literal) + : Type(ReplacementType::Literal), Spec(Literal) {} + ReplacementItem(StringRef Spec, size_t Index, size_t Align, AlignStyle Where, + char Pad, StringRef Options) + : Type(ReplacementType::Format), Spec(Spec), Index(Index), Align(Align), + Where(Where), Pad(Pad), Options(Options) {} + + ReplacementType Type = ReplacementType::Empty; + StringRef Spec; + size_t Index = 0; + size_t Align = 0; + AlignStyle Where = AlignStyle::Right; + char Pad = 0; + StringRef Options; +}; + +class formatv_object_base { +protected: + StringRef Fmt; + ArrayRef<detail::format_adapter *> Adapters; + + static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where, + size_t &Align, char &Pad); + + static std::pair<ReplacementItem, StringRef> + splitLiteralAndReplacement(StringRef Fmt); + + formatv_object_base(StringRef Fmt, + ArrayRef<detail::format_adapter *> Adapters) + : Fmt(Fmt), Adapters(Adapters) {} + + formatv_object_base(formatv_object_base const &rhs) = delete; + formatv_object_base(formatv_object_base &&rhs) = default; + +public: + void format(raw_ostream &S) const { + for (auto &R : parseFormatString(Fmt)) { + if (R.Type == ReplacementType::Empty) + continue; + if (R.Type == ReplacementType::Literal) { + S << R.Spec; + continue; + } + if (R.Index >= Adapters.size()) { + S << R.Spec; + continue; + } + + auto *W = Adapters[R.Index]; + + FmtAlign Align(*W, R.Where, R.Align, R.Pad); + Align.format(S, R.Options); + } + } + static SmallVector<ReplacementItem, 2> parseFormatString(StringRef Fmt); + + static std::optional<ReplacementItem> parseReplacementItem(StringRef Spec); + + std::string str() const { + std::string Result; + raw_string_ostream Stream(Result); + Stream << *this; + Stream.flush(); + return Result; + } + + template <unsigned N> SmallString<N> sstr() const { + SmallString<N> Result; + raw_svector_ostream Stream(Result); + Stream << *this; + return Result; + } + + template <unsigned N> operator SmallString<N>() const { return sstr<N>(); } + + operator std::string() const { return str(); } +}; + +template <typename Tuple> class formatv_object : public formatv_object_base { + // Storage for the parameter adapters. Since the base class erases the type + // of the parameters, we have to own the storage for the parameters here, and + // have the base class store type-erased pointers into this tuple. + Tuple Parameters; + std::array<detail::format_adapter *, std::tuple_size<Tuple>::value> + ParameterPointers; + + // The parameters are stored in a std::tuple, which does not provide runtime + // indexing capabilities. In order to enable runtime indexing, we use this + // structure to put the parameters into a std::array. Since the parameters + // are not all the same type, we use some type-erasure by wrapping the + // parameters in a template class that derives from a non-template superclass. + // Essentially, we are converting a std::tuple<Derived<Ts...>> to a + // std::array<Base*>. + struct create_adapters { + template <typename... Ts> + std::array<detail::format_adapter *, std::tuple_size<Tuple>::value> + operator()(Ts &... Items) { + return {{&Items...}}; + } + }; + +public: + formatv_object(StringRef Fmt, Tuple &&Params) + : formatv_object_base(Fmt, ParameterPointers), + Parameters(std::move(Params)) { + ParameterPointers = std::apply(create_adapters(), Parameters); + } + + formatv_object(formatv_object const &rhs) = delete; + + formatv_object(formatv_object &&rhs) + : formatv_object_base(std::move(rhs)), + Parameters(std::move(rhs.Parameters)) { + ParameterPointers = std::apply(create_adapters(), Parameters); + Adapters = ParameterPointers; + } +}; + +// Format text given a format string and replacement parameters. +// +// ===General Description=== +// +// Formats textual output. `Fmt` is a string consisting of one or more +// replacement sequences with the following grammar: +// +// rep_field ::= "{" index ["," layout] [":" format] "}" +// index ::= <non-negative integer> +// layout ::= [[[char]loc]width] +// format ::= <any string not containing "{" or "}"> +// char ::= <any character except "{" or "}"> +// loc ::= "-" | "=" | "+" +// width ::= <positive integer> +// +// index - A non-negative integer specifying the index of the item in the +// parameter pack to print. Any other value is invalid. +// layout - A string controlling how the field is laid out within the available +// space. +// format - A type-dependent string used to provide additional options to +// the formatting operation. Refer to the documentation of the +// various individual format providers for per-type options. +// char - The padding character. Defaults to ' ' (space). Only valid if +// `loc` is also specified. +// loc - Where to print the formatted text within the field. Only valid if +// `width` is also specified. +// '-' : The field is left aligned within the available space. +// '=' : The field is centered within the available space. +// '+' : The field is right aligned within the available space (this +// is the default). +// width - The width of the field within which to print the formatted text. +// If this is less than the required length then the `char` and `loc` +// fields are ignored, and the field is printed with no leading or +// trailing padding. If this is greater than the required length, +// then the text is output according to the value of `loc`, and padded +// as appropriate on the left and/or right by `char`. +// +// ===Special Characters=== +// +// The characters '{' and '}' are reserved and cannot appear anywhere within a +// replacement sequence. Outside of a replacement sequence, in order to print +// a literal '{' it must be doubled as "{{". +// +// ===Parameter Indexing=== +// +// `index` specifies the index of the parameter in the parameter pack to format +// into the output. Note that it is possible to refer to the same parameter +// index multiple times in a given format string. This makes it possible to +// output the same value multiple times without passing it multiple times to the +// function. For example: +// +// formatv("{0} {1} {0}", "a", "bb") +// +// would yield the string "abba". This can be convenient when it is expensive +// to compute the value of the parameter, and you would otherwise have had to +// save it to a temporary. +// +// ===Formatter Search=== +// +// 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 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: +// void format(const T &Obj, raw_ostream &Stream, StringRef Options) +// Then this method is invoked as described in Step 1. +// 3. If an appropriate operator<< for raw_ostream exists, it will be used. +// For this to work, (raw_ostream& << const T&) must return raw_ostream&. +// +// If a match cannot be found through either of the above methods, a compiler +// error is generated. +// +// ===Invalid Format String Handling=== +// +// In the case of a format string which does not match the grammar described +// above, the output is undefined. With asserts enabled, LLVM will trigger an +// assertion. Otherwise, it will try to do something reasonable, but in general +// the details of what that is are undefined. +// +template <typename... Ts> +inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object<decltype( + std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...))> { + using ParamTuple = decltype( + std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...)); + return formatv_object<ParamTuple>( + Fmt, + std::make_tuple(detail::build_format_adapter(std::forward<Ts>(Vals))...)); +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_FORMATVARIADIC_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FormatVariadicDetails.h b/contrib/libs/llvm16/include/llvm/Support/FormatVariadicDetails.h new file mode 100644 index 00000000000..b2a794bf16d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FormatVariadicDetails.h @@ -0,0 +1,174 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- FormatVariadicDetails.h - Helpers for FormatVariadic.h ----*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATVARIADICDETAILS_H +#define LLVM_SUPPORT_FORMATVARIADICDETAILS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +#include <type_traits> + +namespace llvm { +template <typename T, typename Enable = void> struct format_provider {}; +class Error; + +namespace detail { +class format_adapter { + virtual void anchor(); + +protected: + virtual ~format_adapter() = default; + +public: + virtual void format(raw_ostream &S, StringRef Options) = 0; +}; + +template <typename T> class provider_format_adapter : public format_adapter { + T Item; + +public: + explicit provider_format_adapter(T &&Item) : Item(std::forward<T>(Item)) {} + + void format(llvm::raw_ostream &S, StringRef Options) override { + format_provider<std::decay_t<T>>::format(Item, S, Options); + } +}; + +template <typename T> +class stream_operator_format_adapter : public format_adapter { + T Item; + +public: + explicit stream_operator_format_adapter(T &&Item) + : Item(std::forward<T>(Item)) {} + + void format(llvm::raw_ostream &S, StringRef) override { S << Item; } +}; + +template <typename T> class missing_format_adapter; + +// Test if format_provider<T> is defined on T and contains a member function +// with the signature: +// static void format(const T&, raw_stream &, StringRef); +// +template <class T> class has_FormatProvider { +public: + using Decayed = std::decay_t<T>; + typedef void (*Signature_format)(const Decayed &, llvm::raw_ostream &, + StringRef); + + template <typename U> + static char test(SameType<Signature_format, &U::format> *); + + template <typename U> static double test(...); + + static bool const value = + (sizeof(test<llvm::format_provider<Decayed>>(nullptr)) == 1); +}; + +// Test if raw_ostream& << T -> raw_ostream& is findable via ADL. +template <class T> class has_StreamOperator { +public: + using ConstRefT = const std::decay_t<T> &; + + template <typename U> + static char test( + std::enable_if_t<std::is_same<decltype(std::declval<llvm::raw_ostream &>() + << std::declval<U>()), + llvm::raw_ostream &>::value, + int *>); + + template <typename U> static double test(...); + + static bool const value = (sizeof(test<ConstRefT>(nullptr)) == 1); +}; + +// Simple template that decides whether a type T should use the member-function +// based format() invocation. +template <typename T> +struct uses_format_member + : public std::integral_constant< + bool, + std::is_base_of<format_adapter, std::remove_reference_t<T>>::value> { +}; + +// Simple template that decides whether a type T should use the format_provider +// based format() invocation. The member function takes priority, so this test +// will only be true if there is not ALSO a format member. +template <typename T> +struct uses_format_provider + : public std::integral_constant< + bool, !uses_format_member<T>::value && has_FormatProvider<T>::value> { +}; + +// Simple template that decides whether a type T should use the operator<< +// based format() invocation. This takes last priority. +template <typename T> +struct uses_stream_operator + : public std::integral_constant<bool, !uses_format_member<T>::value && + !uses_format_provider<T>::value && + has_StreamOperator<T>::value> {}; + +// Simple template that decides whether a type T has neither a member-function +// nor format_provider based implementation that it can use. Mostly used so +// that the compiler spits out a nice diagnostic when a type with no format +// implementation can be located. +template <typename T> +struct uses_missing_provider + : public std::integral_constant<bool, !uses_format_member<T>::value && + !uses_format_provider<T>::value && + !uses_stream_operator<T>::value> { +}; + +template <typename T> +std::enable_if_t<uses_format_member<T>::value, T> +build_format_adapter(T &&Item) { + return std::forward<T>(Item); +} + +template <typename T> +std::enable_if_t<uses_format_provider<T>::value, provider_format_adapter<T>> +build_format_adapter(T &&Item) { + return provider_format_adapter<T>(std::forward<T>(Item)); +} + +template <typename T> +std::enable_if_t<uses_stream_operator<T>::value, + stream_operator_format_adapter<T>> +build_format_adapter(T &&Item) { + // If the caller passed an Error by value, then stream_operator_format_adapter + // would be responsible for consuming it. + // Make the caller opt into this by calling fmt_consume(). + static_assert( + !std::is_same<llvm::Error, std::remove_cv_t<T>>::value, + "llvm::Error-by-value must be wrapped in fmt_consume() for formatv"); + return stream_operator_format_adapter<T>(std::forward<T>(Item)); +} + +template <typename T> +std::enable_if_t<uses_missing_provider<T>::value, missing_format_adapter<T>> +build_format_adapter(T &&) { + return missing_format_adapter<T>(); +} +} +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/FormattedStream.h b/contrib/libs/llvm16/include/llvm/Support/FormattedStream.h new file mode 100644 index 00000000000..bb60c575e86 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/FormattedStream.h @@ -0,0 +1,196 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/FormattedStream.h - Formatted streams ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains raw_ostream implementations for streams to do +// things like pretty-print comments. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATTEDSTREAM_H +#define LLVM_SUPPORT_FORMATTEDSTREAM_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include <utility> + +namespace llvm { + +/// formatted_raw_ostream - A raw_ostream that wraps another one and keeps track +/// of line and column position, allowing padding out to specific column +/// boundaries and querying the number of lines written to the stream. This +/// assumes that the contents of the stream is valid UTF-8 encoded text. This +/// doesn't attempt to handle everything Unicode can do (combining characters, +/// right-to-left markers, etc), but should cover the cases likely to appear in +/// source code or diagnostic messages. +class formatted_raw_ostream : public raw_ostream { + /// TheStream - The real stream we output to. We set it to be + /// unbuffered, since we're already doing our own buffering. + /// + raw_ostream *TheStream; + + /// Position - The current output column and line of the data that's + /// been flushed and the portion of the buffer that's been + /// scanned. The line and column scheme is zero-based. + /// + std::pair<unsigned, unsigned> Position; + + /// Scanned - This points to one past the last character in the + /// buffer we've scanned. + /// + const char *Scanned; + + /// PartialUTF8Char - Either empty or a prefix of a UTF-8 code unit sequence + /// for a Unicode scalar value which should be prepended to the buffer for the + /// next call to ComputePosition. This is needed when the buffer is flushed + /// when it ends part-way through the UTF-8 encoding of a Unicode scalar + /// value, so that we can compute the display width of the character once we + /// have the rest of it. + SmallString<4> PartialUTF8Char; + + void write_impl(const char *Ptr, size_t Size) override; + + /// current_pos - Return the current position within the stream, + /// not counting the bytes currently in the buffer. + uint64_t current_pos() const override { + // Our current position in the stream is all the contents which have been + // written to the underlying stream (*not* the current position of the + // underlying stream). + return TheStream->tell(); + } + + /// ComputePosition - Examine the given output buffer and figure out the new + /// position after output. This is safe to call multiple times on the same + /// buffer, as it records the most recently scanned character and resumes from + /// there when the buffer has not been flushed. + void ComputePosition(const char *Ptr, size_t size); + + /// UpdatePosition - scan the characters in [Ptr, Ptr+Size), and update the + /// line and column numbers. Unlike ComputePosition, this must be called + /// exactly once on each region of the buffer. + void UpdatePosition(const char *Ptr, size_t Size); + + void setStream(raw_ostream &Stream) { + releaseStream(); + + TheStream = &Stream; + + // This formatted_raw_ostream inherits from raw_ostream, so it'll do its + // own buffering, and it doesn't need or want TheStream to do another + // layer of buffering underneath. Resize the buffer to what TheStream + // had been using, and tell TheStream not to do its own buffering. + if (size_t BufferSize = TheStream->GetBufferSize()) + SetBufferSize(BufferSize); + else + SetUnbuffered(); + TheStream->SetUnbuffered(); + + Scanned = nullptr; + } + +public: + /// formatted_raw_ostream - Open the specified file for + /// writing. If an error occurs, information about the error is + /// put into ErrorInfo, and the stream should be immediately + /// destroyed; the string will be empty if no error occurred. + /// + /// As a side effect, the given Stream is set to be Unbuffered. + /// This is because formatted_raw_ostream does its own buffering, + /// so it doesn't want another layer of buffering to be happening + /// underneath it. + /// + formatted_raw_ostream(raw_ostream &Stream) + : TheStream(nullptr), Position(0, 0) { + setStream(Stream); + } + explicit formatted_raw_ostream() : TheStream(nullptr), Position(0, 0) { + Scanned = nullptr; + } + + ~formatted_raw_ostream() override { + flush(); + releaseStream(); + } + + /// PadToColumn - Align the output to some column number. If the current + /// column is already equal to or more than NewCol, PadToColumn inserts one + /// space. + /// + /// \param NewCol - The column to move to. + formatted_raw_ostream &PadToColumn(unsigned NewCol); + + unsigned getColumn() { + // Calculate current position, taking buffer contents into account. + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.first; + } + + unsigned getLine() { + // Calculate current position, taking buffer contents into account. + ComputePosition(getBufferStart(), GetNumBytesInBuffer()); + return Position.second; + } + + raw_ostream &resetColor() override { + TheStream->resetColor(); + return *this; + } + + raw_ostream &reverseColor() override { + TheStream->reverseColor(); + return *this; + } + + raw_ostream &changeColor(enum Colors Color, bool Bold, bool BG) override { + TheStream->changeColor(Color, Bold, BG); + return *this; + } + + bool is_displayed() const override { + return TheStream->is_displayed(); + } + +private: + void releaseStream() { + // Transfer the buffer settings from this raw_ostream back to the underlying + // stream. + if (!TheStream) + return; + if (size_t BufferSize = GetBufferSize()) + TheStream->SetBufferSize(BufferSize); + else + TheStream->SetUnbuffered(); + } +}; + +/// fouts() - This returns a reference to a formatted_raw_ostream for +/// standard output. Use it like: fouts() << "foo" << "bar"; +formatted_raw_ostream &fouts(); + +/// ferrs() - This returns a reference to a formatted_raw_ostream for +/// standard error. Use it like: ferrs() << "foo" << "bar"; +formatted_raw_ostream &ferrs(); + +/// fdbgs() - This returns a reference to a formatted_raw_ostream for +/// debug output. Use it like: fdbgs() << "foo" << "bar"; +formatted_raw_ostream &fdbgs(); + +} // end llvm namespace + + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/GenericDomTree.h b/contrib/libs/llvm16/include/llvm/Support/GenericDomTree.h new file mode 100644 index 00000000000..94bedc6914a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/GenericDomTree.h @@ -0,0 +1,977 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- GenericDomTree.h - Generic dominator trees for graphs ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines a set of templates that efficiently compute a dominator +/// tree over a generic graph. This is used typically in LLVM for fast +/// dominance queries on the CFG, but is fully generic w.r.t. the underlying +/// graph types. +/// +/// Unlike ADT/* graph algorithms, generic dominator tree has more requirements +/// on the graph's NodeRef. The NodeRef should be a pointer and, +/// either NodeRef->getParent() must return the parent node that is also a +/// pointer or DomTreeNodeTraits needs to be specialized. +/// +/// FIXME: Maybe GenericDomTree needs a TreeTraits, instead of GraphTraits. +/// +//===----------------------------------------------------------------------===// + +#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/CFGDiff.h" +#include "llvm/Support/CFGUpdate.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> + +namespace llvm { + +template <typename NodeT, bool IsPostDom> +class DominatorTreeBase; + +namespace DomTreeBuilder { +template <typename DomTreeT> +struct SemiNCAInfo; +} // namespace DomTreeBuilder + +/// Base class for the actual dominator tree node. +template <class NodeT> class DomTreeNodeBase { + friend class PostDominatorTree; + friend class DominatorTreeBase<NodeT, false>; + friend class DominatorTreeBase<NodeT, true>; + friend struct DomTreeBuilder::SemiNCAInfo<DominatorTreeBase<NodeT, false>>; + friend struct DomTreeBuilder::SemiNCAInfo<DominatorTreeBase<NodeT, true>>; + + NodeT *TheBB; + DomTreeNodeBase *IDom; + unsigned Level; + SmallVector<DomTreeNodeBase *, 4> Children; + mutable unsigned DFSNumIn = ~0; + mutable unsigned DFSNumOut = ~0; + + public: + DomTreeNodeBase(NodeT *BB, DomTreeNodeBase *iDom) + : TheBB(BB), IDom(iDom), Level(IDom ? IDom->Level + 1 : 0) {} + + using iterator = typename SmallVector<DomTreeNodeBase *, 4>::iterator; + using const_iterator = + typename SmallVector<DomTreeNodeBase *, 4>::const_iterator; + + iterator begin() { return Children.begin(); } + iterator end() { return Children.end(); } + const_iterator begin() const { return Children.begin(); } + const_iterator end() const { return Children.end(); } + + DomTreeNodeBase *const &back() const { return Children.back(); } + DomTreeNodeBase *&back() { return Children.back(); } + + iterator_range<iterator> children() { return make_range(begin(), end()); } + iterator_range<const_iterator> children() const { + return make_range(begin(), end()); + } + + NodeT *getBlock() const { return TheBB; } + DomTreeNodeBase *getIDom() const { return IDom; } + unsigned getLevel() const { return Level; } + + std::unique_ptr<DomTreeNodeBase> addChild( + std::unique_ptr<DomTreeNodeBase> C) { + Children.push_back(C.get()); + return C; + } + + bool isLeaf() const { return Children.empty(); } + size_t getNumChildren() const { return Children.size(); } + + void clearAllChildren() { Children.clear(); } + + bool compare(const DomTreeNodeBase *Other) const { + if (getNumChildren() != Other->getNumChildren()) + return true; + + if (Level != Other->Level) return true; + + SmallPtrSet<const NodeT *, 4> OtherChildren; + for (const DomTreeNodeBase *I : *Other) { + const NodeT *Nd = I->getBlock(); + OtherChildren.insert(Nd); + } + + for (const DomTreeNodeBase *I : *this) { + const NodeT *N = I->getBlock(); + if (OtherChildren.count(N) == 0) + return true; + } + return false; + } + + void setIDom(DomTreeNodeBase *NewIDom) { + assert(IDom && "No immediate dominator?"); + if (IDom == NewIDom) return; + + auto I = find(IDom->Children, this); + assert(I != IDom->Children.end() && + "Not in immediate dominator children set!"); + // I am no longer your child... + IDom->Children.erase(I); + + // Switch to new dominator + IDom = NewIDom; + IDom->Children.push_back(this); + + UpdateLevel(); + } + + /// getDFSNumIn/getDFSNumOut - These return the DFS visitation order for nodes + /// in the dominator tree. They are only guaranteed valid if + /// updateDFSNumbers() has been called. + unsigned getDFSNumIn() const { return DFSNumIn; } + unsigned getDFSNumOut() const { return DFSNumOut; } + +private: + // Return true if this node is dominated by other. Use this only if DFS info + // is valid. + bool DominatedBy(const DomTreeNodeBase *other) const { + return this->DFSNumIn >= other->DFSNumIn && + this->DFSNumOut <= other->DFSNumOut; + } + + void UpdateLevel() { + assert(IDom); + if (Level == IDom->Level + 1) return; + + SmallVector<DomTreeNodeBase *, 64> WorkStack = {this}; + + while (!WorkStack.empty()) { + DomTreeNodeBase *Current = WorkStack.pop_back_val(); + Current->Level = Current->IDom->Level + 1; + + for (DomTreeNodeBase *C : *Current) { + assert(C->IDom); + if (C->Level != C->IDom->Level + 1) WorkStack.push_back(C); + } + } + } +}; + +template <class NodeT> +raw_ostream &operator<<(raw_ostream &O, const DomTreeNodeBase<NodeT> *Node) { + if (Node->getBlock()) + Node->getBlock()->printAsOperand(O, false); + else + O << " <<exit node>>"; + + O << " {" << Node->getDFSNumIn() << "," << Node->getDFSNumOut() << "} [" + << Node->getLevel() << "]\n"; + + return O; +} + +template <class NodeT> +void PrintDomTree(const DomTreeNodeBase<NodeT> *N, raw_ostream &O, + unsigned Lev) { + O.indent(2 * Lev) << "[" << Lev << "] " << N; + for (typename DomTreeNodeBase<NodeT>::const_iterator I = N->begin(), + E = N->end(); + I != E; ++I) + PrintDomTree<NodeT>(*I, O, Lev + 1); +} + +namespace DomTreeBuilder { +// The routines below are provided in a separate header but referenced here. +template <typename DomTreeT> +void Calculate(DomTreeT &DT); + +template <typename DomTreeT> +void CalculateWithUpdates(DomTreeT &DT, + ArrayRef<typename DomTreeT::UpdateType> Updates); + +template <typename DomTreeT> +void InsertEdge(DomTreeT &DT, typename DomTreeT::NodePtr From, + typename DomTreeT::NodePtr To); + +template <typename DomTreeT> +void DeleteEdge(DomTreeT &DT, typename DomTreeT::NodePtr From, + typename DomTreeT::NodePtr To); + +template <typename DomTreeT> +void ApplyUpdates(DomTreeT &DT, + GraphDiff<typename DomTreeT::NodePtr, + DomTreeT::IsPostDominator> &PreViewCFG, + GraphDiff<typename DomTreeT::NodePtr, + DomTreeT::IsPostDominator> *PostViewCFG); + +template <typename DomTreeT> +bool Verify(const DomTreeT &DT, typename DomTreeT::VerificationLevel VL); +} // namespace DomTreeBuilder + +/// Default DomTreeNode traits for NodeT. The default implementation assume a +/// Function-like NodeT. Can be specialized to support different node types. +template <typename NodeT> struct DomTreeNodeTraits { + using NodeType = NodeT; + using NodePtr = NodeT *; + using ParentPtr = decltype(std::declval<NodePtr>()->getParent()); + static_assert(std::is_pointer<ParentPtr>::value, + "Currently NodeT's parent must be a pointer type"); + using ParentType = std::remove_pointer_t<ParentPtr>; + + static NodeT *getEntryNode(ParentPtr Parent) { return &Parent->front(); } + static ParentPtr getParent(NodePtr BB) { return BB->getParent(); } +}; + +/// Core dominator tree base class. +/// +/// This class is a generic template over graph nodes. It is instantiated for +/// 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 NodeTrait = DomTreeNodeTraits<NodeT>; + using NodeType = typename NodeTrait::NodeType; + using NodePtr = typename NodeTrait::NodePtr; + using ParentPtr = typename NodeTrait::ParentPtr; + static_assert(std::is_pointer<ParentPtr>::value, + "Currently NodeT's parent must be a pointer type"); + using ParentType = std::remove_pointer_t<ParentPtr>; + static constexpr bool IsPostDominator = IsPostDom; + + using UpdateType = cfg::Update<NodePtr>; + using UpdateKind = cfg::UpdateKind; + static constexpr UpdateKind Insert = UpdateKind::Insert; + static constexpr UpdateKind Delete = UpdateKind::Delete; + + enum class VerificationLevel { Fast, Basic, Full }; + +protected: + // 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 = nullptr; + ParentPtr Parent = nullptr; + + mutable bool DFSInfoValid = false; + mutable unsigned int SlowQueries = 0; + + friend struct DomTreeBuilder::SemiNCAInfo<DominatorTreeBase>; + + public: + DominatorTreeBase() = default; + + DominatorTreeBase(DominatorTreeBase &&Arg) + : Roots(std::move(Arg.Roots)), + DomTreeNodes(std::move(Arg.DomTreeNodes)), + RootNode(Arg.RootNode), + Parent(Arg.Parent), + DFSInfoValid(Arg.DFSInfoValid), + SlowQueries(Arg.SlowQueries) { + Arg.wipe(); + } + + DominatorTreeBase &operator=(DominatorTreeBase &&RHS) { + Roots = std::move(RHS.Roots); + DomTreeNodes = std::move(RHS.DomTreeNodes); + RootNode = RHS.RootNode; + Parent = RHS.Parent; + DFSInfoValid = RHS.DFSInfoValid; + SlowQueries = RHS.SlowQueries; + RHS.wipe(); + return *this; + } + + DominatorTreeBase(const DominatorTreeBase &) = delete; + DominatorTreeBase &operator=(const DominatorTreeBase &) = delete; + + /// Iteration over roots. + /// + /// This may include multiple blocks if we are computing post dominators. + /// For forward dominators, this will always be a single block (the entry + /// block). + using root_iterator = typename SmallVectorImpl<NodeT *>::iterator; + using const_root_iterator = typename SmallVectorImpl<NodeT *>::const_iterator; + + root_iterator root_begin() { return Roots.begin(); } + const_root_iterator root_begin() const { return Roots.begin(); } + root_iterator root_end() { return Roots.end(); } + const_root_iterator root_end() const { return Roots.end(); } + + size_t root_size() const { return Roots.size(); } + + iterator_range<root_iterator> roots() { + return make_range(root_begin(), root_end()); + } + iterator_range<const_root_iterator> roots() const { + return make_range(root_begin(), root_end()); + } + + /// isPostDominator - Returns true if analysis based of postdoms + /// + bool isPostDominator() const { return IsPostDominator; } + + /// compare - Return false if the other dominator tree base matches this + /// dominator tree base. Otherwise return true. + bool compare(const DominatorTreeBase &Other) const { + if (Parent != Other.Parent) return true; + + if (Roots.size() != Other.Roots.size()) + return true; + + if (!std::is_permutation(Roots.begin(), Roots.end(), Other.Roots.begin())) + return true; + + const DomTreeNodeMapType &OtherDomTreeNodes = Other.DomTreeNodes; + if (DomTreeNodes.size() != OtherDomTreeNodes.size()) + return true; + + for (const auto &DomTreeNode : DomTreeNodes) { + NodeT *BB = DomTreeNode.first; + typename DomTreeNodeMapType::const_iterator OI = + OtherDomTreeNodes.find(BB); + if (OI == OtherDomTreeNodes.end()) + return true; + + DomTreeNodeBase<NodeT> &MyNd = *DomTreeNode.second; + DomTreeNodeBase<NodeT> &OtherNd = *OI->second; + + if (MyNd.compare(&OtherNd)) + return true; + } + + return false; + } + + /// getNode - return the (Post)DominatorTree node for the specified basic + /// block. This is the same as using operator[] on this class. The result + /// may (but is not required to) be null for a forward (backwards) + /// statically unreachable block. + DomTreeNodeBase<NodeT> *getNode(const NodeT *BB) const { + auto I = DomTreeNodes.find(BB); + if (I != DomTreeNodes.end()) + return I->second.get(); + return nullptr; + } + + /// See getNode. + DomTreeNodeBase<NodeT> *operator[](const NodeT *BB) const { + return getNode(BB); + } + + /// getRootNode - This returns the entry node for the CFG of the function. If + /// this tree represents the post-dominance relations for a function, however, + /// this root may be a node with the block == NULL. This is the case when + /// there are multiple exit nodes from a particular function. Consumers of + /// post-dominance information must be capable of dealing with this + /// possibility. + /// + DomTreeNodeBase<NodeT> *getRootNode() { return RootNode; } + const DomTreeNodeBase<NodeT> *getRootNode() const { return RootNode; } + + /// Get all nodes dominated by R, including R itself. + void getDescendants(NodeT *R, SmallVectorImpl<NodeT *> &Result) const { + Result.clear(); + const DomTreeNodeBase<NodeT> *RN = getNode(R); + if (!RN) + return; // If R is unreachable, it will not be present in the DOM tree. + SmallVector<const DomTreeNodeBase<NodeT> *, 8> WL; + WL.push_back(RN); + + while (!WL.empty()) { + const DomTreeNodeBase<NodeT> *N = WL.pop_back_val(); + Result.push_back(N->getBlock()); + WL.append(N->begin(), N->end()); + } + } + + /// properlyDominates - Returns true iff A dominates B and A != B. + /// Note that this is not a constant time operation! + /// + bool properlyDominates(const DomTreeNodeBase<NodeT> *A, + const DomTreeNodeBase<NodeT> *B) const { + if (!A || !B) + return false; + if (A == B) + return false; + return dominates(A, B); + } + + bool properlyDominates(const NodeT *A, const NodeT *B) const; + + /// isReachableFromEntry - Return true if A is dominated by the entry + /// block of the function containing it. + bool isReachableFromEntry(const NodeT *A) const { + assert(!this->isPostDominator() && + "This is not implemented for post dominators"); + return isReachableFromEntry(getNode(const_cast<NodeT *>(A))); + } + + bool isReachableFromEntry(const DomTreeNodeBase<NodeT> *A) const { return A; } + + /// dominates - Returns true iff A dominates B. Note that this is not a + /// constant time operation! + /// + bool dominates(const DomTreeNodeBase<NodeT> *A, + const DomTreeNodeBase<NodeT> *B) const { + // A node trivially dominates itself. + if (B == A) + return true; + + // An unreachable node is dominated by anything. + if (!isReachableFromEntry(B)) + return true; + + // And dominates nothing. + if (!isReachableFromEntry(A)) + return false; + + if (B->getIDom() == A) return true; + + if (A->getIDom() == B) return false; + + // A can only dominate B if it is higher in the tree. + if (A->getLevel() >= B->getLevel()) return false; + + // Compare the result of the tree walk and the dfs numbers, if expensive + // checks are enabled. +#ifdef EXPENSIVE_CHECKS + assert((!DFSInfoValid || + (dominatedBySlowTreeWalk(A, B) == B->DominatedBy(A))) && + "Tree walk disagrees with dfs numbers!"); +#endif + + if (DFSInfoValid) + return B->DominatedBy(A); + + // If we end up with too many slow queries, just update the + // DFS numbers on the theory that we are going to keep querying. + SlowQueries++; + if (SlowQueries > 32) { + updateDFSNumbers(); + return B->DominatedBy(A); + } + + return dominatedBySlowTreeWalk(A, B); + } + + bool dominates(const NodeT *A, const NodeT *B) const; + + NodeT *getRoot() const { + assert(this->Roots.size() == 1 && "Should always have entry node!"); + return this->Roots[0]; + } + + /// Find nearest common dominator basic block for basic block A and B. A and B + /// must have tree nodes. + NodeT *findNearestCommonDominator(NodeT *A, NodeT *B) const { + assert(A && B && "Pointers are not valid"); + assert(NodeTrait::getParent(A) == NodeTrait::getParent(B) && + "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 (!isPostDominator()) { + NodeT &Entry = + *DomTreeNodeTraits<NodeT>::getEntryNode(NodeTrait::getParent(A)); + if (A == &Entry || B == &Entry) + return &Entry; + } + + DomTreeNodeBase<NodeT> *NodeA = getNode(A); + DomTreeNodeBase<NodeT> *NodeB = getNode(B); + assert(NodeA && "A must be in the tree"); + assert(NodeB && "B must be in the tree"); + + // Use level information to go up the tree until the levels match. Then + // continue going up til we arrive at the same node. + while (NodeA != NodeB) { + if (NodeA->getLevel() < NodeB->getLevel()) std::swap(NodeA, NodeB); + + NodeA = NodeA->IDom; + } + + return NodeA->getBlock(); + } + + const NodeT *findNearestCommonDominator(const NodeT *A, + const NodeT *B) const { + // Cast away the const qualifiers here. This is ok since + // const is re-introduced on the return type. + return findNearestCommonDominator(const_cast<NodeT *>(A), + const_cast<NodeT *>(B)); + } + + bool isVirtualRoot(const DomTreeNodeBase<NodeT> *A) const { + return isPostDominator() && !A->getBlock(); + } + + //===--------------------------------------------------------------------===// + // 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. + /// + /// The applyUpdates function can reorder the updates and remove redundant + /// ones internally (as long as it is done in a deterministic fashion). 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 ordered sequence of updates to perform. The current CFG + /// and the reverse of these updates provides the pre-view of the CFG. + /// + void applyUpdates(ArrayRef<UpdateType> Updates) { + GraphDiff<NodePtr, IsPostDominator> PreViewCFG( + Updates, /*ReverseApplyUpdates=*/true); + DomTreeBuilder::ApplyUpdates(*this, PreViewCFG, nullptr); + } + + /// \param Updates An ordered sequence of updates to perform. The current CFG + /// and the reverse of these updates provides the pre-view of the CFG. + /// \param PostViewUpdates An ordered sequence of update to perform in order + /// to obtain a post-view of the CFG. The DT will be updated assuming the + /// obtained PostViewCFG is the desired end state. + void applyUpdates(ArrayRef<UpdateType> Updates, + ArrayRef<UpdateType> PostViewUpdates) { + if (Updates.empty()) { + GraphDiff<NodePtr, IsPostDom> PostViewCFG(PostViewUpdates); + DomTreeBuilder::ApplyUpdates(*this, PostViewCFG, &PostViewCFG); + } else { + // PreViewCFG needs to merge Updates and PostViewCFG. The updates in + // Updates need to be reversed, and match the direction in PostViewCFG. + // The PostViewCFG is created with updates reversed (equivalent to changes + // made to the CFG), so the PreViewCFG needs all the updates reverse + // applied. + SmallVector<UpdateType> AllUpdates(Updates.begin(), Updates.end()); + append_range(AllUpdates, PostViewUpdates); + GraphDiff<NodePtr, IsPostDom> PreViewCFG(AllUpdates, + /*ReverseApplyUpdates=*/true); + GraphDiff<NodePtr, IsPostDom> PostViewCFG(PostViewUpdates); + DomTreeBuilder::ApplyUpdates(*this, PreViewCFG, &PostViewCFG); + } + } + + /// 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 + /// on the actual CFG. There cannot be any other updates that the dominator + /// tree doesn't know about. + /// + /// Note that for postdominators it automatically takes care of inserting + /// a reverse edge internally (so there's no need to swap the parameters). + /// + void insertEdge(NodeT *From, NodeT *To) { + assert(From); + assert(To); + assert(NodeTrait::getParent(From) == Parent); + assert(NodeTrait::getParent(To) == Parent); + DomTreeBuilder::InsertEdge(*this, From, To); + } + + /// 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. 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). + /// + void deleteEdge(NodeT *From, NodeT *To) { + assert(From); + assert(To); + assert(NodeTrait::getParent(From) == Parent); + assert(NodeTrait::getParent(To) == Parent); + DomTreeBuilder::DeleteEdge(*this, From, To); + } + + /// Add a new node to the dominator tree information. + /// + /// This creates a new node as a child of DomBB dominator node, linking it + /// into the children list of the immediate dominator. + /// + /// \param BB New node in CFG. + /// \param DomBB CFG node that is dominator for BB. + /// \returns New dominator tree node that represents new CFG node. + /// + DomTreeNodeBase<NodeT> *addNewBlock(NodeT *BB, NodeT *DomBB) { + assert(getNode(BB) == nullptr && "Block already in dominator tree!"); + DomTreeNodeBase<NodeT> *IDomNode = getNode(DomBB); + assert(IDomNode && "Not immediate dominator specified for block!"); + DFSInfoValid = false; + return createChild(BB, IDomNode); + } + + /// Add a new node to the forward dominator tree and make it a new root. + /// + /// \param BB New node in CFG. + /// \returns New dominator tree node that represents new CFG node. + /// + DomTreeNodeBase<NodeT> *setNewRoot(NodeT *BB) { + assert(getNode(BB) == nullptr && "Block already in dominator tree!"); + assert(!this->isPostDominator() && + "Cannot change root of post-dominator tree"); + DFSInfoValid = false; + DomTreeNodeBase<NodeT> *NewNode = createNode(BB); + if (Roots.empty()) { + addRoot(BB); + } else { + assert(Roots.size() == 1); + NodeT *OldRoot = Roots.front(); + auto &OldNode = DomTreeNodes[OldRoot]; + OldNode = NewNode->addChild(std::move(DomTreeNodes[OldRoot])); + OldNode->IDom = NewNode; + OldNode->UpdateLevel(); + Roots[0] = BB; + } + return RootNode = NewNode; + } + + /// changeImmediateDominator - This method is used to update the dominator + /// tree information when a node's immediate dominator changes. + /// + void changeImmediateDominator(DomTreeNodeBase<NodeT> *N, + DomTreeNodeBase<NodeT> *NewIDom) { + assert(N && NewIDom && "Cannot change null node pointers!"); + DFSInfoValid = false; + N->setIDom(NewIDom); + } + + void changeImmediateDominator(NodeT *BB, NodeT *NewBB) { + changeImmediateDominator(getNode(BB), getNode(NewBB)); + } + + /// eraseNode - Removes a node from the dominator tree. Block must not + /// dominate any other blocks. Removes node from its immediate dominator's + /// children list. Deletes dominator node associated with basic block BB. + void eraseNode(NodeT *BB) { + DomTreeNodeBase<NodeT> *Node = getNode(BB); + assert(Node && "Removing node that isn't in dominator tree."); + assert(Node->isLeaf() && "Node is not a leaf node."); + + DFSInfoValid = false; + + // Remove node from immediate dominator's children list. + DomTreeNodeBase<NodeT> *IDom = Node->getIDom(); + if (IDom) { + const auto I = find(IDom->Children, Node); + assert(I != IDom->Children.end() && + "Not in immediate dominator children set!"); + // I am no longer your child... + IDom->Children.erase(I); + } + + 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 + /// tree to reflect this change. + void splitBlock(NodeT *NewBB) { + if (IsPostDominator) + Split<Inverse<NodeT *>>(NewBB); + else + Split<NodeT *>(NewBB); + } + + /// print - Convert to human readable form + /// + void print(raw_ostream &O) const { + O << "=============================--------------------------------\n"; + if (IsPostDominator) + O << "Inorder PostDominator Tree: "; + else + O << "Inorder Dominator Tree: "; + if (!DFSInfoValid) + O << "DFSNumbers invalid: " << SlowQueries << " slow queries."; + O << "\n"; + + // The postdom tree can have a null root if there are no returns. + if (getRootNode()) PrintDomTree<NodeT>(getRootNode(), O, 1); + O << "Roots: "; + for (const NodePtr Block : Roots) { + Block->printAsOperand(O, false); + O << " "; + } + O << "\n"; + } + +public: + /// updateDFSNumbers - Assign In and Out numbers to the nodes while walking + /// dominator tree in dfs order. + void updateDFSNumbers() const { + if (DFSInfoValid) { + SlowQueries = 0; + return; + } + + 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; + + // 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; + const auto ChildIt = WorkStack.back().second; + + // If we visited all of the children of this node, "recurse" back up the + // stack setting the DFOutNum. + if (ChildIt == Node->end()) { + Node->DFSNumOut = DFSNum++; + WorkStack.pop_back(); + } else { + // Otherwise, recursively visit this child. + const DomTreeNodeBase<NodeT> *Child = *ChildIt; + ++WorkStack.back().second; + + WorkStack.push_back({Child, Child->begin()}); + Child->DFSNumIn = DFSNum++; + } + } + + SlowQueries = 0; + DFSInfoValid = true; + } + + /// recalculate - compute a dominator tree for the given function + void recalculate(ParentType &Func) { + Parent = &Func; + DomTreeBuilder::Calculate(*this); + } + + void recalculate(ParentType &Func, ArrayRef<UpdateType> Updates) { + Parent = &Func; + DomTreeBuilder::CalculateWithUpdates(*this, Updates); + } + + /// verify - checks if the tree is correct. There are 3 level of verification: + /// - Full -- verifies if the tree is correct by making sure all the + /// properties (including the parent and the sibling property) + /// hold. + /// Takes O(N^3) time. + /// + /// - Basic -- checks if the tree is correct, but compares it to a freshly + /// constructed tree instead of checking the sibling property. + /// Takes O(N^2) time. + /// + /// - Fast -- checks basic tree structure and compares it with a freshly + /// constructed tree. + /// Takes O(N^2) time worst case, but is faster in practise (same + /// as tree construction). + bool verify(VerificationLevel VL = VerificationLevel::Full) const { + return DomTreeBuilder::Verify(*this, VL); + } + + void reset() { + DomTreeNodes.clear(); + Roots.clear(); + RootNode = nullptr; + Parent = nullptr; + DFSInfoValid = false; + SlowQueries = 0; + } + +protected: + void addRoot(NodeT *BB) { this->Roots.push_back(BB); } + + DomTreeNodeBase<NodeT> *createChild(NodeT *BB, DomTreeNodeBase<NodeT> *IDom) { + return (DomTreeNodes[BB] = IDom->addChild( + std::make_unique<DomTreeNodeBase<NodeT>>(BB, IDom))) + .get(); + } + + DomTreeNodeBase<NodeT> *createNode(NodeT *BB) { + return (DomTreeNodes[BB] = + std::make_unique<DomTreeNodeBase<NodeT>>(BB, nullptr)) + .get(); + } + + // NewBB is split and now it has one successor. Update dominator tree to + // reflect this change. + template <class N> + void Split(typename GraphTraits<N>::NodeRef NewBB) { + using GraphT = GraphTraits<N>; + using NodeRef = typename GraphT::NodeRef; + assert(std::distance(GraphT::child_begin(NewBB), + GraphT::child_end(NewBB)) == 1 && + "NewBB should have a single successor!"); + NodeRef NewBBSucc = *GraphT::child_begin(NewBB); + + SmallVector<NodeRef, 4> PredBlocks(children<Inverse<N>>(NewBB)); + + assert(!PredBlocks.empty() && "No predblocks?"); + + bool NewBBDominatesNewBBSucc = true; + for (auto *Pred : children<Inverse<N>>(NewBBSucc)) { + if (Pred != NewBB && !dominates(NewBBSucc, Pred) && + isReachableFromEntry(Pred)) { + NewBBDominatesNewBBSucc = false; + break; + } + } + + // Find NewBB's immediate dominator and create new dominator tree node for + // NewBB. + NodeT *NewBBIDom = nullptr; + unsigned i = 0; + for (i = 0; i < PredBlocks.size(); ++i) + if (isReachableFromEntry(PredBlocks[i])) { + NewBBIDom = PredBlocks[i]; + break; + } + + // It's possible that none of the predecessors of NewBB are reachable; + // in that case, NewBB itself is unreachable, so nothing needs to be + // changed. + if (!NewBBIDom) return; + + for (i = i + 1; i < PredBlocks.size(); ++i) { + if (isReachableFromEntry(PredBlocks[i])) + NewBBIDom = findNearestCommonDominator(NewBBIDom, PredBlocks[i]); + } + + // Create the new dominator tree node... and set the idom of NewBB. + DomTreeNodeBase<NodeT> *NewBBNode = addNewBlock(NewBB, NewBBIDom); + + // If NewBB strictly dominates other blocks, then it is now the immediate + // dominator of NewBBSucc. Update the dominator tree as appropriate. + if (NewBBDominatesNewBBSucc) { + DomTreeNodeBase<NodeT> *NewBBSuccNode = getNode(NewBBSucc); + changeImmediateDominator(NewBBSuccNode, NewBBNode); + } + } + + private: + bool dominatedBySlowTreeWalk(const DomTreeNodeBase<NodeT> *A, + const DomTreeNodeBase<NodeT> *B) const { + assert(A != B); + assert(isReachableFromEntry(B)); + assert(isReachableFromEntry(A)); + + const unsigned ALevel = A->getLevel(); + const DomTreeNodeBase<NodeT> *IDom; + + // Don't walk nodes above A's subtree. When we reach A's level, we must + // either find A or be in some other subtree not dominated by A. + while ((IDom = B->getIDom()) != nullptr && IDom->getLevel() >= ALevel) + B = IDom; // Walk up the tree + + return B == A; + } + + /// Wipe this tree's state without releasing any resources. + /// + /// This is essentially a post-move helper only. It leaves the object in an + /// assignable and destroyable state, but otherwise invalid. + void wipe() { + DomTreeNodes.clear(); + RootNode = nullptr; + Parent = nullptr; + } +}; + +template <typename T> +using DomTreeBase = DominatorTreeBase<T, false>; + +template <typename T> +using PostDomTreeBase = DominatorTreeBase<T, true>; + +// These two functions are declared out of line as a workaround for building +// with old (< r147295) versions of clang because of pr11642. +template <typename NodeT, bool IsPostDom> +bool DominatorTreeBase<NodeT, IsPostDom>::dominates(const NodeT *A, + const NodeT *B) const { + if (A == B) + return true; + + // Cast away the const qualifiers here. This is ok since + // this function doesn't actually return the values returned + // from getNode. + return dominates(getNode(const_cast<NodeT *>(A)), + getNode(const_cast<NodeT *>(B))); +} +template <typename NodeT, bool IsPostDom> +bool DominatorTreeBase<NodeT, IsPostDom>::properlyDominates( + const NodeT *A, const NodeT *B) const { + if (A == B) + return false; + + // Cast away the const qualifiers here. This is ok since + // this function doesn't actually return the values returned + // from getNode. + return dominates(getNode(const_cast<NodeT *>(A)), + getNode(const_cast<NodeT *>(B))); +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_GENERICDOMTREE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/GenericDomTreeConstruction.h b/contrib/libs/llvm16/include/llvm/Support/GenericDomTreeConstruction.h new file mode 100644 index 00000000000..d5a3a14ee2c --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/GenericDomTreeConstruction.h @@ -0,0 +1,1641 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- GenericDomTreeConstruction.h - Dominator Calculation ------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// Generic dominator tree construction - this file provides routines to +/// construct immediate dominator information for a flow-graph based on the +/// Semi-NCA algorithm described in this dissertation: +/// +/// [1] Linear-Time Algorithms for Dominators and Related Problems +/// Loukas Georgiadis, Princeton University, November 2005, pp. 21-23: +/// ftp://ftp.cs.princeton.edu/reports/2005/737.pdf +/// +/// Semi-NCA algorithm runs in O(n^2) worst-case time but usually slightly +/// faster than Simple Lengauer-Tarjan in practice. +/// +/// O(n^2) worst cases happen when the computation of nearest common ancestors +/// requires O(n) average time, which is very unlikely in real world. If this +/// ever turns out to be an issue, consider implementing a hybrid algorithm +/// that uses SLT to perform full constructions and SemiNCA for incremental +/// updates. +/// +/// The file uses the Depth Based Search algorithm to perform incremental +/// updates (insertion and deletions). The implemented algorithm is based on +/// this publication: +/// +/// [2] An Experimental Study of Dynamic Dominators +/// Loukas Georgiadis, et al., April 12 2016, pp. 5-7, 9-10: +/// https://arxiv.org/pdf/1604.02711.pdf +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_GENERICDOMTREECONSTRUCTION_H +#define LLVM_SUPPORT_GENERICDOMTREECONSTRUCTION_H + +#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" +#include <optional> +#include <queue> + +#define DEBUG_TYPE "dom-tree-builder" + +namespace llvm { +namespace DomTreeBuilder { + +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; + using GraphDiffT = GraphDiff<NodePtr, IsPostDom>; + + // Information record used by Semi-NCA during tree construction. + struct InfoRec { + unsigned DFSNum = 0; + unsigned Parent = 0; + unsigned Semi = 0; + NodePtr Label = nullptr; + NodePtr IDom = nullptr; + SmallVector<NodePtr, 2> ReverseChildren; + }; + + // Number to node mapping is 1-based. Initialize the mapping to start with + // a dummy element. + std::vector<NodePtr> NumToNode = {nullptr}; + DenseMap<NodePtr, InfoRec> NodeToInfo; + + using UpdateT = typename DomTreeT::UpdateType; + using UpdateKind = typename DomTreeT::UpdateKind; + struct BatchUpdateInfo { + // Note: Updates inside PreViewCFG are already legalized. + BatchUpdateInfo(GraphDiffT &PreViewCFG, GraphDiffT *PostViewCFG = nullptr) + : PreViewCFG(PreViewCFG), PostViewCFG(PostViewCFG), + NumLegalized(PreViewCFG.getNumLegalizedUpdates()) {} + + // Remembers if the whole tree was recalculated at some point during the + // current batch update. + bool IsRecalculated = false; + GraphDiffT &PreViewCFG; + GraphDiffT *PostViewCFG; + const size_t NumLegalized; + }; + + 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 Inversed> + static SmallVector<NodePtr, 8> getChildren(NodePtr N, BatchUpdatePtr BUI) { + if (BUI) + return BUI->PreViewCFG.template getChildren<Inversed>(N); + return getChildren<Inversed>(N); + } + + template <bool Inversed> + static SmallVector<NodePtr, 8> getChildren(NodePtr N) { + using DirectedNodeT = + std::conditional_t<Inversed, Inverse<NodePtr>, NodePtr>; + auto R = children<DirectedNodeT>(N); + SmallVector<NodePtr, 8> Res(detail::reverse_if<!Inversed>(R)); + + // Remove nullptr children for clang. + llvm::erase_value(Res, nullptr); + return Res; + } + + NodePtr getIDom(NodePtr BB) const { + auto InfoIt = NodeToInfo.find(BB); + if (InfoIt == NodeToInfo.end()) return nullptr; + + return InfoIt->second.IDom; + } + + TreeNodePtr getNodeForBlock(NodePtr BB, DomTreeT &DT) { + if (TreeNodePtr Node = DT.getNode(BB)) return Node; + + // Haven't calculated this node yet? Get or calculate the node for the + // immediate dominator. + NodePtr IDom = getIDom(BB); + + assert(IDom || DT.DomTreeNodes[nullptr]); + TreeNodePtr IDomNode = getNodeForBlock(IDom, DT); + + // Add a new tree node for this NodeT, and link it as a child of + // IDomNode + return DT.createChild(BB, IDomNode); + } + + static bool AlwaysDescend(NodePtr, NodePtr) { return true; } + + struct BlockNamePrinter { + NodePtr N; + + BlockNamePrinter(NodePtr Block) : N(Block) {} + BlockNamePrinter(TreeNodePtr TN) : N(TN ? TN->getBlock() : nullptr) {} + + friend raw_ostream &operator<<(raw_ostream &O, const BlockNamePrinter &BP) { + if (!BP.N) + O << "nullptr"; + else + BP.N->printAsOperand(O, false); + + return O; + } + }; + + using NodeOrderMap = DenseMap<NodePtr, unsigned>; + + // 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. + // + // 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. + // + // If SuccOrder is specified then in this order the DFS traverses the children + // otherwise the order is implied by the results of getChildren(). + template <bool IsReverse = false, typename DescendCondition> + unsigned runDFS(NodePtr V, unsigned LastNum, DescendCondition Condition, + unsigned AttachToNum, + const NodeOrderMap *SuccOrder = nullptr) { + assert(V); + SmallVector<NodePtr, 64> WorkList = {V}; + if (NodeToInfo.count(V) != 0) NodeToInfo[V].Parent = AttachToNum; + + while (!WorkList.empty()) { + const NodePtr BB = WorkList.pop_back_val(); + auto &BBInfo = NodeToInfo[BB]; + + // Visited nodes always have positive DFS numbers. + if (BBInfo.DFSNum != 0) continue; + BBInfo.DFSNum = BBInfo.Semi = ++LastNum; + BBInfo.Label = BB; + NumToNode.push_back(BB); + + constexpr bool Direction = IsReverse != IsPostDom; // XOR. + auto Successors = getChildren<Direction>(BB, BatchUpdates); + if (SuccOrder && Successors.size() > 1) + llvm::sort( + Successors.begin(), Successors.end(), [=](NodePtr A, NodePtr B) { + return SuccOrder->find(A)->second < SuccOrder->find(B)->second; + }); + + for (const NodePtr Succ : Successors) { + const auto SIT = NodeToInfo.find(Succ); + // Don't visit nodes more than once but remember to collect + // ReverseChildren. + if (SIT != NodeToInfo.end() && SIT->second.DFSNum != 0) { + if (Succ != BB) SIT->second.ReverseChildren.push_back(BB); + continue; + } + + if (!Condition(BB, Succ)) continue; + + // It's fine to add Succ to the map, because we know that it will be + // visited later. + auto &SuccInfo = NodeToInfo[Succ]; + WorkList.push_back(Succ); + SuccInfo.Parent = LastNum; + SuccInfo.ReverseChildren.push_back(BB); + } + } + + return LastNum; + } + + // V is a predecessor of W. eval() returns V if V < W, otherwise the minimum + // of sdom(U), where U > W and there is a virtual forest path from U to V. The + // virtual forest consists of linked edges of processed vertices. + // + // We can follow Parent pointers (virtual forest edges) to determine the + // ancestor U with minimum sdom(U). But it is slow and thus we employ the path + // compression technique to speed up to O(m*log(n)). Theoretically the virtual + // forest can be organized as balanced trees to achieve almost linear + // O(m*alpha(m,n)) running time. But it requires two auxiliary arrays (Size + // and Child) and is unlikely to be faster than the simple implementation. + // + // For each vertex V, its Label points to the vertex with the minimal sdom(U) + // (Semi) in its path from V (included) to NodeToInfo[V].Parent (excluded). + NodePtr eval(NodePtr V, unsigned LastLinked, + SmallVectorImpl<InfoRec *> &Stack) { + InfoRec *VInfo = &NodeToInfo[V]; + if (VInfo->Parent < LastLinked) + return VInfo->Label; + + // Store ancestors except the last (root of a virtual tree) into a stack. + assert(Stack.empty()); + do { + Stack.push_back(VInfo); + VInfo = &NodeToInfo[NumToNode[VInfo->Parent]]; + } while (VInfo->Parent >= LastLinked); + + // Path compression. Point each vertex's Parent to the root and update its + // Label if any of its ancestors (PInfo->Label) has a smaller Semi. + const InfoRec *PInfo = VInfo; + const InfoRec *PLabelInfo = &NodeToInfo[PInfo->Label]; + do { + VInfo = Stack.pop_back_val(); + VInfo->Parent = PInfo->Parent; + const InfoRec *VLabelInfo = &NodeToInfo[VInfo->Label]; + if (PLabelInfo->Semi < VLabelInfo->Semi) + VInfo->Label = PInfo->Label; + else + PLabelInfo = VLabelInfo; + PInfo = VInfo; + } while (!Stack.empty()); + return VInfo->Label; + } + + // This function requires DFS to be run before calling it. + void runSemiNCA(DomTreeT &DT, const unsigned MinLevel = 0) { + const unsigned NextDFSNum(NumToNode.size()); + // Initialize IDoms to spanning tree parents. + for (unsigned i = 1; i < NextDFSNum; ++i) { + const NodePtr V = NumToNode[i]; + auto &VInfo = NodeToInfo[V]; + VInfo.IDom = NumToNode[VInfo.Parent]; + } + + // Step #1: Calculate the semidominators of all vertices. + SmallVector<InfoRec *, 32> EvalStack; + for (unsigned i = NextDFSNum - 1; i >= 2; --i) { + NodePtr W = NumToNode[i]; + auto &WInfo = NodeToInfo[W]; + + // Initialize the semi dominator to point to the parent node. + WInfo.Semi = WInfo.Parent; + for (const auto &N : WInfo.ReverseChildren) { + if (NodeToInfo.count(N) == 0) // Skip unreachable predecessors. + continue; + + const TreeNodePtr TN = DT.getNode(N); + // Skip predecessors whose level is above the subtree we are processing. + if (TN && TN->getLevel() < MinLevel) + continue; + + unsigned SemiU = NodeToInfo[eval(N, i + 1, EvalStack)].Semi; + if (SemiU < WInfo.Semi) WInfo.Semi = SemiU; + } + } + + // Step #2: Explicitly define the immediate dominator of each vertex. + // IDom[i] = NCA(SDom[i], SpanningTreeParent(i)). + // Note that the parents were stored in IDoms and later got invalidated + // during path compression in Eval. + for (unsigned i = 2; i < NextDFSNum; ++i) { + const NodePtr W = NumToNode[i]; + auto &WInfo = NodeToInfo[W]; + const unsigned SDomNum = NodeToInfo[NumToNode[WInfo.Semi]].DFSNum; + NodePtr WIDomCandidate = WInfo.IDom; + while (NodeToInfo[WIDomCandidate].DFSNum > SDomNum) + WIDomCandidate = NodeToInfo[WIDomCandidate].IDom; + + WInfo.IDom = WIDomCandidate; + } + } + + // 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"); + + auto &BBInfo = NodeToInfo[nullptr]; + BBInfo.DFSNum = BBInfo.Semi = 1; + BBInfo.Label = nullptr; + + 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 !getChildren<false>(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; + } + + SemiNCAInfo SNCA(BUI); + + // PostDominatorTree always has a virtual root. + SNCA.addVirtualRoot(); + unsigned Num = 1; + + LLVM_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); + LLVM_DEBUG(dbgs() << "Found a new trivial root: " << BlockNamePrinter(N) + << "\n"); + LLVM_DEBUG(dbgs() << "Last visited node: " + << BlockNamePrinter(SNCA.NumToNode[Num]) << "\n"); + } + } + + LLVM_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; + + // SuccOrder is the order of blocks in the function. It is needed to make + // the calculation of the FurthestAway node and the whole PostDomTree + // immune to swap successors transformation (e.g. canonicalizing branch + // predicates). SuccOrder is initialized lazily only for successors of + // reverse unreachable nodes. + std::optional<NodeOrderMap> SuccOrder; + auto InitSuccOrderOnce = [&]() { + SuccOrder = NodeOrderMap(); + for (const auto Node : nodes(DT.Parent)) + if (SNCA.NodeToInfo.count(Node) == 0) + for (const auto Succ : getChildren<false>(Node, SNCA.BatchUpdates)) + SuccOrder->try_emplace(Succ, 0); + + // Add mapping for all entries of SuccOrder. + unsigned NodeNum = 0; + for (const auto Node : nodes(DT.Parent)) { + ++NodeNum; + auto Order = SuccOrder->find(Node); + if (Order != SuccOrder->end()) { + assert(Order->second == 0); + Order->second = NodeNum; + } + } + }; + + // 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. + for (const NodePtr I : nodes(DT.Parent)) { + if (SNCA.NodeToInfo.count(I) == 0) { + LLVM_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. + LLVM_DEBUG(dbgs() << "\t\t\tRunning forward DFS\n"); + + if (!SuccOrder) + InitSuccOrderOnce(); + assert(SuccOrder); + + const unsigned NewNum = + SNCA.runDFS<true>(I, Num, AlwaysDescend, Num, &*SuccOrder); + const NodePtr FurthestAway = SNCA.NumToNode[NewNum]; + LLVM_DEBUG(dbgs() << "\t\t\tFound a new furthest away node " + << "(non-trivial root): " + << BlockNamePrinter(FurthestAway) << "\n"); + Roots.push_back(FurthestAway); + LLVM_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]; + LLVM_DEBUG(dbgs() << "\t\t\t\tRemoving DFS info for " + << BlockNamePrinter(N) << "\n"); + SNCA.NodeToInfo.erase(N); + SNCA.NumToNode.pop_back(); + } + const unsigned PrevNum = Num; + LLVM_DEBUG(dbgs() << "\t\t\tRunning reverse DFS\n"); + Num = SNCA.runDFS(FurthestAway, Num, AlwaysDescend, 1); + for (unsigned i = PrevNum + 1; i <= Num; ++i) + LLVM_DEBUG(dbgs() << "\t\t\t\tfound node " + << BlockNamePrinter(SNCA.NumToNode[i]) << "\n"); + } + } + } + + LLVM_DEBUG(dbgs() << "Total: " << Total << ", Num: " << Num << "\n"); + LLVM_DEBUG(dbgs() << "Discovered CFG nodes:\n"); + LLVM_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); + + LLVM_DEBUG(dbgs() << "Found roots: "); + LLVM_DEBUG(for (auto *Root + : Roots) dbgs() + << BlockNamePrinter(Root) << " "); + LLVM_DEBUG(dbgs() << "\n"); + + return Roots; + } + + // 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"); + LLVM_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; + LLVM_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::is_contained(Roots, N)) { + LLVM_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; + // If the update is using the actual CFG, BUI is null. If it's using a view, + // BUI is non-null and the PreCFGView is used. When calculating from + // scratch, make the PreViewCFG equal to the PostCFGView, so Post is used. + BatchUpdatePtr PostViewBUI = nullptr; + if (BUI && BUI->PostViewCFG) { + BUI->PreViewCFG = *BUI->PostViewCFG; + PostViewBUI = BUI; + } + // This is rebuilding the whole tree, not incrementally, but PostViewBUI is + // used in case the caller needs a DT update with a CFGView. + SemiNCAInfo SNCA(PostViewBUI); + + // Step #0: Number blocks in depth-first order and initialize variables used + // in later stages of the algorithm. + DT.Roots = FindRoots(DT, PostViewBUI); + SNCA.doFullDFSWalk(DT, AlwaysDescend); + + SNCA.runSemiNCA(DT); + if (BUI) { + BUI->IsRecalculated = true; + LLVM_DEBUG( + dbgs() << "DomTree recalculated, skipping future batch updates\n"); + } + + if (DT.Roots.empty()) return; + + // 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.createNode(Root); + SNCA.attachNewSubtree(DT, DT.RootNode); + } + + void attachNewSubtree(DomTreeT& DT, const TreeNodePtr AttachTo) { + // Attach the first unreachable block to AttachTo. + NodeToInfo[NumToNode[1]].IDom = AttachTo->getBlock(); + // Loop over all of the discovered blocks in the function... + for (size_t i = 1, e = NumToNode.size(); i != e; ++i) { + NodePtr W = NumToNode[i]; + + // Don't replace this with 'count', the insertion side effect is important + if (DT.DomTreeNodes[W]) continue; // Haven't calculated this node yet? + + NodePtr ImmDom = getIDom(W); + + // 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. + DT.createChild(W, IDomNode); + } + } + + void reattachExistingSubtree(DomTreeT &DT, const TreeNodePtr AttachTo) { + NodeToInfo[NumToNode[1]].IDom = AttachTo->getBlock(); + for (size_t i = 1, e = NumToNode.size(); i != e; ++i) { + const NodePtr N = NumToNode[i]; + const TreeNodePtr TN = DT.getNode(N); + assert(TN); + const TreeNodePtr NewIDom = DT.getNode(NodeToInfo[N].IDom); + TN->setIDom(NewIDom); + } + } + + // Helper struct used during edge insertions. + struct InsertionInfo { + struct Compare { + bool operator()(TreeNodePtr LHS, TreeNodePtr RHS) const { + return LHS->getLevel() < RHS->getLevel(); + } + }; + + // Bucket queue of tree nodes ordered by descending level. For simplicity, + // we use a priority_queue here. + std::priority_queue<TreeNodePtr, SmallVector<TreeNodePtr, 8>, + Compare> + Bucket; + SmallDenseSet<TreeNodePtr, 8> Visited; + SmallVector<TreeNodePtr, 8> Affected; +#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS + SmallVector<TreeNodePtr, 8> VisitedUnaffected; +#endif + }; + + 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"); + LLVM_DEBUG(dbgs() << "Inserting edge " << BlockNamePrinter(From) << " -> " + << BlockNamePrinter(To) << "\n"); + 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.createChild(From, VirtualRoot); + DT.Roots.push_back(From); + } + + DT.DFSInfoValid = false; + + const TreeNodePtr ToTN = DT.getNode(To); + if (!ToTN) + InsertUnreachable(DT, BUI, FromTN, To); + else + 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; + + if (!llvm::is_contained(DT.Roots, To->getBlock())) + return false; // To is not a root, nothing to update. + + LLVM_DEBUG(dbgs() << "\t\tAfter the insertion, " << BlockNamePrinter(To) + << " is no longer a root\n\t\tRebuilding the tree!!!\n"); + + CalculateFromScratch(DT, BUI); + return true; + } + + static bool isPermutation(const SmallVectorImpl<NodePtr> &A, + const SmallVectorImpl<NodePtr> &B) { + if (A.size() != B.size()) + return false; + SmallPtrSet<NodePtr, 4> Set(A.begin(), A.end()); + for (NodePtr N : B) + if (Set.count(N) == 0) + return false; + 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 (llvm::none_of(DT.Roots, [BUI](const NodePtr N) { + return HasForwardSuccessors(N, BUI); + })) + return; + + // Recalculate the set of roots. + RootsT Roots = FindRoots(DT, BUI); + if (!isPermutation(DT.Roots, Roots)) { + // The roots chosen in the CFG have changed. 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 node within an + // infinite loop becomes a root. + + LLVM_DEBUG(dbgs() << "Roots are different in updated trees\n" + << "The entire tree needs to be rebuilt\n"); + // It may be possible to update the tree without recalculating it, but + // we do not know yet how to do it, and it happens rarely in practice. + CalculateFromScratch(DT, BUI); + } + } + + // Handles insertion to a node already in the dominator tree. + static void InsertReachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr From, const TreeNodePtr To) { + LLVM_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 = + (From->getBlock() && To->getBlock()) + ? DT.findNearestCommonDominator(From->getBlock(), To->getBlock()) + : nullptr; + assert(NCDBlock || DT.isPostDominator()); + const TreeNodePtr NCD = DT.getNode(NCDBlock); + assert(NCD); + + LLVM_DEBUG(dbgs() << "\t\tNCA == " << BlockNamePrinter(NCD) << "\n"); + const unsigned NCDLevel = NCD->getLevel(); + + // Based on Lemma 2.5 from [2], after insertion of (From,To), v is affected + // iff depth(NCD)+1 < depth(v) && a path P from To to v exists where every + // w on P s.t. depth(v) <= depth(w) + // + // This reduces to a widest path problem (maximizing the depth of the + // minimum vertex in the path) which can be solved by a modified version of + // Dijkstra with a bucket queue (named depth-based search in [2]). + + // To is in the path, so depth(NCD)+1 < depth(v) <= depth(To). Nothing + // affected if this does not hold. + if (NCDLevel + 1 >= To->getLevel()) + return; + + InsertionInfo II; + SmallVector<TreeNodePtr, 8> UnaffectedOnCurrentLevel; + II.Bucket.push(To); + II.Visited.insert(To); + + while (!II.Bucket.empty()) { + TreeNodePtr TN = II.Bucket.top(); + II.Bucket.pop(); + II.Affected.push_back(TN); + + const unsigned CurrentLevel = TN->getLevel(); + LLVM_DEBUG(dbgs() << "Mark " << BlockNamePrinter(TN) << + "as affected, CurrentLevel " << CurrentLevel << "\n"); + + assert(TN->getBlock() && II.Visited.count(TN) && "Preconditions!"); + + while (true) { + // Unlike regular Dijkstra, we have an inner loop to expand more + // vertices. The first iteration is for the (affected) vertex popped + // from II.Bucket and the rest are for vertices in + // UnaffectedOnCurrentLevel, which may eventually expand to affected + // vertices. + // + // Invariant: there is an optimal path from `To` to TN with the minimum + // depth being CurrentLevel. + for (const NodePtr Succ : getChildren<IsPostDom>(TN->getBlock(), BUI)) { + const TreeNodePtr SuccTN = DT.getNode(Succ); + assert(SuccTN && + "Unreachable successor found at reachable insertion"); + const unsigned SuccLevel = SuccTN->getLevel(); + + LLVM_DEBUG(dbgs() << "\tSuccessor " << BlockNamePrinter(Succ) + << ", level = " << SuccLevel << "\n"); + + // There is an optimal path from `To` to Succ with the minimum depth + // being min(CurrentLevel, SuccLevel). + // + // If depth(NCD)+1 < depth(Succ) is not satisfied, Succ is unaffected + // and no affected vertex may be reached by a path passing through it. + // Stop here. Also, Succ may be visited by other predecessors but the + // first visit has the optimal path. Stop if Succ has been visited. + if (SuccLevel <= NCDLevel + 1 || !II.Visited.insert(SuccTN).second) + continue; + + if (SuccLevel > CurrentLevel) { + // Succ is unaffected but it may (transitively) expand to affected + // vertices. Store it in UnaffectedOnCurrentLevel. + LLVM_DEBUG(dbgs() << "\t\tMarking visited not affected " + << BlockNamePrinter(Succ) << "\n"); + UnaffectedOnCurrentLevel.push_back(SuccTN); +#ifndef NDEBUG + II.VisitedUnaffected.push_back(SuccTN); +#endif + } else { + // The condition is satisfied (Succ is affected). Add Succ to the + // bucket queue. + LLVM_DEBUG(dbgs() << "\t\tAdd " << BlockNamePrinter(Succ) + << " to a Bucket\n"); + II.Bucket.push(SuccTN); + } + } + + if (UnaffectedOnCurrentLevel.empty()) + break; + TN = UnaffectedOnCurrentLevel.pop_back_val(); + LLVM_DEBUG(dbgs() << " Next: " << BlockNamePrinter(TN) << "\n"); + } + } + + // Finish by updating immediate dominators and levels. + UpdateInsertion(DT, BUI, NCD, II); + } + + // Updates immediate dominators and levels after insertion. + static void UpdateInsertion(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr NCD, InsertionInfo &II) { + LLVM_DEBUG(dbgs() << "Updating NCD = " << BlockNamePrinter(NCD) << "\n"); + + for (const TreeNodePtr TN : II.Affected) { + LLVM_DEBUG(dbgs() << "\tIDom(" << BlockNamePrinter(TN) + << ") = " << BlockNamePrinter(NCD) << "\n"); + TN->setIDom(NCD); + } + +#if defined(LLVM_ENABLE_ABI_BREAKING_CHECKS) && !defined(NDEBUG) + for (const TreeNodePtr TN : II.VisitedUnaffected) + assert(TN->getLevel() == TN->getIDom()->getLevel() + 1 && + "TN should have been updated by an affected ancestor"); +#endif + + if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI); + } + + // Handles insertion to previously unreachable nodes. + static void InsertUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr From, const NodePtr To) { + LLVM_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, BUI, To, From, DiscoveredEdgesToReachable); + + LLVM_DEBUG(dbgs() << "Inserted " << BlockNamePrinter(From) + << " -> (prev unreachable) " << BlockNamePrinter(To) + << "\n"); + + // Used the discovered edges and inset discovered connecting (incoming) + // edges. + for (const auto &Edge : DiscoveredEdgesToReachable) { + LLVM_DEBUG(dbgs() << "\tInserting discovered connecting edge " + << BlockNamePrinter(Edge.first) << " -> " + << BlockNamePrinter(Edge.second) << "\n"); + InsertReachable(DT, BUI, DT.getNode(Edge.first), Edge.second); + } + } + + // Connects nodes that become reachable with an insertion. + static void ComputeUnreachableDominators( + DomTreeT &DT, const BatchUpdatePtr BUI, const NodePtr Root, + const TreeNodePtr Incoming, + SmallVectorImpl<std::pair<NodePtr, TreeNodePtr>> + &DiscoveredConnectingEdges) { + assert(!DT.getNode(Root) && "Root must not be reachable"); + + // Visit only previously unreachable nodes. + auto UnreachableDescender = [&DT, &DiscoveredConnectingEdges](NodePtr From, + NodePtr To) { + const TreeNodePtr ToTN = DT.getNode(To); + if (!ToTN) return true; + + DiscoveredConnectingEdges.push_back({From, ToTN}); + return false; + }; + + SemiNCAInfo SNCA(BUI); + SNCA.runDFS(Root, 0, UnreachableDescender, 0); + SNCA.runSemiNCA(DT); + SNCA.attachNewSubtree(DT, Incoming); + + LLVM_DEBUG(dbgs() << "After adding unreachable nodes\n"); + } + + static void DeleteEdge(DomTreeT &DT, const BatchUpdatePtr BUI, + const NodePtr From, const NodePtr To) { + assert(From && To && "Cannot disconnect nullptrs"); + LLVM_DEBUG(dbgs() << "Deleting edge " << BlockNamePrinter(From) << " -> " + << BlockNamePrinter(To) << "\n"); + +#ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS + // 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 = [BUI](const NodePtr SuccCandidate, const NodePtr Of) { + auto Successors = getChildren<IsPostDom>(Of, BUI); + return llvm::is_contained(Successors, SuccCandidate); + }; + (void)IsSuccessor; + assert(!IsSuccessor(To, From) && "Deleted edge still exists in the CFG!"); +#endif + + const TreeNodePtr FromTN = DT.getNode(From); + // Deletion in an unreachable subtree -- nothing to do. + if (!FromTN) return; + + const TreeNodePtr ToTN = DT.getNode(To); + if (!ToTN) { + LLVM_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); + + // If To dominates From -- nothing to do. + if (ToTN != NCD) { + DT.DFSInfoValid = false; + + const TreeNodePtr ToIDom = ToTN->getIDom(); + LLVM_DEBUG(dbgs() << "\tNCD " << BlockNamePrinter(NCD) << ", ToIDom " + << BlockNamePrinter(ToIDom) << "\n"); + + // To remains reachable after deletion. + // (Based on the caption under Figure 4. from [2].) + if (FromTN != ToIDom || HasProperSupport(DT, BUI, ToTN)) + DeleteReachable(DT, BUI, FromTN, ToTN); + else + DeleteUnreachable(DT, BUI, ToTN); + } + + if (IsPostDom) UpdateRootsAfterUpdate(DT, BUI); + } + + // Handles deletions that leave destination nodes reachable. + static void DeleteReachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr FromTN, + const TreeNodePtr ToTN) { + LLVM_DEBUG(dbgs() << "Deleting reachable " << BlockNamePrinter(FromTN) + << " -> " << BlockNamePrinter(ToTN) << "\n"); + LLVM_DEBUG(dbgs() << "\tRebuilding subtree\n"); + + // Find the top of the subtree that needs to be rebuilt. + // (Based on the lemma 2.6 from [2].) + const NodePtr ToIDom = + DT.findNearestCommonDominator(FromTN->getBlock(), ToTN->getBlock()); + assert(ToIDom || DT.isPostDominator()); + const TreeNodePtr ToIDomTN = DT.getNode(ToIDom); + assert(ToIDomTN); + const TreeNodePtr PrevIDomSubTree = ToIDomTN->getIDom(); + // Top of the subtree to rebuild is the root node. Rebuild the tree from + // scratch. + if (!PrevIDomSubTree) { + LLVM_DEBUG(dbgs() << "The entire tree needs to be rebuilt\n"); + CalculateFromScratch(DT, BUI); + return; + } + + // Only visit nodes in the subtree starting at To. + const unsigned Level = ToIDomTN->getLevel(); + auto DescendBelow = [Level, &DT](NodePtr, NodePtr To) { + return DT.getNode(To)->getLevel() > Level; + }; + + LLVM_DEBUG(dbgs() << "\tTop of subtree: " << BlockNamePrinter(ToIDomTN) + << "\n"); + + SemiNCAInfo SNCA(BUI); + SNCA.runDFS(ToIDom, 0, DescendBelow, 0); + LLVM_DEBUG(dbgs() << "\tRunning Semi-NCA\n"); + SNCA.runSemiNCA(DT, Level); + SNCA.reattachExistingSubtree(DT, PrevIDomSubTree); + } + + // Checks if a node has proper support, as defined on the page 3 and later + // explained on the page 7 of [2]. + static bool HasProperSupport(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr TN) { + LLVM_DEBUG(dbgs() << "IsReachableFromIDom " << BlockNamePrinter(TN) + << "\n"); + auto TNB = TN->getBlock(); + for (const NodePtr Pred : getChildren<!IsPostDom>(TNB, BUI)) { + LLVM_DEBUG(dbgs() << "\tPred " << BlockNamePrinter(Pred) << "\n"); + if (!DT.getNode(Pred)) continue; + + const NodePtr Support = DT.findNearestCommonDominator(TNB, Pred); + LLVM_DEBUG(dbgs() << "\tSupport " << BlockNamePrinter(Support) << "\n"); + if (Support != TNB) { + LLVM_DEBUG(dbgs() << "\t" << BlockNamePrinter(TN) + << " is reachable from support " + << BlockNamePrinter(Support) << "\n"); + return true; + } + } + + return false; + } + + // Handle deletions that make destination node unreachable. + // (Based on the lemma 2.7 from the [2].) + static void DeleteUnreachable(DomTreeT &DT, const BatchUpdatePtr BUI, + const TreeNodePtr ToTN) { + LLVM_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. + LLVM_DEBUG(dbgs() << "\tDeletion made a region reverse-unreachable\n"); + LLVM_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(); + + // Traverse destination node's descendants with greater level in the tree + // and collect visited nodes. + auto DescendAndCollect = [Level, &AffectedQueue, &DT](NodePtr, NodePtr To) { + const TreeNodePtr TN = DT.getNode(To); + assert(TN); + if (TN->getLevel() > Level) return true; + if (!llvm::is_contained(AffectedQueue, To)) + AffectedQueue.push_back(To); + + return false; + }; + + SemiNCAInfo SNCA(BUI); + unsigned LastDFSNum = + SNCA.runDFS(ToTN->getBlock(), 0, DescendAndCollect, 0); + + TreeNodePtr MinNode = ToTN; + + // 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); + const NodePtr NCDBlock = + DT.findNearestCommonDominator(TN->getBlock(), ToTN->getBlock()); + assert(NCDBlock || DT.isPostDominator()); + const TreeNodePtr NCD = DT.getNode(NCDBlock); + assert(NCD); + + LLVM_DEBUG(dbgs() << "Processing affected node " << BlockNamePrinter(TN) + << " with NCD = " << BlockNamePrinter(NCD) + << ", MinNode =" << BlockNamePrinter(MinNode) << "\n"); + if (NCD != TN && NCD->getLevel() < MinNode->getLevel()) MinNode = NCD; + } + + // Root reached, rebuild the whole tree from scratch. + if (!MinNode->getIDom()) { + LLVM_DEBUG(dbgs() << "The entire tree needs to be rebuilt\n"); + CalculateFromScratch(DT, BUI); + return; + } + + // Erase the unreachable subtree in reverse preorder to process all children + // before deleting their parent. + for (unsigned i = LastDFSNum; i > 0; --i) { + const NodePtr N = SNCA.NumToNode[i]; + const TreeNodePtr TN = DT.getNode(N); + LLVM_DEBUG(dbgs() << "Erasing node " << BlockNamePrinter(TN) << "\n"); + + EraseNode(DT, TN); + } + + // The affected subtree start at the To node -- there's no extra work to do. + if (MinNode == ToTN) return; + + LLVM_DEBUG(dbgs() << "DeleteUnreachable: running DFS with MinNode = " + << BlockNamePrinter(MinNode) << "\n"); + const unsigned MinLevel = MinNode->getLevel(); + const TreeNodePtr PrevIDom = MinNode->getIDom(); + assert(PrevIDom); + SNCA.clear(); + + // Identify nodes that remain in the affected subtree. + auto DescendBelow = [MinLevel, &DT](NodePtr, NodePtr To) { + const TreeNodePtr ToTN = DT.getNode(To); + return ToTN && ToTN->getLevel() > MinLevel; + }; + SNCA.runDFS(MinNode->getBlock(), 0, DescendBelow, 0); + + LLVM_DEBUG(dbgs() << "Previous IDom(MinNode) = " + << BlockNamePrinter(PrevIDom) << "\nRunning Semi-NCA\n"); + + // Rebuild the remaining part of affected subtree. + SNCA.runSemiNCA(DT, MinLevel); + SNCA.reattachExistingSubtree(DT, PrevIDom); + } + + // Removes leaf tree nodes from the dominator tree. + static void EraseNode(DomTreeT &DT, const TreeNodePtr TN) { + assert(TN); + assert(TN->getNumChildren() == 0 && "Not a tree leaf"); + + const TreeNodePtr IDom = TN->getIDom(); + assert(IDom); + + auto ChIt = llvm::find(IDom->Children, TN); + assert(ChIt != IDom->Children.end()); + std::swap(*ChIt, IDom->Children.back()); + IDom->Children.pop_back(); + + DT.DomTreeNodes.erase(TN->getBlock()); + } + + //~~ + //===--------------------- DomTree Batch Updater --------------------------=== + //~~ + + static void ApplyUpdates(DomTreeT &DT, GraphDiffT &PreViewCFG, + GraphDiffT *PostViewCFG) { + // Note: the PostViewCFG is only used when computing from scratch. It's data + // should already included in the PreViewCFG for incremental updates. + const size_t NumUpdates = PreViewCFG.getNumLegalizedUpdates(); + if (NumUpdates == 0) + return; + + // Take the fast path for a single update and avoid running the batch update + // machinery. + if (NumUpdates == 1) { + UpdateT Update = PreViewCFG.popUpdateForIncrementalUpdates(); + if (!PostViewCFG) { + if (Update.getKind() == UpdateKind::Insert) + InsertEdge(DT, /*BUI=*/nullptr, Update.getFrom(), Update.getTo()); + else + DeleteEdge(DT, /*BUI=*/nullptr, Update.getFrom(), Update.getTo()); + } else { + BatchUpdateInfo BUI(*PostViewCFG, PostViewCFG); + if (Update.getKind() == UpdateKind::Insert) + InsertEdge(DT, &BUI, Update.getFrom(), Update.getTo()); + else + DeleteEdge(DT, &BUI, Update.getFrom(), Update.getTo()); + } + return; + } + + BatchUpdateInfo BUI(PreViewCFG, PostViewCFG); + // Recalculate the DominatorTree when the number of updates + // exceeds a threshold, which usually makes direct updating slower than + // recalculation. We select this threshold proportional to the + // size of the DominatorTree. The constant is selected + // by choosing the one with an acceptable performance on some real-world + // inputs. + + // Make unittests of the incremental algorithm work + if (DT.DomTreeNodes.size() <= 100) { + if (BUI.NumLegalized > DT.DomTreeNodes.size()) + CalculateFromScratch(DT, &BUI); + } else if (BUI.NumLegalized > DT.DomTreeNodes.size() / 40) + CalculateFromScratch(DT, &BUI); + + // 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 < BUI.NumLegalized && !BUI.IsRecalculated; ++i) + ApplyNextUpdate(DT, BUI); + } + + static void ApplyNextUpdate(DomTreeT &DT, BatchUpdateInfo &BUI) { + // Popping the next update, will move the PreViewCFG to the next snapshot. + UpdateT CurrentUpdate = BUI.PreViewCFG.popUpdateForIncrementalUpdates(); +#if 0 + // FIXME: The LLVM_DEBUG macro only plays well with a modular + // build of LLVM when the header is marked as textual, but doing + // so causes redefinition errors. + LLVM_DEBUG(dbgs() << "Applying update: "); + LLVM_DEBUG(CurrentUpdate.dump(); dbgs() << "\n"); +#endif + + 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. + // Running time: O(N). + 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 (!isPermutation(DT.Roots, ComputedRoots)) { + 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. + // Running time: O(N). + 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. + // Running time: O(N). + static bool VerifyLevels(const DomTreeT &DT) { + for (auto &NodeToTN : DT.DomTreeNodes) { + const TreeNodePtr TN = NodeToTN.second.get(); + const NodePtr BB = TN->getBlock(); + if (!BB) continue; + + const TreeNodePtr IDom = TN->getIDom(); + if (!IDom && TN->getLevel() != 0) { + errs() << "Node without an IDom " << BlockNamePrinter(BB) + << " has a nonzero level " << TN->getLevel() << "!\n"; + errs().flush(); + + return false; + } + + if (IDom && TN->getLevel() != IDom->getLevel() + 1) { + errs() << "Node " << BlockNamePrinter(BB) << " has level " + << TN->getLevel() << " while its IDom " + << BlockNamePrinter(IDom->getBlock()) << " has level " + << IDom->getLevel() << "!\n"; + errs().flush(); + + return false; + } + } + + return true; + } + + // 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. + // Running time: O(N log(N)). + static bool VerifyDFSNumbers(const DomTreeT &DT) { + if (!DT.DFSInfoValid || !DT.Parent) + return true; + + const NodePtr RootBB = IsPostDom ? nullptr : *DT.root_begin(); + const TreeNodePtr Root = DT.getNode(RootBB); + + auto PrintNodeAndDFSNums = [](const TreeNodePtr TN) { + errs() << BlockNamePrinter(TN) << " {" << TN->getDFSNumIn() << ", " + << TN->getDFSNumOut() << '}'; + }; + + // 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->isLeaf()) { + 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()); + llvm::sort(Children, [](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; + } + + // The below routines verify the correctness of the dominator tree relative to + // the CFG it's coming from. A tree is a dominator tree iff it has two + // properties, called the parent property and the sibling property. Tarjan + // and Lengauer prove (but don't explicitly name) the properties as part of + // the proofs in their 1972 paper, but the proofs are mostly part of proving + // things about semidominators and idoms, and some of them are simply asserted + // based on even earlier papers (see, e.g., lemma 2). Some papers refer to + // these properties as "valid" and "co-valid". See, e.g., "Dominators, + // directed bipolar orders, and independent spanning trees" by Loukas + // Georgiadis and Robert E. Tarjan, as well as "Dominator Tree Verification + // and Vertex-Disjoint Paths " by the same authors. + + // A very simple and direct explanation of these properties can be found in + // "An Experimental Study of Dynamic Dominators", found at + // https://arxiv.org/abs/1604.02711 + + // The easiest way to think of the parent property is that it's a requirement + // of being a dominator. Let's just take immediate dominators. For PARENT to + // be an immediate dominator of CHILD, all paths in the CFG must go through + // PARENT before they hit CHILD. This implies that if you were to cut PARENT + // out of the CFG, there should be no paths to CHILD that are reachable. If + // there are, then you now have a path from PARENT to CHILD that goes around + // PARENT and still reaches CHILD, which by definition, means PARENT can't be + // a dominator of CHILD (let alone an immediate one). + + // The sibling property is similar. It says that for each pair of sibling + // nodes in the dominator tree (LEFT and RIGHT) , they must not dominate each + // other. If sibling LEFT dominated sibling RIGHT, it means there are no + // paths in the CFG from sibling LEFT to sibling RIGHT that do not go through + // LEFT, and thus, LEFT is really an ancestor (in the dominator tree) of + // RIGHT, not a sibling. + + // It is possible to verify the parent and sibling properties in linear time, + // but the algorithms are complex. Instead, we do it in a straightforward + // N^2 and N^3 way below, using direct path reachability. + + // Checks if the tree has the parent property: if for all edges from V to W in + // the input graph, such that V is reachable, the parent of W in the tree is + // an ancestor of V in the tree. + // Running time: O(N^2). + // + // This means that if a node gets disconnected from the graph, then all of + // the nodes it dominated previously will now become unreachable. + bool verifyParentProperty(const DomTreeT &DT) { + for (auto &NodeToTN : DT.DomTreeNodes) { + const TreeNodePtr TN = NodeToTN.second.get(); + const NodePtr BB = TN->getBlock(); + if (!BB || TN->isLeaf()) + continue; + + LLVM_DEBUG(dbgs() << "Verifying parent property of node " + << BlockNamePrinter(TN) << "\n"); + clear(); + doFullDFSWalk(DT, [BB](NodePtr From, NodePtr To) { + return From != BB && To != BB; + }); + + for (TreeNodePtr Child : TN->children()) + if (NodeToInfo.count(Child->getBlock()) != 0) { + errs() << "Child " << BlockNamePrinter(Child) + << " reachable after its parent " << BlockNamePrinter(BB) + << " is removed!\n"; + errs().flush(); + + return false; + } + } + + return true; + } + + // Check if the tree has sibling property: if a node V does not dominate a + // node W for all siblings V and W in the tree. + // Running time: O(N^3). + // + // This means that if a node gets disconnected from the graph, then all of its + // siblings will now still be reachable. + bool verifySiblingProperty(const DomTreeT &DT) { + for (auto &NodeToTN : DT.DomTreeNodes) { + const TreeNodePtr TN = NodeToTN.second.get(); + const NodePtr BB = TN->getBlock(); + if (!BB || TN->isLeaf()) + continue; + + for (const TreeNodePtr N : TN->children()) { + clear(); + NodePtr BBN = N->getBlock(); + doFullDFSWalk(DT, [BBN](NodePtr From, NodePtr To) { + return From != BBN && To != BBN; + }); + + for (const TreeNodePtr S : TN->children()) { + if (S == N) continue; + + if (NodeToInfo.count(S->getBlock()) == 0) { + errs() << "Node " << BlockNamePrinter(S) + << " not reachable when its sibling " << BlockNamePrinter(N) + << " is removed!\n"; + errs().flush(); + + return false; + } + } + } + } + + return true; + } + + // Check if the given tree is the same as a freshly computed one for the same + // Parent. + // Running time: O(N^2), but faster in practice (same as tree construction). + // + // Note that this does not check if that the tree construction algorithm is + // correct and should be only used for fast (but possibly unsound) + // verification. + static bool IsSameAsFreshTree(const DomTreeT &DT) { + DomTreeT FreshTree; + FreshTree.recalculate(*DT.Parent); + const bool Different = DT.compare(FreshTree); + + if (Different) { + errs() << (DT.isPostDominator() ? "Post" : "") + << "DominatorTree is different than a freshly computed one!\n" + << "\tCurrent:\n"; + DT.print(errs()); + errs() << "\n\tFreshly computed tree:\n"; + FreshTree.print(errs()); + errs().flush(); + } + + return !Different; + } +}; + +template <class DomTreeT> +void Calculate(DomTreeT &DT) { + SemiNCAInfo<DomTreeT>::CalculateFromScratch(DT, nullptr); +} + +template <typename DomTreeT> +void CalculateWithUpdates(DomTreeT &DT, + ArrayRef<typename DomTreeT::UpdateType> Updates) { + // FIXME: Updated to use the PreViewCFG and behave the same as until now. + // This behavior is however incorrect; this actually needs the PostViewCFG. + GraphDiff<typename DomTreeT::NodePtr, DomTreeT::IsPostDominator> PreViewCFG( + Updates, /*ReverseApplyUpdates=*/true); + typename SemiNCAInfo<DomTreeT>::BatchUpdateInfo BUI(PreViewCFG); + SemiNCAInfo<DomTreeT>::CalculateFromScratch(DT, &BUI); +} + +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, 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, nullptr, From, To); +} + +template <class DomTreeT> +void ApplyUpdates(DomTreeT &DT, + GraphDiff<typename DomTreeT::NodePtr, + DomTreeT::IsPostDominator> &PreViewCFG, + GraphDiff<typename DomTreeT::NodePtr, + DomTreeT::IsPostDominator> *PostViewCFG) { + SemiNCAInfo<DomTreeT>::ApplyUpdates(DT, PreViewCFG, PostViewCFG); +} + +template <class DomTreeT> +bool Verify(const DomTreeT &DT, typename DomTreeT::VerificationLevel VL) { + SemiNCAInfo<DomTreeT> SNCA(nullptr); + + // Simplist check is to compare against a new tree. This will also + // usefully print the old and new trees, if they are different. + if (!SNCA.IsSameAsFreshTree(DT)) + return false; + + // Common checks to verify the properties of the tree. O(N log N) at worst. + if (!SNCA.verifyRoots(DT) || !SNCA.verifyReachability(DT) || + !SNCA.VerifyLevels(DT) || !SNCA.VerifyDFSNumbers(DT)) + return false; + + // Extra checks depending on VerificationLevel. Up to O(N^3). + if (VL == DomTreeT::VerificationLevel::Basic || + VL == DomTreeT::VerificationLevel::Full) + if (!SNCA.verifyParentProperty(DT)) + return false; + if (VL == DomTreeT::VerificationLevel::Full) + if (!SNCA.verifySiblingProperty(DT)) + return false; + + return true; +} + +} // namespace DomTreeBuilder +} // namespace llvm + +#undef DEBUG_TYPE + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/GenericIteratedDominanceFrontier.h b/contrib/libs/llvm16/include/llvm/Support/GenericIteratedDominanceFrontier.h new file mode 100644 index 00000000000..ef576216fe5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/GenericIteratedDominanceFrontier.h @@ -0,0 +1,220 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- IteratedDominanceFrontier.h - Calculate IDF --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// Compute iterated dominance frontiers using a linear time algorithm. +/// +/// The algorithm used here is based on: +/// +/// Sreedhar and Gao. A linear time algorithm for placing phi-nodes. +/// In Proceedings of the 22nd ACM SIGPLAN-SIGACT Symposium on Principles of +/// Programming Languages +/// POPL '95. ACM, New York, NY, 62-73. +/// +/// It has been modified to not explicitly use the DJ graph data structure and +/// to directly compute pruned SSA using per-variable liveness information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_GENERICITERATEDDOMINANCEFRONTIER_H +#define LLVM_SUPPORT_GENERICITERATEDDOMINANCEFRONTIER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/GenericDomTree.h" +#include <queue> + +namespace llvm { + +namespace IDFCalculatorDetail { + +/// Generic utility class used for getting the children of a basic block. +/// May be specialized if, for example, one wouldn't like to return nullpointer +/// successors. +template <class NodeTy, bool IsPostDom> struct ChildrenGetterTy { + using NodeRef = typename GraphTraits<NodeTy *>::NodeRef; + using ChildrenTy = SmallVector<NodeRef, 8>; + + ChildrenTy get(const NodeRef &N); +}; + +} // end of namespace IDFCalculatorDetail + +/// Determine the iterated dominance frontier, given a set of defining +/// blocks, and optionally, a set of live-in blocks. +/// +/// In turn, the results can be used to place phi nodes. +/// +/// This algorithm is a linear time computation of Iterated Dominance Frontiers, +/// pruned using the live-in set. +/// By default, liveness is not used to prune the IDF computation. +/// The template parameters should be of a CFG block type. +template <class NodeTy, bool IsPostDom> class IDFCalculatorBase { +public: + using OrderedNodeTy = + std::conditional_t<IsPostDom, Inverse<NodeTy *>, NodeTy *>; + using ChildrenGetterTy = + IDFCalculatorDetail::ChildrenGetterTy<NodeTy, IsPostDom>; + + IDFCalculatorBase(DominatorTreeBase<NodeTy, IsPostDom> &DT) : DT(DT) {} + + IDFCalculatorBase(DominatorTreeBase<NodeTy, IsPostDom> &DT, + const ChildrenGetterTy &C) + : DT(DT), ChildrenGetter(C) {} + + /// Give the IDF calculator the set of blocks in which the value is + /// defined. This is equivalent to the set of starting blocks it should be + /// calculating the IDF for (though later gets pruned based on liveness). + /// + /// Note: This set *must* live for the entire lifetime of the IDF calculator. + void setDefiningBlocks(const SmallPtrSetImpl<NodeTy *> &Blocks) { + DefBlocks = &Blocks; + } + + /// Give the IDF calculator the set of blocks in which the value is + /// live on entry to the block. This is used to prune the IDF calculation to + /// not include blocks where any phi insertion would be dead. + /// + /// Note: This set *must* live for the entire lifetime of the IDF calculator. + void setLiveInBlocks(const SmallPtrSetImpl<NodeTy *> &Blocks) { + LiveInBlocks = &Blocks; + useLiveIn = true; + } + + /// Reset the live-in block set to be empty, and tell the IDF + /// calculator to not use liveness anymore. + void resetLiveInBlocks() { + LiveInBlocks = nullptr; + useLiveIn = false; + } + + /// Calculate iterated dominance frontiers + /// + /// This uses the linear-time phi algorithm based on DJ-graphs mentioned in + /// the file-level comment. It performs DF->IDF pruning using the live-in + /// set, to avoid computing the IDF for blocks where an inserted PHI node + /// would be dead. + void calculate(SmallVectorImpl<NodeTy *> &IDFBlocks); + +private: + DominatorTreeBase<NodeTy, IsPostDom> &DT; + ChildrenGetterTy ChildrenGetter; + bool useLiveIn = false; + const SmallPtrSetImpl<NodeTy *> *LiveInBlocks; + const SmallPtrSetImpl<NodeTy *> *DefBlocks; +}; + +//===----------------------------------------------------------------------===// +// Implementation. +//===----------------------------------------------------------------------===// + +namespace IDFCalculatorDetail { + +template <class NodeTy, bool IsPostDom> +typename ChildrenGetterTy<NodeTy, IsPostDom>::ChildrenTy +ChildrenGetterTy<NodeTy, IsPostDom>::get(const NodeRef &N) { + using OrderedNodeTy = + typename IDFCalculatorBase<NodeTy, IsPostDom>::OrderedNodeTy; + + auto Children = children<OrderedNodeTy>(N); + return {Children.begin(), Children.end()}; +} + +} // end of namespace IDFCalculatorDetail + +template <class NodeTy, bool IsPostDom> +void IDFCalculatorBase<NodeTy, IsPostDom>::calculate( + SmallVectorImpl<NodeTy *> &IDFBlocks) { + // Use a priority queue keyed on dominator tree level so that inserted nodes + // are handled from the bottom of the dominator tree upwards. We also augment + // the level with a DFS number to ensure that the blocks are ordered in a + // deterministic way. + using DomTreeNodePair = + std::pair<DomTreeNodeBase<NodeTy> *, std::pair<unsigned, unsigned>>; + using IDFPriorityQueue = + std::priority_queue<DomTreeNodePair, SmallVector<DomTreeNodePair, 32>, + less_second>; + + IDFPriorityQueue PQ; + + DT.updateDFSNumbers(); + + SmallVector<DomTreeNodeBase<NodeTy> *, 32> Worklist; + SmallPtrSet<DomTreeNodeBase<NodeTy> *, 32> VisitedPQ; + SmallPtrSet<DomTreeNodeBase<NodeTy> *, 32> VisitedWorklist; + + for (NodeTy *BB : *DefBlocks) + if (DomTreeNodeBase<NodeTy> *Node = DT.getNode(BB)) { + PQ.push({Node, std::make_pair(Node->getLevel(), Node->getDFSNumIn())}); + VisitedWorklist.insert(Node); + } + + while (!PQ.empty()) { + DomTreeNodePair RootPair = PQ.top(); + PQ.pop(); + DomTreeNodeBase<NodeTy> *Root = RootPair.first; + unsigned RootLevel = RootPair.second.first; + + // Walk all dominator tree children of Root, inspecting their CFG edges with + // targets elsewhere on the dominator tree. Only targets whose level is at + // most Root's level are added to the iterated dominance frontier of the + // definition set. + + assert(Worklist.empty()); + Worklist.push_back(Root); + + while (!Worklist.empty()) { + DomTreeNodeBase<NodeTy> *Node = Worklist.pop_back_val(); + NodeTy *BB = Node->getBlock(); + // Succ is the successor in the direction we are calculating IDF, so it is + // successor for IDF, and predecessor for Reverse IDF. + auto DoWork = [&](NodeTy *Succ) { + DomTreeNodeBase<NodeTy> *SuccNode = DT.getNode(Succ); + + const unsigned SuccLevel = SuccNode->getLevel(); + if (SuccLevel > RootLevel) + return; + + if (!VisitedPQ.insert(SuccNode).second) + return; + + NodeTy *SuccBB = SuccNode->getBlock(); + if (useLiveIn && !LiveInBlocks->count(SuccBB)) + return; + + IDFBlocks.emplace_back(SuccBB); + if (!DefBlocks->count(SuccBB)) + PQ.push(std::make_pair( + SuccNode, std::make_pair(SuccLevel, SuccNode->getDFSNumIn()))); + }; + + for (auto *Succ : ChildrenGetter.get(BB)) + DoWork(Succ); + + for (auto DomChild : *Node) { + if (VisitedWorklist.insert(DomChild).second) + Worklist.push_back(DomChild); + } + } + } +} + +} // end of namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/GlobPattern.h b/contrib/libs/llvm16/include/llvm/Support/GlobPattern.h new file mode 100644 index 00000000000..de38921a796 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/GlobPattern.h @@ -0,0 +1,68 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- GlobPattern.h - glob pattern matcher implementation -*- C++ -*-----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a glob pattern matcher. The glob pattern is the +// rule used by the shell. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_GLOBPATTERN_H +#define LLVM_SUPPORT_GLOBPATTERN_H + +#include "llvm/ADT/BitVector.h" +#include "llvm/Support/Error.h" +#include <optional> +#include <vector> + +// This class represents a glob pattern. Supported metacharacters +// are "*", "?", "\", "[<chars>]", "[^<chars>]", and "[!<chars>]". +namespace llvm { + +template <typename T> class ArrayRef; +class StringRef; + +class GlobPattern { +public: + static Expected<GlobPattern> create(StringRef Pat); + bool match(StringRef S) const; + + // Returns true for glob pattern "*". Can be used to avoid expensive + // preparation/acquisition of the input for match(). + bool isTrivialMatchAll() const { + if (Prefix && Prefix->empty()) { + assert(!Suffix); + return true; + } + return false; + } + +private: + bool matchOne(ArrayRef<BitVector> Pat, StringRef S) const; + + // Parsed glob pattern. + std::vector<BitVector> Tokens; + + // The following members are for optimization. + std::optional<StringRef> Exact; + std::optional<StringRef> Prefix; + std::optional<StringRef> Suffix; +}; +} + +#endif // LLVM_SUPPORT_GLOBPATTERN_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/GraphWriter.h b/contrib/libs/llvm16/include/llvm/Support/GraphWriter.h new file mode 100644 index 00000000000..44ac83f0a5a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/GraphWriter.h @@ -0,0 +1,451 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/GraphWriter.h - Write graph to a .dot file --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a simple interface that can be used to print out generic +// LLVM graphs to ".dot" files. "dot" is a tool that is part of the AT&T +// graphviz package (http://www.research.att.com/sw/tools/graphviz/) which can +// be used to turn the files output by this interface into a variety of +// different graphics formats. +// +// Graphs do not need to implement any interface past what is already required +// by the GraphTraits template, but they can choose to implement specializations +// of the DOTGraphTraits template if they want to customize the graphs output in +// any way. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_GRAPHWRITER_H +#define LLVM_SUPPORT_GRAPHWRITER_H + +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <iterator> +#include <string> +#include <type_traits> +#include <vector> + +namespace llvm { + +namespace DOT { // Private functions... + +std::string EscapeString(const std::string &Label); + +/// Get a color string for this node number. Simply round-robin selects +/// from a reasonable number of colors. +StringRef getColorString(unsigned NodeNumber); + +} // end namespace DOT + +namespace GraphProgram { + +enum Name { + DOT, + FDP, + NEATO, + TWOPI, + CIRCO +}; + +} // end namespace GraphProgram + +bool DisplayGraph(StringRef Filename, bool wait = true, + GraphProgram::Name program = GraphProgram::DOT); + +template<typename GraphType> +class GraphWriter { + raw_ostream &O; + const GraphType &G; + bool RenderUsingHTML = false; + + using DOTTraits = DOTGraphTraits<GraphType>; + using GTraits = GraphTraits<GraphType>; + using NodeRef = typename GTraits::NodeRef; + using node_iterator = typename GTraits::nodes_iterator; + using child_iterator = typename GTraits::ChildIteratorType; + DOTTraits DTraits; + + static_assert(std::is_pointer<NodeRef>::value, + "FIXME: Currently GraphWriter requires the NodeRef type to be " + "a pointer.\nThe pointer usage should be moved to " + "DOTGraphTraits, and removed from GraphWriter itself."); + + // Writes the edge labels of the node to O and returns true if there are any + // edge labels not equal to the empty string "". + bool getEdgeSourceLabels(raw_ostream &O, NodeRef Node) { + child_iterator EI = GTraits::child_begin(Node); + child_iterator EE = GTraits::child_end(Node); + bool hasEdgeSourceLabels = false; + + if (RenderUsingHTML) + O << "</tr><tr>"; + + for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) { + std::string label = DTraits.getEdgeSourceLabel(Node, EI); + + if (label.empty()) + continue; + + hasEdgeSourceLabels = true; + + if (RenderUsingHTML) + O << "<td colspan=\"1\" port=\"s" << i << "\">" << label << "</td>"; + else { + if (i) + O << "|"; + + O << "<s" << i << ">" << DOT::EscapeString(label); + } + } + + if (EI != EE && hasEdgeSourceLabels) { + if (RenderUsingHTML) + O << "<td colspan=\"1\" port=\"s64\">truncated...</td>"; + else + O << "|<s64>truncated..."; + } + + return hasEdgeSourceLabels; + } + +public: + GraphWriter(raw_ostream &o, const GraphType &g, bool SN) : O(o), G(g) { + DTraits = DOTTraits(SN); + RenderUsingHTML = DTraits.renderNodesUsingHTML(); + } + + void writeGraph(const std::string &Title = "") { + // Output the header for the graph... + writeHeader(Title); + + // Emit all of the nodes in the graph... + writeNodes(); + + // Output any customizations on the graph + DOTGraphTraits<GraphType>::addCustomGraphFeatures(G, *this); + + // Output the end of the graph + writeFooter(); + } + + void writeHeader(const std::string &Title) { + std::string GraphName(DTraits.getGraphName(G)); + + if (!Title.empty()) + O << "digraph \"" << DOT::EscapeString(Title) << "\" {\n"; + else if (!GraphName.empty()) + O << "digraph \"" << DOT::EscapeString(GraphName) << "\" {\n"; + else + O << "digraph unnamed {\n"; + + if (DTraits.renderGraphFromBottomUp()) + O << "\trankdir=\"BT\";\n"; + + if (!Title.empty()) + O << "\tlabel=\"" << DOT::EscapeString(Title) << "\";\n"; + else if (!GraphName.empty()) + O << "\tlabel=\"" << DOT::EscapeString(GraphName) << "\";\n"; + O << DTraits.getGraphProperties(G); + O << "\n"; + } + + void writeFooter() { + // Finish off the graph + O << "}\n"; + } + + void writeNodes() { + // Loop over the graph, printing it out... + for (const auto Node : nodes<GraphType>(G)) + if (!isNodeHidden(Node)) + writeNode(Node); + } + + bool isNodeHidden(NodeRef Node) { return DTraits.isNodeHidden(Node, G); } + + void writeNode(NodeRef Node) { + std::string NodeAttributes = DTraits.getNodeAttributes(Node, G); + + O << "\tNode" << static_cast<const void *>(Node) << " [shape="; + if (RenderUsingHTML) + O << "none,"; + else + O << "record,"; + + if (!NodeAttributes.empty()) O << NodeAttributes << ","; + O << "label="; + + if (RenderUsingHTML) { + // Count the numbewr of edges out of the node to determine how + // many columns to span (max 64) + unsigned ColSpan = 0; + child_iterator EI = GTraits::child_begin(Node); + child_iterator EE = GTraits::child_end(Node); + for (; EI != EE && ColSpan != 64; ++EI, ++ColSpan) + ; + if (ColSpan == 0) + ColSpan = 1; + // Include truncated messages when counting. + if (EI != EE) + ++ColSpan; + O << "<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\"" + << " cellpadding=\"0\"><tr><td align=\"text\" colspan=\"" << ColSpan + << "\">"; + } else + O << "\"{"; + + if (!DTraits.renderGraphFromBottomUp()) { + if (RenderUsingHTML) + O << DTraits.getNodeLabel(Node, G) << "</td>"; + else + O << DOT::EscapeString(DTraits.getNodeLabel(Node, G)); + + // If we should include the address of the node in the label, do so now. + std::string Id = DTraits.getNodeIdentifierLabel(Node, G); + if (!Id.empty()) + O << "|" << DOT::EscapeString(Id); + + std::string NodeDesc = DTraits.getNodeDescription(Node, G); + if (!NodeDesc.empty()) + O << "|" << DOT::EscapeString(NodeDesc); + } + + std::string edgeSourceLabels; + raw_string_ostream EdgeSourceLabels(edgeSourceLabels); + bool hasEdgeSourceLabels = getEdgeSourceLabels(EdgeSourceLabels, Node); + + if (hasEdgeSourceLabels) { + if (!DTraits.renderGraphFromBottomUp()) + if (!RenderUsingHTML) + O << "|"; + + if (RenderUsingHTML) + O << EdgeSourceLabels.str(); + else + O << "{" << EdgeSourceLabels.str() << "}"; + + if (DTraits.renderGraphFromBottomUp()) + if (!RenderUsingHTML) + O << "|"; + } + + if (DTraits.renderGraphFromBottomUp()) { + if (RenderUsingHTML) + O << DTraits.getNodeLabel(Node, G); + else + O << DOT::EscapeString(DTraits.getNodeLabel(Node, G)); + + // If we should include the address of the node in the label, do so now. + std::string Id = DTraits.getNodeIdentifierLabel(Node, G); + if (!Id.empty()) + O << "|" << DOT::EscapeString(Id); + + std::string NodeDesc = DTraits.getNodeDescription(Node, G); + if (!NodeDesc.empty()) + O << "|" << DOT::EscapeString(NodeDesc); + } + + if (DTraits.hasEdgeDestLabels()) { + O << "|{"; + + unsigned i = 0, e = DTraits.numEdgeDestLabels(Node); + for (; i != e && i != 64; ++i) { + if (i) O << "|"; + O << "<d" << i << ">" + << DOT::EscapeString(DTraits.getEdgeDestLabel(Node, i)); + } + + if (i != e) + O << "|<d64>truncated..."; + O << "}"; + } + + if (RenderUsingHTML) + O << "</tr></table>>"; + else + O << "}\""; + O << "];\n"; // Finish printing the "node" line + + // Output all of the edges now + child_iterator EI = GTraits::child_begin(Node); + child_iterator EE = GTraits::child_end(Node); + for (unsigned i = 0; EI != EE && i != 64; ++EI, ++i) + if (!DTraits.isNodeHidden(*EI, G)) + writeEdge(Node, i, EI); + for (; EI != EE; ++EI) + if (!DTraits.isNodeHidden(*EI, G)) + writeEdge(Node, 64, EI); + } + + void writeEdge(NodeRef Node, unsigned edgeidx, child_iterator EI) { + if (NodeRef TargetNode = *EI) { + int DestPort = -1; + if (DTraits.edgeTargetsEdgeSource(Node, EI)) { + child_iterator TargetIt = DTraits.getEdgeTarget(Node, EI); + + // Figure out which edge this targets... + unsigned Offset = + (unsigned)std::distance(GTraits::child_begin(TargetNode), TargetIt); + DestPort = static_cast<int>(Offset); + } + + if (DTraits.getEdgeSourceLabel(Node, EI).empty()) + edgeidx = -1; + + emitEdge(static_cast<const void*>(Node), edgeidx, + static_cast<const void*>(TargetNode), DestPort, + DTraits.getEdgeAttributes(Node, EI, G)); + } + } + + /// emitSimpleNode - Outputs a simple (non-record) node + void emitSimpleNode(const void *ID, const std::string &Attr, + const std::string &Label, unsigned NumEdgeSources = 0, + const std::vector<std::string> *EdgeSourceLabels = nullptr) { + O << "\tNode" << ID << "[ "; + if (!Attr.empty()) + O << Attr << ","; + O << " label =\""; + if (NumEdgeSources) O << "{"; + O << DOT::EscapeString(Label); + if (NumEdgeSources) { + O << "|{"; + + for (unsigned i = 0; i != NumEdgeSources; ++i) { + if (i) O << "|"; + O << "<s" << i << ">"; + if (EdgeSourceLabels) O << DOT::EscapeString((*EdgeSourceLabels)[i]); + } + O << "}}"; + } + O << "\"];\n"; + } + + /// emitEdge - Output an edge from a simple node into the graph... + void emitEdge(const void *SrcNodeID, int SrcNodePort, + const void *DestNodeID, int DestNodePort, + const std::string &Attrs) { + if (SrcNodePort > 64) return; // Eminating from truncated part? + if (DestNodePort > 64) DestNodePort = 64; // Targeting the truncated part? + + O << "\tNode" << SrcNodeID; + if (SrcNodePort >= 0) + O << ":s" << SrcNodePort; + O << " -> Node" << DestNodeID; + if (DestNodePort >= 0 && DTraits.hasEdgeDestLabels()) + O << ":d" << DestNodePort; + + if (!Attrs.empty()) + O << "[" << Attrs << "]"; + O << ";\n"; + } + + /// getOStream - Get the raw output stream into the graph file. Useful to + /// write fancy things using addCustomGraphFeatures(). + raw_ostream &getOStream() { + return O; + } +}; + +template<typename GraphType> +raw_ostream &WriteGraph(raw_ostream &O, const GraphType &G, + bool ShortNames = false, + const Twine &Title = "") { + // Start the graph emission process... + GraphWriter<GraphType> W(O, G, ShortNames); + + // Emit the graph. + W.writeGraph(Title.str()); + + return O; +} + +std::string createGraphFilename(const Twine &Name, int &FD); + +/// Writes graph into a provided @c Filename. +/// If @c Filename is empty, generates a random one. +/// \return The resulting filename, or an empty string if writing +/// failed. +template <typename GraphType> +std::string WriteGraph(const GraphType &G, const Twine &Name, + bool ShortNames = false, + const Twine &Title = "", + std::string Filename = "") { + int FD; + if (Filename.empty()) { + Filename = createGraphFilename(Name.str(), FD); + } else { + std::error_code EC = sys::fs::openFileForWrite( + Filename, FD, sys::fs::CD_CreateAlways, sys::fs::OF_Text); + + // Writing over an existing file is not considered an error. + if (EC == std::errc::file_exists) { + errs() << "file exists, overwriting" << "\n"; + } else if (EC) { + errs() << "error writing into file" << "\n"; + return ""; + } else { + errs() << "writing to the newly created file " << Filename << "\n"; + } + } + raw_fd_ostream O(FD, /*shouldClose=*/ true); + + if (FD == -1) { + errs() << "error opening file '" << Filename << "' for writing!\n"; + return ""; + } + + llvm::WriteGraph(O, G, ShortNames, Title); + errs() << " done. \n"; + + return Filename; +} + +/// DumpDotGraph - Just dump a dot graph to the user-provided file name. +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +template <typename GraphType> +LLVM_DUMP_METHOD void +dumpDotGraphToFile(const GraphType &G, const Twine &FileName, + const Twine &Title, bool ShortNames = false, + const Twine &Name = "") { + llvm::WriteGraph(G, Name, ShortNames, Title, FileName.str()); +} +#endif + +/// ViewGraph - Emit a dot graph, run 'dot', run gv on the postscript file, +/// then cleanup. For use from the debugger. +/// +template<typename GraphType> +void ViewGraph(const GraphType &G, const Twine &Name, + bool ShortNames = false, const Twine &Title = "", + GraphProgram::Name Program = GraphProgram::DOT) { + std::string Filename = llvm::WriteGraph(G, Name, ShortNames, Title); + + if (Filename.empty()) + return; + + DisplayGraph(Filename, false, Program); +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_GRAPHWRITER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/HashBuilder.h b/contrib/libs/llvm16/include/llvm/Support/HashBuilder.h new file mode 100644 index 00000000000..e636485ffac --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/HashBuilder.h @@ -0,0 +1,442 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/HashBuilder.h - Convenient hashing interface-*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements an interface allowing to conveniently build hashes of +// various data types, without relying on the underlying hasher type to know +// about hashed data types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_HASHBUILDER_H +#define LLVM_SUPPORT_HASHBUILDER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/type_traits.h" + +#include <iterator> +#include <optional> +#include <utility> + +namespace llvm { + +namespace hashbuilder_detail { +/// Trait to indicate whether a type's bits can be hashed directly (after +/// endianness correction). +template <typename U> +struct IsHashableData + : std::integral_constant<bool, is_integral_or_enum<U>::value> {}; + +} // namespace hashbuilder_detail + +/// Declares the hasher member, and functions forwarding directly to the hasher. +template <typename HasherT> class HashBuilderBase { +public: + template <typename HasherT_ = HasherT> + using HashResultTy = decltype(std::declval<HasherT_ &>().final()); + + HasherT &getHasher() { return Hasher; } + + /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. + /// + /// This may not take the size of `Data` into account. + /// Users of this function should pay attention to respect endianness + /// contraints. + void update(ArrayRef<uint8_t> Data) { this->getHasher().update(Data); } + + /// Forward to `HasherT::update(ArrayRef<uint8_t>)`. + /// + /// This may not take the size of `Data` into account. + /// Users of this function should pay attention to respect endianness + /// contraints. + void update(StringRef Data) { + update( + ArrayRef(reinterpret_cast<const uint8_t *>(Data.data()), Data.size())); + } + + /// Forward to `HasherT::final()` if available. + template <typename HasherT_ = HasherT> HashResultTy<HasherT_> final() { + return this->getHasher().final(); + } + + /// Forward to `HasherT::result()` if available. + template <typename HasherT_ = HasherT> HashResultTy<HasherT_> result() { + return this->getHasher().result(); + } + +protected: + explicit HashBuilderBase(HasherT &Hasher) : Hasher(Hasher) {} + + template <typename... ArgTypes> + explicit HashBuilderBase(ArgTypes &&...Args) + : OptionalHasher(std::in_place, std::forward<ArgTypes>(Args)...), + Hasher(*OptionalHasher) {} + +private: + std::optional<HasherT> OptionalHasher; + HasherT &Hasher; +}; + +/// Implementation of the `HashBuilder` interface. +/// +/// `support::endianness::native` is not supported. `HashBuilder` is +/// expected to canonicalize `support::endianness::native` to one of +/// `support::endianness::big` or `support::endianness::little`. +template <typename HasherT, support::endianness Endianness> +class HashBuilderImpl : public HashBuilderBase<HasherT> { + static_assert(Endianness != support::endianness::native, + "HashBuilder should canonicalize endianness"); + +public: + explicit HashBuilderImpl(HasherT &Hasher) + : HashBuilderBase<HasherT>(Hasher) {} + template <typename... ArgTypes> + explicit HashBuilderImpl(ArgTypes &&...Args) + : HashBuilderBase<HasherT>(Args...) {} + + /// Implement hashing for hashable data types, e.g. integral or enum values. + template <typename T> + std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value, + HashBuilderImpl &> + add(T Value) { + return adjustForEndiannessAndAdd(Value); + } + + /// Support hashing `ArrayRef`. + /// + /// `Value.size()` is taken into account to ensure cases like + /// ``` + /// builder.add({1}); + /// builder.add({2, 3}); + /// ``` + /// and + /// ``` + /// builder.add({1, 2}); + /// builder.add({3}); + /// ``` + /// do not collide. + template <typename T> HashBuilderImpl &add(ArrayRef<T> Value) { + // As of implementation time, simply calling `addRange(Value)` would also go + // through the `update` fast path. But that would rely on the implementation + // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call + // `update` to guarantee the fast path. + add(Value.size()); + if (hashbuilder_detail::IsHashableData<T>::value && + Endianness == support::endian::system_endianness()) { + this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()), + Value.size() * sizeof(T))); + } else { + for (auto &V : Value) + add(V); + } + return *this; + } + + /// Support hashing `StringRef`. + /// + /// `Value.size()` is taken into account to ensure cases like + /// ``` + /// builder.add("a"); + /// builder.add("bc"); + /// ``` + /// and + /// ``` + /// builder.add("ab"); + /// builder.add("c"); + /// ``` + /// do not collide. + HashBuilderImpl &add(StringRef Value) { + // As of implementation time, simply calling `addRange(Value)` would also go + // through `update`. But that would rely on the implementation of + // `StringRef::begin()` and `StringRef::end()`. Explicitly call `update` to + // guarantee the fast path. + add(Value.size()); + this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()), + Value.size())); + return *this; + } + + template <typename T> + using HasAddHashT = + decltype(addHash(std::declval<HashBuilderImpl &>(), std::declval<T &>())); + /// Implement hashing for user-defined `struct`s. + /// + /// Any user-define `struct` can participate in hashing via `HashBuilder` by + /// providing a `addHash` templated function. + /// + /// ``` + /// template <typename HasherT, support::endianness Endianness> + /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder, + /// const UserDefinedStruct &Value); + /// ``` + /// + /// For example: + /// ``` + /// struct SimpleStruct { + /// char c; + /// int i; + /// }; + /// + /// template <typename HasherT, support::endianness Endianness> + /// void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, + /// const SimpleStruct &Value) { + /// HBuilder.add(Value.c); + /// HBuilder.add(Value.i); + /// } + /// ``` + /// + /// To avoid endianness issues, specializations of `addHash` should + /// generally rely on exising `add`, `addRange`, and `addRangeElements` + /// functions. If directly using `update`, an implementation must correctly + /// handle endianness. + /// + /// ``` + /// struct __attribute__ ((packed)) StructWithFastHash { + /// int I; + /// char C; + /// + /// // If possible, we want to hash both `I` and `C` in a single + /// // `update` call for performance concerns. + /// template <typename HasherT, support::endianness Endianness> + /// friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, + /// const StructWithFastHash &Value) { + /// if (Endianness == support::endian::system_endianness()) { + /// HBuilder.update(ArrayRef( + /// reinterpret_cast<const uint8_t *>(&Value), sizeof(Value))); + /// } else { + /// // Rely on existing `add` methods to handle endianness. + /// HBuilder.add(Value.I); + /// HBuilder.add(Value.C); + /// } + /// } + /// }; + /// ``` + /// + /// To avoid collisions, specialization of `addHash` for variable-size + /// types must take the size into account. + /// + /// For example: + /// ``` + /// struct CustomContainer { + /// private: + /// size_t Size; + /// int Elements[100]; + /// + /// public: + /// CustomContainer(size_t Size) : Size(Size) { + /// for (size_t I = 0; I != Size; ++I) + /// Elements[I] = I; + /// } + /// template <typename HasherT, support::endianness Endianness> + /// friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, + /// const CustomContainer &Value) { + /// if (Endianness == support::endian::system_endianness()) { + /// HBuilder.update(ArrayRef( + /// reinterpret_cast<const uint8_t *>(&Value.Size), + /// sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0]))); + /// } else { + /// // `addRange` will take care of encoding the size. + /// HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] + + /// Value.Size); + /// } + /// } + /// }; + /// ``` + template <typename T> + std::enable_if_t<is_detected<HasAddHashT, T>::value && + !hashbuilder_detail::IsHashableData<T>::value, + HashBuilderImpl &> + add(const T &Value) { + addHash(*this, Value); + return *this; + } + + template <typename T1, typename T2> + HashBuilderImpl &add(const std::pair<T1, T2> &Value) { + return add(Value.first, Value.second); + } + + template <typename... Ts> HashBuilderImpl &add(const std::tuple<Ts...> &Arg) { + std::apply([this](const auto &...Args) { this->add(Args...); }, Arg); + return *this; + } + + /// A convenenience variadic helper. + /// It simply iterates over its arguments, in order. + /// ``` + /// add(Arg1, Arg2); + /// ``` + /// is equivalent to + /// ``` + /// add(Arg1) + /// add(Arg2) + /// ``` + template <typename... Ts> + std::enable_if_t<(sizeof...(Ts) > 1), HashBuilderImpl &> + add(const Ts &...Args) { + return (add(Args), ...); + } + + template <typename ForwardIteratorT> + HashBuilderImpl &addRange(ForwardIteratorT First, ForwardIteratorT Last) { + add(std::distance(First, Last)); + return addRangeElements(First, Last); + } + + template <typename RangeT> HashBuilderImpl &addRange(const RangeT &Range) { + return addRange(adl_begin(Range), adl_end(Range)); + } + + template <typename ForwardIteratorT> + HashBuilderImpl &addRangeElements(ForwardIteratorT First, + ForwardIteratorT Last) { + return addRangeElementsImpl( + First, Last, + typename std::iterator_traits<ForwardIteratorT>::iterator_category()); + } + + template <typename RangeT> + HashBuilderImpl &addRangeElements(const RangeT &Range) { + return addRangeElements(adl_begin(Range), adl_end(Range)); + } + + template <typename T> + using HasByteSwapT = decltype(support::endian::byte_swap( + std::declval<T &>(), support::endianness::little)); + /// Adjust `Value` for the target endianness and add it to the hash. + template <typename T> + std::enable_if_t<is_detected<HasByteSwapT, T>::value, HashBuilderImpl &> + adjustForEndiannessAndAdd(const T &Value) { + T SwappedValue = support::endian::byte_swap(Value, Endianness); + this->update(ArrayRef(reinterpret_cast<const uint8_t *>(&SwappedValue), + sizeof(SwappedValue))); + return *this; + } + +private: + // FIXME: Once available, specialize this function for `contiguous_iterator`s, + // and use it for `ArrayRef` and `StringRef`. + template <typename ForwardIteratorT> + HashBuilderImpl &addRangeElementsImpl(ForwardIteratorT First, + ForwardIteratorT Last, + std::forward_iterator_tag) { + for (auto It = First; It != Last; ++It) + add(*It); + return *this; + } + + template <typename T> + std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value && + Endianness == support::endian::system_endianness(), + HashBuilderImpl &> + addRangeElementsImpl(T *First, T *Last, std::forward_iterator_tag) { + this->update(ArrayRef(reinterpret_cast<const uint8_t *>(First), + (Last - First) * sizeof(T))); + return *this; + } +}; + +/// Interface to help hash various types through a hasher type. +/// +/// Via provided specializations of `add`, `addRange`, and `addRangeElements` +/// functions, various types (e.g. `ArrayRef`, `StringRef`, etc.) can be hashed +/// without requiring any knowledge of hashed types from the hasher type. +/// +/// The only method expected from the templated hasher type `HasherT` is: +/// * void update(ArrayRef<uint8_t> Data) +/// +/// Additionally, the following methods will be forwarded to the hasher type: +/// * decltype(std::declval<HasherT &>().final()) final() +/// * decltype(std::declval<HasherT &>().result()) result() +/// +/// From a user point of view, the interface provides the following: +/// * `template<typename T> add(const T &Value)` +/// The `add` function implements hashing of various types. +/// * `template <typename ItT> void addRange(ItT First, ItT Last)` +/// The `addRange` function is designed to aid hashing a range of values. +/// It explicitly adds the size of the range in the hash. +/// * `template <typename ItT> void addRangeElements(ItT First, ItT Last)` +/// The `addRangeElements` function is also designed to aid hashing a range of +/// values. In contrast to `addRange`, it **ignores** the size of the range, +/// behaving as if elements were added one at a time with `add`. +/// +/// User-defined `struct` types can participate in this interface by providing +/// an `addHash` templated function. See the associated template specialization +/// for details. +/// +/// This interface does not impose requirements on the hasher +/// `update(ArrayRef<uint8_t> Data)` method. We want to avoid collisions for +/// variable-size types; for example for +/// ``` +/// builder.add({1}); +/// builder.add({2, 3}); +/// ``` +/// and +/// ``` +/// builder.add({1, 2}); +/// builder.add({3}); +/// ``` +/// . Thus, specializations of `add` and `addHash` for variable-size types must +/// not assume that the hasher type considers the size as part of the hash; they +/// must explicitly add the size to the hash. See for example specializations +/// for `ArrayRef` and `StringRef`. +/// +/// Additionally, since types are eventually forwarded to the hasher's +/// `void update(ArrayRef<uint8_t>)` method, endianness plays a role in the hash +/// computation (for example when computing `add((int)123)`). +/// Specifiying a non-`native` `Endianness` template parameter allows to compute +/// stable hash across platforms with different endianness. +template <class HasherT, support::endianness Endianness> +using HashBuilder = + HashBuilderImpl<HasherT, (Endianness == support::endianness::native + ? support::endian::system_endianness() + : Endianness)>; + +namespace hashbuilder_detail { +class HashCodeHasher { +public: + HashCodeHasher() : Code(0) {} + void update(ArrayRef<uint8_t> Data) { + hash_code DataCode = hash_value(Data); + Code = hash_combine(Code, DataCode); + } + hash_code Code; +}; + +using HashCodeHashBuilder = HashBuilder<hashbuilder_detail::HashCodeHasher, + support::endianness::native>; +} // namespace hashbuilder_detail + +/// Provide a default implementation of `hash_value` when `addHash(const T &)` +/// is supported. +template <typename T> +std::enable_if_t< + is_detected<hashbuilder_detail::HashCodeHashBuilder::HasAddHashT, T>::value, + hash_code> +hash_value(const T &Value) { + hashbuilder_detail::HashCodeHashBuilder HBuilder; + HBuilder.add(Value); + return HBuilder.getHasher().Code; +} +} // end namespace llvm + +#endif // LLVM_SUPPORT_HASHBUILDER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Host.h b/contrib/libs/llvm16/include/llvm/Support/Host.h new file mode 100644 index 00000000000..b1e1b9e4db2 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Host.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Host.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of `llvm/TargetParser/Host.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/Host.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/InitLLVM.h b/contrib/libs/llvm16/include/llvm/Support/InitLLVM.h new file mode 100644 index 00000000000..f89411c9796 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/InitLLVM.h @@ -0,0 +1,63 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- InitLLVM.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_INITLLVM_H +#define LLVM_SUPPORT_INITLLVM_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/PrettyStackTrace.h" +#include <optional> + +// The main() functions in typical LLVM tools start with InitLLVM which does +// the following one-time initializations: +// +// 1. Setting up a signal handler so that pretty stack trace is printed out +// if a process crashes. A signal handler that exits when a failed write to +// a pipe occurs may optionally be installed: this is on-by-default. +// +// 2. Set up the global new-handler which is called when a memory allocation +// attempt fails. +// +// 3. If running on Windows, obtain command line arguments using a +// multibyte character-aware API and convert arguments into UTF-8 +// encoding, so that you can assume that command line arguments are +// always encoded in UTF-8 on any platform. +// +// InitLLVM calls llvm_shutdown() on destruction, which cleans up +// ManagedStatic objects. +namespace llvm { +class InitLLVM { +public: + InitLLVM(int &Argc, const char **&Argv, + bool InstallPipeSignalExitHandler = true); + InitLLVM(int &Argc, char **&Argv, bool InstallPipeSignalExitHandler = true) + : InitLLVM(Argc, const_cast<const char **&>(Argv), + InstallPipeSignalExitHandler) {} + + ~InitLLVM(); + +private: + BumpPtrAllocator Alloc; + SmallVector<const char *, 0> Args; + std::optional<PrettyStackTraceProgram> StackPrinter; +}; +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/InstructionCost.h b/contrib/libs/llvm16/include/llvm/Support/InstructionCost.h new file mode 100644 index 00000000000..a9a7ea1e15d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/InstructionCost.h @@ -0,0 +1,298 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- InstructionCost.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines an InstructionCost class that is used when calculating +/// the cost of an instruction, or a group of instructions. In addition to a +/// numeric value representing the cost the class also contains a state that +/// can be used to encode particular properties, such as a cost being invalid. +/// Operations on InstructionCost implement saturation arithmetic, so that +/// accumulating costs on large cost-values don't overflow. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_INSTRUCTIONCOST_H +#define LLVM_SUPPORT_INSTRUCTIONCOST_H + +#include "llvm/Support/MathExtras.h" +#include <limits> +#include <optional> + +namespace llvm { + +class raw_ostream; + +class InstructionCost { +public: + using CostType = int64_t; + + /// CostState describes the state of a cost. + enum CostState { + Valid, /// < The cost value represents a valid cost, even when the + /// cost-value is large. + Invalid /// < Invalid indicates there is no way to represent the cost as a + /// numeric value. This state exists to represent a possible issue, + /// e.g. if the cost-model knows the operation cannot be expanded + /// into a valid code-sequence by the code-generator. While some + /// passes may assert that the calculated cost must be valid, it is + /// up to individual passes how to interpret an Invalid cost. For + /// example, a transformation pass could choose not to perform a + /// transformation if the resulting cost would end up Invalid. + /// Because some passes may assert a cost is Valid, it is not + /// recommended to use Invalid costs to model 'Unknown'. + /// Note that Invalid is semantically different from a (very) high, + /// but valid cost, which intentionally indicates no issue, but + /// rather a strong preference not to select a certain operation. + }; + +private: + CostType Value = 0; + CostState State = Valid; + + void propagateState(const InstructionCost &RHS) { + if (RHS.State == Invalid) + State = Invalid; + } + + static CostType getMaxValue() { return std::numeric_limits<CostType>::max(); } + static CostType getMinValue() { return std::numeric_limits<CostType>::min(); } + +public: + // A default constructed InstructionCost is a valid zero cost + InstructionCost() = default; + + InstructionCost(CostState) = delete; + InstructionCost(CostType Val) : Value(Val), State(Valid) {} + + static InstructionCost getMax() { return getMaxValue(); } + static InstructionCost getMin() { return getMinValue(); } + static InstructionCost getInvalid(CostType Val = 0) { + InstructionCost Tmp(Val); + Tmp.setInvalid(); + return Tmp; + } + + bool isValid() const { return State == Valid; } + void setValid() { State = Valid; } + void setInvalid() { State = Invalid; } + CostState getState() const { return State; } + + /// This function is intended to be used as sparingly as possible, since the + /// class provides the full range of operator support required for arithmetic + /// and comparisons. + std::optional<CostType> getValue() const { + if (isValid()) + return Value; + return std::nullopt; + } + + /// For all of the arithmetic operators provided here any invalid state is + /// perpetuated and cannot be removed. Once a cost becomes invalid it stays + /// invalid, and it also inherits any invalid state from the RHS. + /// Arithmetic work on the actual values is implemented with saturation, + /// to avoid overflow when using more extreme cost values. + + InstructionCost &operator+=(const InstructionCost &RHS) { + propagateState(RHS); + + // Saturating addition. + InstructionCost::CostType Result; + if (AddOverflow(Value, RHS.Value, Result)) + Result = RHS.Value > 0 ? getMaxValue() : getMinValue(); + + Value = Result; + return *this; + } + + InstructionCost &operator+=(const CostType RHS) { + InstructionCost RHS2(RHS); + *this += RHS2; + return *this; + } + + InstructionCost &operator-=(const InstructionCost &RHS) { + propagateState(RHS); + + // Saturating subtract. + InstructionCost::CostType Result; + if (SubOverflow(Value, RHS.Value, Result)) + Result = RHS.Value > 0 ? getMinValue() : getMaxValue(); + Value = Result; + return *this; + } + + InstructionCost &operator-=(const CostType RHS) { + InstructionCost RHS2(RHS); + *this -= RHS2; + return *this; + } + + InstructionCost &operator*=(const InstructionCost &RHS) { + propagateState(RHS); + + // Saturating multiply. + InstructionCost::CostType Result; + if (MulOverflow(Value, RHS.Value, Result)) { + if ((Value > 0 && RHS.Value > 0) || (Value < 0 && RHS.Value < 0)) + Result = getMaxValue(); + else + Result = getMinValue(); + } + + Value = Result; + return *this; + } + + InstructionCost &operator*=(const CostType RHS) { + InstructionCost RHS2(RHS); + *this *= RHS2; + return *this; + } + + InstructionCost &operator/=(const InstructionCost &RHS) { + propagateState(RHS); + Value /= RHS.Value; + return *this; + } + + InstructionCost &operator/=(const CostType RHS) { + InstructionCost RHS2(RHS); + *this /= RHS2; + return *this; + } + + InstructionCost &operator++() { + *this += 1; + return *this; + } + + InstructionCost operator++(int) { + InstructionCost Copy = *this; + ++*this; + return Copy; + } + + InstructionCost &operator--() { + *this -= 1; + return *this; + } + + InstructionCost operator--(int) { + InstructionCost Copy = *this; + --*this; + return Copy; + } + + /// For the comparison operators we have chosen to use lexicographical + /// ordering where valid costs are always considered to be less than invalid + /// costs. This avoids having to add asserts to the comparison operators that + /// the states are valid and users can test for validity of the cost + /// explicitly. + bool operator<(const InstructionCost &RHS) const { + if (State != RHS.State) + return State < RHS.State; + return Value < RHS.Value; + } + + // Implement in terms of operator< to ensure that the two comparisons stay in + // sync + bool operator==(const InstructionCost &RHS) const { + return !(*this < RHS) && !(RHS < *this); + } + + bool operator!=(const InstructionCost &RHS) const { return !(*this == RHS); } + + bool operator==(const CostType RHS) const { + InstructionCost RHS2(RHS); + return *this == RHS2; + } + + bool operator!=(const CostType RHS) const { return !(*this == RHS); } + + bool operator>(const InstructionCost &RHS) const { return RHS < *this; } + + bool operator<=(const InstructionCost &RHS) const { return !(RHS < *this); } + + bool operator>=(const InstructionCost &RHS) const { return !(*this < RHS); } + + bool operator<(const CostType RHS) const { + InstructionCost RHS2(RHS); + return *this < RHS2; + } + + bool operator>(const CostType RHS) const { + InstructionCost RHS2(RHS); + return *this > RHS2; + } + + bool operator<=(const CostType RHS) const { + InstructionCost RHS2(RHS); + return *this <= RHS2; + } + + bool operator>=(const CostType RHS) const { + InstructionCost RHS2(RHS); + return *this >= RHS2; + } + + void print(raw_ostream &OS) const; + + template <class Function> + auto map(const Function &F) const -> InstructionCost { + if (isValid()) + return F(Value); + return getInvalid(); + } +}; + +inline InstructionCost operator+(const InstructionCost &LHS, + const InstructionCost &RHS) { + InstructionCost LHS2(LHS); + LHS2 += RHS; + return LHS2; +} + +inline InstructionCost operator-(const InstructionCost &LHS, + const InstructionCost &RHS) { + InstructionCost LHS2(LHS); + LHS2 -= RHS; + return LHS2; +} + +inline InstructionCost operator*(const InstructionCost &LHS, + const InstructionCost &RHS) { + InstructionCost LHS2(LHS); + LHS2 *= RHS; + return LHS2; +} + +inline InstructionCost operator/(const InstructionCost &LHS, + const InstructionCost &RHS) { + InstructionCost LHS2(LHS); + LHS2 /= RHS; + return LHS2; +} + +inline raw_ostream &operator<<(raw_ostream &OS, const InstructionCost &V) { + V.print(OS); + return OS; +} + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ItaniumManglingCanonicalizer.h b/contrib/libs/llvm16/include/llvm/Support/ItaniumManglingCanonicalizer.h new file mode 100644 index 00000000000..383203e048d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ItaniumManglingCanonicalizer.h @@ -0,0 +1,104 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- ItaniumManglingCanonicalizer.h -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a class for computing equivalence classes of mangled names +// given a set of equivalences between name fragments. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ITANIUMMANGLINGCANONICALIZER_H +#define LLVM_SUPPORT_ITANIUMMANGLINGCANONICALIZER_H + +#include <cstdint> + +namespace llvm { + +class StringRef; + +/// Canonicalizer for mangled names. +/// +/// This class allows specifying a list of "equivalent" manglings. For example, +/// you can specify that Ss is equivalent to +/// NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE +/// and then manglings that refer to libstdc++'s 'std::string' will be +/// considered equivalent to manglings that are the same except that they refer +/// to libc++'s 'std::string'. +/// +/// This can be used when data (eg, profiling data) is available for a version +/// of a program built in a different configuration, with correspondingly +/// different manglings. +class ItaniumManglingCanonicalizer { +public: + ItaniumManglingCanonicalizer(); + ItaniumManglingCanonicalizer(const ItaniumManglingCanonicalizer &) = delete; + void operator=(const ItaniumManglingCanonicalizer &) = delete; + ~ItaniumManglingCanonicalizer(); + + enum class EquivalenceError { + Success, + + /// Both the equivalent manglings have already been used as components of + /// some other mangling we've looked at. It's too late to add this + /// equivalence. + ManglingAlreadyUsed, + + /// The first equivalent mangling is invalid. + InvalidFirstMangling, + + /// The second equivalent mangling is invalid. + InvalidSecondMangling, + }; + + enum class FragmentKind { + /// The mangling fragment is a <name> (or a predefined <substitution>). + Name, + /// The mangling fragment is a <type>. + Type, + /// The mangling fragment is an <encoding>. + Encoding, + }; + + /// Add an equivalence between \p First and \p Second. Both manglings must + /// live at least as long as the canonicalizer. + EquivalenceError addEquivalence(FragmentKind Kind, StringRef First, + StringRef Second); + + using Key = uintptr_t; + + /// Form a canonical key for the specified mangling. They key will be the + /// same for all equivalent manglings, and different for any two + /// non-equivalent manglings, but is otherwise unspecified. + /// + /// Returns Key() if (and only if) the mangling is not a valid Itanium C++ + /// ABI mangling. + /// + /// The string denoted by Mangling must live as long as the canonicalizer. + Key canonicalize(StringRef Mangling); + + /// Find a canonical key for the specified mangling, if one has already been + /// formed. Otherwise returns Key(). + Key lookup(StringRef Mangling); + +private: + struct Impl; + Impl *P; +}; +} // namespace llvm + +#endif // LLVM_SUPPORT_ITANIUMMANGLINGCANONICALIZER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/JSON.h b/contrib/libs/llvm16/include/llvm/Support/JSON.h new file mode 100644 index 00000000000..6a1bbca118e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/JSON.h @@ -0,0 +1,1091 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- JSON.h - JSON values, parsing and serialization -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +/// +/// \file +/// This file supports working with JSON data. +/// +/// It comprises: +/// +/// - classes which hold dynamically-typed parsed JSON structures +/// These are value types that can be composed, inspected, and modified. +/// See json::Value, and the related types json::Object and json::Array. +/// +/// - functions to parse JSON text into Values, and to serialize Values to text. +/// See parse(), operator<<, and format_provider. +/// +/// - a convention and helpers for mapping between json::Value and user-defined +/// types. See fromJSON(), ObjectMapper, and the class comment on Value. +/// +/// - an output API json::OStream which can emit JSON without materializing +/// all structures as json::Value. +/// +/// Typically, JSON data would be read from an external source, parsed into +/// a Value, and then converted into some native data structure before doing +/// real work on it. (And vice versa when writing). +/// +/// Other serialization mechanisms you may consider: +/// +/// - YAML is also text-based, and more human-readable than JSON. It's a more +/// complex format and data model, and YAML parsers aren't ubiquitous. +/// YAMLParser.h is a streaming parser suitable for parsing large documents +/// (including JSON, as YAML is a superset). It can be awkward to use +/// directly. YAML I/O (YAMLTraits.h) provides data mapping that is more +/// declarative than the toJSON/fromJSON conventions here. +/// +/// - LLVM bitstream is a space- and CPU- efficient binary format. Typically it +/// encodes LLVM IR ("bitcode"), but it can be a container for other data. +/// Low-level reader/writer libraries are in Bitstream/Bitstream*.h +/// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_JSON_H +#define LLVM_SUPPORT_JSON_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" +#include <cmath> +#include <map> + +namespace llvm { +namespace json { + +// === String encodings === +// +// JSON strings are character sequences (not byte sequences like std::string). +// We need to know the encoding, and for simplicity only support UTF-8. +// +// - When parsing, invalid UTF-8 is a syntax error like any other +// +// - When creating Values from strings, callers must ensure they are UTF-8. +// with asserts on, invalid UTF-8 will crash the program +// with asserts off, we'll substitute the replacement character (U+FFFD) +// Callers can use json::isUTF8() and json::fixUTF8() for validation. +// +// - When retrieving strings from Values (e.g. asString()), the result will +// always be valid UTF-8. + +/// Returns true if \p S is valid UTF-8, which is required for use as JSON. +/// If it returns false, \p Offset is set to a byte offset near the first error. +bool isUTF8(llvm::StringRef S, size_t *ErrOffset = nullptr); +/// Replaces invalid UTF-8 sequences in \p S with the replacement character +/// (U+FFFD). The returned string is valid UTF-8. +/// This is much slower than isUTF8, so test that first. +std::string fixUTF8(llvm::StringRef S); + +class Array; +class ObjectKey; +class Value; +template <typename T> Value toJSON(const std::optional<T> &Opt); + +/// An Object is a JSON object, which maps strings to heterogenous JSON values. +/// It simulates DenseMap<ObjectKey, Value>. ObjectKey is a maybe-owned string. +class Object { + using Storage = DenseMap<ObjectKey, Value, llvm::DenseMapInfo<StringRef>>; + Storage M; + +public: + using key_type = ObjectKey; + using mapped_type = Value; + using value_type = Storage::value_type; + using iterator = Storage::iterator; + using const_iterator = Storage::const_iterator; + + Object() = default; + // KV is a trivial key-value struct for list-initialization. + // (using std::pair forces extra copies). + struct KV; + explicit Object(std::initializer_list<KV> Properties); + + iterator begin() { return M.begin(); } + const_iterator begin() const { return M.begin(); } + iterator end() { return M.end(); } + const_iterator end() const { return M.end(); } + + bool empty() const { return M.empty(); } + size_t size() const { return M.size(); } + + void clear() { M.clear(); } + std::pair<iterator, bool> insert(KV E); + template <typename... Ts> + std::pair<iterator, bool> try_emplace(const ObjectKey &K, Ts &&... Args) { + return M.try_emplace(K, std::forward<Ts>(Args)...); + } + template <typename... Ts> + std::pair<iterator, bool> try_emplace(ObjectKey &&K, Ts &&... Args) { + return M.try_emplace(std::move(K), std::forward<Ts>(Args)...); + } + bool erase(StringRef K); + void erase(iterator I) { M.erase(I); } + + iterator find(StringRef K) { return M.find_as(K); } + const_iterator find(StringRef K) const { return M.find_as(K); } + // operator[] acts as if Value was default-constructible as null. + Value &operator[](const ObjectKey &K); + Value &operator[](ObjectKey &&K); + // Look up a property, returning nullptr if it doesn't exist. + Value *get(StringRef K); + const Value *get(StringRef K) const; + // Typed accessors return std::nullopt/nullptr if + // - the property doesn't exist + // - or it has the wrong type + std::optional<std::nullptr_t> getNull(StringRef K) const; + std::optional<bool> getBoolean(StringRef K) const; + std::optional<double> getNumber(StringRef K) const; + std::optional<int64_t> getInteger(StringRef K) const; + std::optional<llvm::StringRef> getString(StringRef K) const; + const json::Object *getObject(StringRef K) const; + json::Object *getObject(StringRef K); + const json::Array *getArray(StringRef K) const; + json::Array *getArray(StringRef K); +}; +bool operator==(const Object &LHS, const Object &RHS); +inline bool operator!=(const Object &LHS, const Object &RHS) { + return !(LHS == RHS); +} + +/// An Array is a JSON array, which contains heterogeneous JSON values. +/// It simulates std::vector<Value>. +class Array { + std::vector<Value> V; + +public: + using value_type = Value; + using iterator = std::vector<Value>::iterator; + using const_iterator = std::vector<Value>::const_iterator; + + Array() = default; + explicit Array(std::initializer_list<Value> Elements); + template <typename Collection> explicit Array(const Collection &C) { + for (const auto &V : C) + emplace_back(V); + } + + Value &operator[](size_t I); + const Value &operator[](size_t I) const; + Value &front(); + const Value &front() const; + Value &back(); + const Value &back() const; + Value *data(); + const Value *data() const; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + bool empty() const; + size_t size() const; + void reserve(size_t S); + + void clear(); + void push_back(const Value &E); + void push_back(Value &&E); + template <typename... Args> void emplace_back(Args &&...A); + void pop_back(); + iterator insert(const_iterator P, const Value &E); + iterator insert(const_iterator P, Value &&E); + template <typename It> iterator insert(const_iterator P, It A, It Z); + template <typename... Args> iterator emplace(const_iterator P, Args &&...A); + + friend bool operator==(const Array &L, const Array &R); +}; +inline bool operator!=(const Array &L, const Array &R) { return !(L == R); } + +/// A Value is an JSON value of unknown type. +/// They can be copied, but should generally be moved. +/// +/// === Composing values === +/// +/// You can implicitly construct Values from: +/// - strings: std::string, SmallString, formatv, StringRef, char* +/// (char*, and StringRef are references, not copies!) +/// - numbers +/// - booleans +/// - null: nullptr +/// - arrays: {"foo", 42.0, false} +/// - serializable things: types with toJSON(const T&)->Value, found by ADL +/// +/// They can also be constructed from object/array helpers: +/// - json::Object is a type like map<ObjectKey, Value> +/// - json::Array is a type like vector<Value> +/// These can be list-initialized, or used to build up collections in a loop. +/// json::ary(Collection) converts all items in a collection to Values. +/// +/// === Inspecting values === +/// +/// Each Value is one of the JSON kinds: +/// null (nullptr_t) +/// boolean (bool) +/// number (double, int64 or uint64) +/// string (StringRef) +/// array (json::Array) +/// object (json::Object) +/// +/// The kind can be queried directly, or implicitly via the typed accessors: +/// if (std::optional<StringRef> S = E.getAsString() +/// assert(E.kind() == Value::String); +/// +/// Array and Object also have typed indexing accessors for easy traversal: +/// Expected<Value> E = parse(R"( {"options": {"font": "sans-serif"}} )"); +/// if (Object* O = E->getAsObject()) +/// if (Object* Opts = O->getObject("options")) +/// if (std::optional<StringRef> Font = Opts->getString("font")) +/// assert(Opts->at("font").kind() == Value::String); +/// +/// === Converting JSON values to C++ types === +/// +/// The convention is to have a deserializer function findable via ADL: +/// fromJSON(const json::Value&, T&, Path) -> bool +/// +/// The return value indicates overall success, and Path is used for precise +/// error reporting. (The Path::Root passed in at the top level fromJSON call +/// captures any nested error and can render it in context). +/// If conversion fails, fromJSON calls Path::report() and immediately returns. +/// This ensures that the first fatal error survives. +/// +/// Deserializers are provided for: +/// - bool +/// - int and int64_t +/// - double +/// - std::string +/// - vector<T>, where T is deserializable +/// - map<string, T>, where T is deserializable +/// - std::optional<T>, where T is deserializable +/// ObjectMapper can help writing fromJSON() functions for object types. +/// +/// For conversion in the other direction, the serializer function is: +/// toJSON(const T&) -> json::Value +/// If this exists, then it also allows constructing Value from T, and can +/// be used to serialize vector<T>, map<string, T>, and std::optional<T>. +/// +/// === Serialization === +/// +/// Values can be serialized to JSON: +/// 1) raw_ostream << Value // Basic formatting. +/// 2) raw_ostream << formatv("{0}", Value) // Basic formatting. +/// 3) raw_ostream << formatv("{0:2}", Value) // Pretty-print with indent 2. +/// +/// And parsed: +/// Expected<Value> E = json::parse("[1, 2, null]"); +/// assert(E && E->kind() == Value::Array); +class Value { +public: + enum Kind { + Null, + Boolean, + /// Number values can store both int64s and doubles at full precision, + /// depending on what they were constructed/parsed from. + Number, + String, + Array, + Object, + }; + + // It would be nice to have Value() be null. But that would make {} null too. + Value(const Value &M) { copyFrom(M); } + Value(Value &&M) { moveFrom(std::move(M)); } + Value(std::initializer_list<Value> Elements); + Value(json::Array &&Elements) : Type(T_Array) { + create<json::Array>(std::move(Elements)); + } + template <typename Elt> + Value(const std::vector<Elt> &C) : Value(json::Array(C)) {} + Value(json::Object &&Properties) : Type(T_Object) { + create<json::Object>(std::move(Properties)); + } + template <typename Elt> + Value(const std::map<std::string, Elt> &C) : Value(json::Object(C)) {} + // Strings: types with value semantics. Must be valid UTF-8. + Value(std::string V) : Type(T_String) { + if (LLVM_UNLIKELY(!isUTF8(V))) { + assert(false && "Invalid UTF-8 in value used as JSON"); + V = fixUTF8(std::move(V)); + } + create<std::string>(std::move(V)); + } + Value(const llvm::SmallVectorImpl<char> &V) + : Value(std::string(V.begin(), V.end())) {} + Value(const llvm::formatv_object_base &V) : Value(V.str()) {} + // Strings: types with reference semantics. Must be valid UTF-8. + Value(StringRef V) : Type(T_StringRef) { + create<llvm::StringRef>(V); + if (LLVM_UNLIKELY(!isUTF8(V))) { + assert(false && "Invalid UTF-8 in value used as JSON"); + *this = Value(fixUTF8(V)); + } + } + Value(const char *V) : Value(StringRef(V)) {} + Value(std::nullptr_t) : Type(T_Null) {} + // Boolean (disallow implicit conversions). + // (The last template parameter is a dummy to keep templates distinct.) + template <typename T, + typename = std::enable_if_t<std::is_same<T, bool>::value>, + bool = false> + Value(T B) : Type(T_Boolean) { + create<bool>(B); + } + + // Unsigned 64-bit long integers. + template <typename T, + typename = std::enable_if_t<std::is_same<T, uint64_t>::value>, + bool = false, bool = false> + Value(T V) : Type(T_UINT64) { + create<uint64_t>(uint64_t{V}); + } + + // Integers (except boolean and uint64_t). + // Must be non-narrowing convertible to int64_t. + template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>, + typename = std::enable_if_t<!std::is_same<T, bool>::value>, + typename = std::enable_if_t<!std::is_same<T, uint64_t>::value>> + Value(T I) : Type(T_Integer) { + create<int64_t>(int64_t{I}); + } + // Floating point. Must be non-narrowing convertible to double. + template <typename T, + typename = std::enable_if_t<std::is_floating_point<T>::value>, + double * = nullptr> + Value(T D) : Type(T_Double) { + create<double>(double{D}); + } + // Serializable types: with a toJSON(const T&)->Value function, found by ADL. + template <typename T, + typename = std::enable_if_t<std::is_same< + Value, decltype(toJSON(*(const T *)nullptr))>::value>, + Value * = nullptr> + Value(const T &V) : Value(toJSON(V)) {} + + Value &operator=(const Value &M) { + destroy(); + copyFrom(M); + return *this; + } + Value &operator=(Value &&M) { + destroy(); + moveFrom(std::move(M)); + return *this; + } + ~Value() { destroy(); } + + Kind kind() const { + switch (Type) { + case T_Null: + return Null; + case T_Boolean: + return Boolean; + case T_Double: + case T_Integer: + case T_UINT64: + return Number; + case T_String: + case T_StringRef: + return String; + case T_Object: + return Object; + case T_Array: + return Array; + } + llvm_unreachable("Unknown kind"); + } + + // Typed accessors return std::nullopt/nullptr if the Value is not of this + // type. + std::optional<std::nullptr_t> getAsNull() const { + if (LLVM_LIKELY(Type == T_Null)) + return nullptr; + return std::nullopt; + } + std::optional<bool> getAsBoolean() const { + if (LLVM_LIKELY(Type == T_Boolean)) + return as<bool>(); + return std::nullopt; + } + std::optional<double> getAsNumber() const { + if (LLVM_LIKELY(Type == T_Double)) + return as<double>(); + if (LLVM_LIKELY(Type == T_Integer)) + return as<int64_t>(); + if (LLVM_LIKELY(Type == T_UINT64)) + return as<uint64_t>(); + return std::nullopt; + } + // Succeeds if the Value is a Number, and exactly representable as int64_t. + std::optional<int64_t> getAsInteger() const { + if (LLVM_LIKELY(Type == T_Integer)) + return as<int64_t>(); + if (LLVM_LIKELY(Type == T_Double)) { + double D = as<double>(); + if (LLVM_LIKELY(std::modf(D, &D) == 0.0 && + D >= double(std::numeric_limits<int64_t>::min()) && + D <= double(std::numeric_limits<int64_t>::max()))) + return D; + } + return std::nullopt; + } + std::optional<uint64_t> getAsUINT64() const { + if (Type == T_UINT64) + return as<uint64_t>(); + else if (Type == T_Integer) { + int64_t N = as<int64_t>(); + if (N >= 0) + return as<uint64_t>(); + } + return std::nullopt; + } + std::optional<llvm::StringRef> getAsString() const { + if (Type == T_String) + return llvm::StringRef(as<std::string>()); + if (LLVM_LIKELY(Type == T_StringRef)) + return as<llvm::StringRef>(); + return std::nullopt; + } + const json::Object *getAsObject() const { + return LLVM_LIKELY(Type == T_Object) ? &as<json::Object>() : nullptr; + } + json::Object *getAsObject() { + return LLVM_LIKELY(Type == T_Object) ? &as<json::Object>() : nullptr; + } + const json::Array *getAsArray() const { + return LLVM_LIKELY(Type == T_Array) ? &as<json::Array>() : nullptr; + } + json::Array *getAsArray() { + return LLVM_LIKELY(Type == T_Array) ? &as<json::Array>() : nullptr; + } + +private: + void destroy(); + void copyFrom(const Value &M); + // We allow moving from *const* Values, by marking all members as mutable! + // This hack is needed to support initializer-list syntax efficiently. + // (std::initializer_list<T> is a container of const T). + void moveFrom(const Value &&M); + friend class Array; + friend class Object; + + template <typename T, typename... U> void create(U &&... V) { + new (reinterpret_cast<T *>(&Union)) T(std::forward<U>(V)...); + } + template <typename T> T &as() const { + // Using this two-step static_cast via void * instead of reinterpret_cast + // silences a -Wstrict-aliasing false positive from GCC6 and earlier. + void *Storage = static_cast<void *>(&Union); + return *static_cast<T *>(Storage); + } + + friend class OStream; + + enum ValueType : char16_t { + T_Null, + T_Boolean, + T_Double, + T_Integer, + T_UINT64, + T_StringRef, + T_String, + T_Object, + T_Array, + }; + // All members mutable, see moveFrom(). + mutable ValueType Type; + mutable llvm::AlignedCharArrayUnion<bool, double, int64_t, uint64_t, + llvm::StringRef, std::string, json::Array, + json::Object> + Union; + friend bool operator==(const Value &, const Value &); +}; + +bool operator==(const Value &, const Value &); +inline bool operator!=(const Value &L, const Value &R) { return !(L == R); } + +// Array Methods +inline Value &Array::operator[](size_t I) { return V[I]; } +inline const Value &Array::operator[](size_t I) const { return V[I]; } +inline Value &Array::front() { return V.front(); } +inline const Value &Array::front() const { return V.front(); } +inline Value &Array::back() { return V.back(); } +inline const Value &Array::back() const { return V.back(); } +inline Value *Array::data() { return V.data(); } +inline const Value *Array::data() const { return V.data(); } + +inline typename Array::iterator Array::begin() { return V.begin(); } +inline typename Array::const_iterator Array::begin() const { return V.begin(); } +inline typename Array::iterator Array::end() { return V.end(); } +inline typename Array::const_iterator Array::end() const { return V.end(); } + +inline bool Array::empty() const { return V.empty(); } +inline size_t Array::size() const { return V.size(); } +inline void Array::reserve(size_t S) { V.reserve(S); } + +inline void Array::clear() { V.clear(); } +inline void Array::push_back(const Value &E) { V.push_back(E); } +inline void Array::push_back(Value &&E) { V.push_back(std::move(E)); } +template <typename... Args> inline void Array::emplace_back(Args &&...A) { + V.emplace_back(std::forward<Args>(A)...); +} +inline void Array::pop_back() { V.pop_back(); } +inline typename Array::iterator Array::insert(const_iterator P, const Value &E) { + return V.insert(P, E); +} +inline typename Array::iterator Array::insert(const_iterator P, Value &&E) { + return V.insert(P, std::move(E)); +} +template <typename It> +inline typename Array::iterator Array::insert(const_iterator P, It A, It Z) { + return V.insert(P, A, Z); +} +template <typename... Args> +inline typename Array::iterator Array::emplace(const_iterator P, Args &&...A) { + return V.emplace(P, std::forward<Args>(A)...); +} +inline bool operator==(const Array &L, const Array &R) { return L.V == R.V; } + +/// ObjectKey is a used to capture keys in Object. Like Value but: +/// - only strings are allowed +/// - it's optimized for the string literal case (Owned == nullptr) +/// Like Value, strings must be UTF-8. See isUTF8 documentation for details. +class ObjectKey { +public: + ObjectKey(const char *S) : ObjectKey(StringRef(S)) {} + ObjectKey(std::string S) : Owned(new std::string(std::move(S))) { + if (LLVM_UNLIKELY(!isUTF8(*Owned))) { + assert(false && "Invalid UTF-8 in value used as JSON"); + *Owned = fixUTF8(std::move(*Owned)); + } + Data = *Owned; + } + ObjectKey(llvm::StringRef S) : Data(S) { + if (LLVM_UNLIKELY(!isUTF8(Data))) { + assert(false && "Invalid UTF-8 in value used as JSON"); + *this = ObjectKey(fixUTF8(S)); + } + } + ObjectKey(const llvm::SmallVectorImpl<char> &V) + : ObjectKey(std::string(V.begin(), V.end())) {} + ObjectKey(const llvm::formatv_object_base &V) : ObjectKey(V.str()) {} + + ObjectKey(const ObjectKey &C) { *this = C; } + ObjectKey(ObjectKey &&C) : ObjectKey(static_cast<const ObjectKey &&>(C)) {} + ObjectKey &operator=(const ObjectKey &C) { + if (C.Owned) { + Owned.reset(new std::string(*C.Owned)); + Data = *Owned; + } else { + Data = C.Data; + } + return *this; + } + ObjectKey &operator=(ObjectKey &&) = default; + + operator llvm::StringRef() const { return Data; } + std::string str() const { return Data.str(); } + +private: + // FIXME: this is unneccesarily large (3 pointers). Pointer + length + owned + // could be 2 pointers at most. + std::unique_ptr<std::string> Owned; + llvm::StringRef Data; +}; + +inline bool operator==(const ObjectKey &L, const ObjectKey &R) { + return llvm::StringRef(L) == llvm::StringRef(R); +} +inline bool operator!=(const ObjectKey &L, const ObjectKey &R) { + return !(L == R); +} +inline bool operator<(const ObjectKey &L, const ObjectKey &R) { + return StringRef(L) < StringRef(R); +} + +struct Object::KV { + ObjectKey K; + Value V; +}; + +inline Object::Object(std::initializer_list<KV> Properties) { + for (const auto &P : Properties) { + auto R = try_emplace(P.K, nullptr); + if (R.second) + R.first->getSecond().moveFrom(std::move(P.V)); + } +} +inline std::pair<Object::iterator, bool> Object::insert(KV E) { + return try_emplace(std::move(E.K), std::move(E.V)); +} +inline bool Object::erase(StringRef K) { + return M.erase(ObjectKey(K)); +} + +/// A "cursor" marking a position within a Value. +/// The Value is a tree, and this is the path from the root to the current node. +/// This is used to associate errors with particular subobjects. +class Path { +public: + class Root; + + /// Records that the value at the current path is invalid. + /// Message is e.g. "expected number" and becomes part of the final error. + /// This overwrites any previously written error message in the root. + void report(llvm::StringLiteral Message); + + /// The root may be treated as a Path. + Path(Root &R) : Parent(nullptr), Seg(&R) {} + /// Derives a path for an array element: this[Index] + Path index(unsigned Index) const { return Path(this, Segment(Index)); } + /// Derives a path for an object field: this.Field + Path field(StringRef Field) const { return Path(this, Segment(Field)); } + +private: + /// One element in a JSON path: an object field (.foo) or array index [27]. + /// Exception: the root Path encodes a pointer to the Path::Root. + class Segment { + uintptr_t Pointer; + unsigned Offset; + + public: + Segment() = default; + Segment(Root *R) : Pointer(reinterpret_cast<uintptr_t>(R)) {} + Segment(llvm::StringRef Field) + : Pointer(reinterpret_cast<uintptr_t>(Field.data())), + Offset(static_cast<unsigned>(Field.size())) {} + Segment(unsigned Index) : Pointer(0), Offset(Index) {} + + bool isField() const { return Pointer != 0; } + StringRef field() const { + return StringRef(reinterpret_cast<const char *>(Pointer), Offset); + } + unsigned index() const { return Offset; } + Root *root() const { return reinterpret_cast<Root *>(Pointer); } + }; + + const Path *Parent; + Segment Seg; + + Path(const Path *Parent, Segment S) : Parent(Parent), Seg(S) {} +}; + +/// The root is the trivial Path to the root value. +/// It also stores the latest reported error and the path where it occurred. +class Path::Root { + llvm::StringRef Name; + llvm::StringLiteral ErrorMessage; + std::vector<Path::Segment> ErrorPath; // Only valid in error state. Reversed. + + friend void Path::report(llvm::StringLiteral Message); + +public: + Root(llvm::StringRef Name = "") : Name(Name), ErrorMessage("") {} + // No copy/move allowed as there are incoming pointers. + Root(Root &&) = delete; + Root &operator=(Root &&) = delete; + Root(const Root &) = delete; + Root &operator=(const Root &) = delete; + + /// Returns the last error reported, or else a generic error. + Error getError() const; + /// Print the root value with the error shown inline as a comment. + /// Unrelated parts of the value are elided for brevity, e.g. + /// { + /// "id": 42, + /// "name": /* expected string */ null, + /// "properties": { ... } + /// } + void printErrorContext(const Value &, llvm::raw_ostream &) const; +}; + +// Standard deserializers are provided for primitive types. +// See comments on Value. +inline bool fromJSON(const Value &E, std::string &Out, Path P) { + if (auto S = E.getAsString()) { + Out = std::string(*S); + return true; + } + P.report("expected string"); + return false; +} +inline bool fromJSON(const Value &E, int &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected integer"); + return false; +} +inline bool fromJSON(const Value &E, int64_t &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected integer"); + return false; +} +inline bool fromJSON(const Value &E, double &Out, Path P) { + if (auto S = E.getAsNumber()) { + Out = *S; + return true; + } + P.report("expected number"); + return false; +} +inline bool fromJSON(const Value &E, bool &Out, Path P) { + if (auto S = E.getAsBoolean()) { + Out = *S; + return true; + } + P.report("expected boolean"); + return false; +} +inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { + if (auto S = E.getAsUINT64()) { + Out = *S; + return true; + } + P.report("expected uint64_t"); + return false; +} +inline bool fromJSON(const Value &E, std::nullptr_t &Out, Path P) { + if (auto S = E.getAsNull()) { + Out = *S; + return true; + } + P.report("expected null"); + return false; +} +template <typename T> +bool fromJSON(const Value &E, std::optional<T> &Out, Path P) { + if (E.getAsNull()) { + Out = std::nullopt; + return true; + } + T Result; + if (!fromJSON(E, Result, P)) + return false; + Out = std::move(Result); + return true; +} +template <typename T> +bool fromJSON(const Value &E, std::vector<T> &Out, Path P) { + if (auto *A = E.getAsArray()) { + Out.clear(); + Out.resize(A->size()); + for (size_t I = 0; I < A->size(); ++I) + if (!fromJSON((*A)[I], Out[I], P.index(I))) + return false; + return true; + } + P.report("expected array"); + return false; +} +template <typename T> +bool fromJSON(const Value &E, std::map<std::string, T> &Out, Path P) { + if (auto *O = E.getAsObject()) { + Out.clear(); + for (const auto &KV : *O) + if (!fromJSON(KV.second, Out[std::string(llvm::StringRef(KV.first))], + P.field(KV.first))) + return false; + return true; + } + P.report("expected object"); + return false; +} + +// Allow serialization of std::optional<T> for supported T. +template <typename T> Value toJSON(const std::optional<T> &Opt) { + return Opt ? Value(*Opt) : Value(nullptr); +} + +/// Helper for mapping JSON objects onto protocol structs. +/// +/// Example: +/// \code +/// bool fromJSON(const Value &E, MyStruct &R, Path P) { +/// ObjectMapper O(E, P); +/// // When returning false, error details were already reported. +/// return O && O.map("mandatory_field", R.MandatoryField) && +/// O.mapOptional("optional_field", R.OptionalField); +/// } +/// \endcode +class ObjectMapper { +public: + /// If O is not an object, this mapper is invalid and an error is reported. + ObjectMapper(const Value &E, Path P) : O(E.getAsObject()), P(P) { + if (!O) + P.report("expected object"); + } + + /// True if the expression is an object. + /// Must be checked before calling map(). + operator bool() const { return O; } + + /// Maps a property to a field. + /// If the property is missing or invalid, reports an error. + template <typename T> bool map(StringLiteral Prop, T &Out) { + assert(*this && "Must check this is an object before calling map()"); + if (const Value *E = O->get(Prop)) + return fromJSON(*E, Out, P.field(Prop)); + P.field(Prop).report("missing value"); + return false; + } + + /// Maps a property to a field, if it exists. + /// If the property exists and is invalid, reports an error. + /// (Optional requires special handling, because missing keys are OK). + template <typename T> bool map(StringLiteral Prop, std::optional<T> &Out) { + assert(*this && "Must check this is an object before calling map()"); + if (const Value *E = O->get(Prop)) + return fromJSON(*E, Out, P.field(Prop)); + Out = std::nullopt; + return true; + } + + /// Maps a property to a field, if it exists. + /// If the property exists and is invalid, reports an error. + /// If the property does not exist, Out is unchanged. + template <typename T> bool mapOptional(StringLiteral Prop, T &Out) { + assert(*this && "Must check this is an object before calling map()"); + if (const Value *E = O->get(Prop)) + return fromJSON(*E, Out, P.field(Prop)); + return true; + } + +private: + const Object *O; + Path P; +}; + +/// Parses the provided JSON source, or returns a ParseError. +/// The returned Value is self-contained and owns its strings (they do not refer +/// to the original source). +llvm::Expected<Value> parse(llvm::StringRef JSON); + +class ParseError : public llvm::ErrorInfo<ParseError> { + const char *Msg; + unsigned Line, Column, Offset; + +public: + static char ID; + ParseError(const char *Msg, unsigned Line, unsigned Column, unsigned Offset) + : Msg(Msg), Line(Line), Column(Column), Offset(Offset) {} + void log(llvm::raw_ostream &OS) const override { + OS << llvm::formatv("[{0}:{1}, byte={2}]: {3}", Line, Column, Offset, Msg); + } + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } +}; + +/// Version of parse() that converts the parsed value to the type T. +/// RootName describes the root object and is used in error messages. +template <typename T> +Expected<T> parse(const llvm::StringRef &JSON, const char *RootName = "") { + auto V = parse(JSON); + if (!V) + return V.takeError(); + Path::Root R(RootName); + T Result; + if (fromJSON(*V, Result, R)) + return std::move(Result); + return R.getError(); +} + +/// json::OStream allows writing well-formed JSON without materializing +/// all structures as json::Value ahead of time. +/// It's faster, lower-level, and less safe than OS << json::Value. +/// It also allows emitting more constructs, such as comments. +/// +/// Only one "top-level" object can be written to a stream. +/// Simplest usage involves passing lambdas (Blocks) to fill in containers: +/// +/// json::OStream J(OS); +/// J.array([&]{ +/// for (const Event &E : Events) +/// J.object([&] { +/// J.attribute("timestamp", int64_t(E.Time)); +/// J.attributeArray("participants", [&] { +/// for (const Participant &P : E.Participants) +/// J.value(P.toString()); +/// }); +/// }); +/// }); +/// +/// This would produce JSON like: +/// +/// [ +/// { +/// "timestamp": 19287398741, +/// "participants": [ +/// "King Kong", +/// "Miley Cyrus", +/// "Cleopatra" +/// ] +/// }, +/// ... +/// ] +/// +/// The lower level begin/end methods (arrayBegin()) are more flexible but +/// care must be taken to pair them correctly: +/// +/// json::OStream J(OS); +// J.arrayBegin(); +/// for (const Event &E : Events) { +/// J.objectBegin(); +/// J.attribute("timestamp", int64_t(E.Time)); +/// J.attributeBegin("participants"); +/// for (const Participant &P : E.Participants) +/// J.value(P.toString()); +/// J.attributeEnd(); +/// J.objectEnd(); +/// } +/// J.arrayEnd(); +/// +/// If the call sequence isn't valid JSON, asserts will fire in debug mode. +/// This can be mismatched begin()/end() pairs, trying to emit attributes inside +/// an array, and so on. +/// With asserts disabled, this is undefined behavior. +class OStream { + public: + using Block = llvm::function_ref<void()>; + // If IndentSize is nonzero, output is pretty-printed. + explicit OStream(llvm::raw_ostream &OS, unsigned IndentSize = 0) + : OS(OS), IndentSize(IndentSize) { + Stack.emplace_back(); + } + ~OStream() { + assert(Stack.size() == 1 && "Unmatched begin()/end()"); + assert(Stack.back().Ctx == Singleton); + assert(Stack.back().HasValue && "Did not write top-level value"); + } + + /// Flushes the underlying ostream. OStream does not buffer internally. + void flush() { OS.flush(); } + + // High level functions to output a value. + // Valid at top-level (exactly once), in an attribute value (exactly once), + // or in an array (any number of times). + + /// Emit a self-contained value (number, string, vector<string> etc). + void value(const Value &V); + /// Emit an array whose elements are emitted in the provided Block. + void array(Block Contents) { + arrayBegin(); + Contents(); + arrayEnd(); + } + /// Emit an object whose elements are emitted in the provided Block. + void object(Block Contents) { + objectBegin(); + Contents(); + objectEnd(); + } + /// Emit an externally-serialized value. + /// The caller must write exactly one valid JSON value to the provided stream. + /// No validation or formatting of this value occurs. + void rawValue(llvm::function_ref<void(raw_ostream &)> Contents) { + rawValueBegin(); + Contents(OS); + rawValueEnd(); + } + void rawValue(llvm::StringRef Contents) { + rawValue([&](raw_ostream &OS) { OS << Contents; }); + } + /// Emit a JavaScript comment associated with the next printed value. + /// The string must be valid until the next attribute or value is emitted. + /// Comments are not part of standard JSON, and many parsers reject them! + void comment(llvm::StringRef); + + // High level functions to output object attributes. + // Valid only within an object (any number of times). + + /// Emit an attribute whose value is self-contained (number, vector<int> etc). + void attribute(llvm::StringRef Key, const Value& Contents) { + attributeImpl(Key, [&] { value(Contents); }); + } + /// Emit an attribute whose value is an array with elements from the Block. + void attributeArray(llvm::StringRef Key, Block Contents) { + attributeImpl(Key, [&] { array(Contents); }); + } + /// Emit an attribute whose value is an object with attributes from the Block. + void attributeObject(llvm::StringRef Key, Block Contents) { + attributeImpl(Key, [&] { object(Contents); }); + } + + // Low-level begin/end functions to output arrays, objects, and attributes. + // Must be correctly paired. Allowed contexts are as above. + + void arrayBegin(); + void arrayEnd(); + void objectBegin(); + void objectEnd(); + void attributeBegin(llvm::StringRef Key); + void attributeEnd(); + raw_ostream &rawValueBegin(); + void rawValueEnd(); + +private: + void attributeImpl(llvm::StringRef Key, Block Contents) { + attributeBegin(Key); + Contents(); + attributeEnd(); + } + + void valueBegin(); + void flushComment(); + void newline(); + + enum Context { + Singleton, // Top level, or object attribute. + Array, + Object, + RawValue, // External code writing a value to OS directly. + }; + struct State { + Context Ctx = Singleton; + bool HasValue = false; + }; + llvm::SmallVector<State, 16> Stack; // Never empty. + llvm::StringRef PendingComment; + llvm::raw_ostream &OS; + unsigned IndentSize; + unsigned Indent = 0; +}; + +/// Serializes this Value to JSON, writing it to the provided stream. +/// The formatting is compact (no extra whitespace) and deterministic. +/// For pretty-printing, use the formatv() format_provider below. +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Value &V) { + OStream(OS).value(V); + return OS; +} +} // namespace json + +/// Allow printing json::Value with formatv(). +/// The default style is basic/compact formatting, like operator<<. +/// A format string like formatv("{0:2}", Value) pretty-prints with indent 2. +template <> struct format_provider<llvm::json::Value> { + static void format(const llvm::json::Value &, raw_ostream &, StringRef); +}; +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/KnownBits.h b/contrib/libs/llvm16/include/llvm/Support/KnownBits.h new file mode 100644 index 00000000000..81a5113a812 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/KnownBits.h @@ -0,0 +1,478 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/KnownBits.h - Stores known zeros/ones -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains a class for representing known zeros and ones used by +// computeKnownBits. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_KNOWNBITS_H +#define LLVM_SUPPORT_KNOWNBITS_H + +#include "llvm/ADT/APInt.h" +#include <optional> + +namespace llvm { + +// Struct for tracking the known zeros and ones of a value. +struct KnownBits { + APInt Zero; + APInt One; + +private: + // Internal constructor for creating a KnownBits from two APInts. + KnownBits(APInt Zero, APInt One) + : Zero(std::move(Zero)), One(std::move(One)) {} + +public: + // Default construct Zero and One. + KnownBits() = default; + + /// Create a known bits object of BitWidth bits initialized to unknown. + KnownBits(unsigned BitWidth) : Zero(BitWidth, 0), One(BitWidth, 0) {} + + /// Get the bit width of this value. + unsigned getBitWidth() const { + assert(Zero.getBitWidth() == One.getBitWidth() && + "Zero and One should have the same width!"); + return Zero.getBitWidth(); + } + + /// Returns true if there is conflicting information. + bool hasConflict() const { return Zero.intersects(One); } + + /// Returns true if we know the value of all bits. + bool isConstant() const { + assert(!hasConflict() && "KnownBits conflict!"); + return Zero.countPopulation() + One.countPopulation() == getBitWidth(); + } + + /// Returns the value when all bits have a known value. This just returns One + /// with a protective assertion. + const APInt &getConstant() const { + assert(isConstant() && "Can only get value when all bits are known"); + return One; + } + + /// Returns true if we don't know any bits. + bool isUnknown() const { return Zero.isZero() && One.isZero(); } + + /// Resets the known state of all bits. + void resetAll() { + Zero.clearAllBits(); + One.clearAllBits(); + } + + /// Returns true if value is all zero. + bool isZero() const { + assert(!hasConflict() && "KnownBits conflict!"); + return Zero.isAllOnes(); + } + + /// Returns true if value is all one bits. + bool isAllOnes() const { + assert(!hasConflict() && "KnownBits conflict!"); + return One.isAllOnes(); + } + + /// Make all bits known to be zero and discard any previous information. + void setAllZero() { + Zero.setAllBits(); + One.clearAllBits(); + } + + /// Make all bits known to be one and discard any previous information. + void setAllOnes() { + Zero.clearAllBits(); + One.setAllBits(); + } + + /// Returns true if this value is known to be negative. + bool isNegative() const { return One.isSignBitSet(); } + + /// Returns true if this value is known to be non-negative. + bool isNonNegative() const { return Zero.isSignBitSet(); } + + /// Returns true if this value is known to be non-zero. + bool isNonZero() const { return !One.isZero(); } + + /// Returns true if this value is known to be positive. + bool isStrictlyPositive() const { + return Zero.isSignBitSet() && !One.isZero(); + } + + /// Make this value negative. + void makeNegative() { + One.setSignBit(); + } + + /// Make this value non-negative. + void makeNonNegative() { + Zero.setSignBit(); + } + + /// Return the minimal unsigned value possible given these KnownBits. + APInt getMinValue() const { + // Assume that all bits that aren't known-ones are zeros. + return One; + } + + /// Return the minimal signed value possible given these KnownBits. + APInt getSignedMinValue() const { + // Assume that all bits that aren't known-ones are zeros. + APInt Min = One; + // Sign bit is unknown. + if (Zero.isSignBitClear()) + Min.setSignBit(); + return Min; + } + + /// Return the maximal unsigned value possible given these KnownBits. + APInt getMaxValue() const { + // Assume that all bits that aren't known-zeros are ones. + return ~Zero; + } + + /// Return the maximal signed value possible given these KnownBits. + APInt getSignedMaxValue() const { + // Assume that all bits that aren't known-zeros are ones. + APInt Max = ~Zero; + // Sign bit is unknown. + if (One.isSignBitClear()) + Max.clearSignBit(); + return Max; + } + + /// Return known bits for a truncation of the value we're tracking. + KnownBits trunc(unsigned BitWidth) const { + return KnownBits(Zero.trunc(BitWidth), One.trunc(BitWidth)); + } + + /// Return known bits for an "any" extension of the value we're tracking, + /// where we don't know anything about the extended bits. + KnownBits anyext(unsigned BitWidth) const { + return KnownBits(Zero.zext(BitWidth), One.zext(BitWidth)); + } + + /// Return known bits for a zero extension of the value we're tracking. + KnownBits zext(unsigned BitWidth) const { + unsigned OldBitWidth = getBitWidth(); + APInt NewZero = Zero.zext(BitWidth); + NewZero.setBitsFrom(OldBitWidth); + return KnownBits(NewZero, One.zext(BitWidth)); + } + + /// Return known bits for a sign extension of the value we're tracking. + KnownBits sext(unsigned BitWidth) const { + return KnownBits(Zero.sext(BitWidth), One.sext(BitWidth)); + } + + /// Return known bits for an "any" extension or truncation of the value we're + /// tracking. + KnownBits anyextOrTrunc(unsigned BitWidth) const { + if (BitWidth > getBitWidth()) + return anyext(BitWidth); + if (BitWidth < getBitWidth()) + return trunc(BitWidth); + return *this; + } + + /// Return known bits for a zero extension or truncation of the value we're + /// tracking. + KnownBits zextOrTrunc(unsigned BitWidth) const { + if (BitWidth > getBitWidth()) + return zext(BitWidth); + if (BitWidth < getBitWidth()) + return trunc(BitWidth); + return *this; + } + + /// Return known bits for a sign extension or truncation of the value we're + /// tracking. + KnownBits sextOrTrunc(unsigned BitWidth) const { + if (BitWidth > getBitWidth()) + return sext(BitWidth); + if (BitWidth < getBitWidth()) + return trunc(BitWidth); + return *this; + } + + /// Return known bits for a in-register sign extension of the value we're + /// tracking. + KnownBits sextInReg(unsigned SrcBitWidth) const; + + /// Insert the bits from a smaller known bits starting at bitPosition. + void insertBits(const KnownBits &SubBits, unsigned BitPosition) { + Zero.insertBits(SubBits.Zero, BitPosition); + One.insertBits(SubBits.One, BitPosition); + } + + /// Return a subset of the known bits from [bitPosition,bitPosition+numBits). + KnownBits extractBits(unsigned NumBits, unsigned BitPosition) const { + return KnownBits(Zero.extractBits(NumBits, BitPosition), + One.extractBits(NumBits, BitPosition)); + } + + /// Concatenate the bits from \p Lo onto the bottom of *this. This is + /// equivalent to: + /// (this->zext(NewWidth) << Lo.getBitWidth()) | Lo.zext(NewWidth) + KnownBits concat(const KnownBits &Lo) const { + return KnownBits(Zero.concat(Lo.Zero), One.concat(Lo.One)); + } + + /// Return KnownBits based on this, but updated given that the underlying + /// value is known to be greater than or equal to Val. + KnownBits makeGE(const APInt &Val) const; + + /// Returns the minimum number of trailing zero bits. + unsigned countMinTrailingZeros() const { + return Zero.countTrailingOnes(); + } + + /// Returns the minimum number of trailing one bits. + unsigned countMinTrailingOnes() const { + return One.countTrailingOnes(); + } + + /// Returns the minimum number of leading zero bits. + unsigned countMinLeadingZeros() const { + return Zero.countLeadingOnes(); + } + + /// Returns the minimum number of leading one bits. + unsigned countMinLeadingOnes() const { + return One.countLeadingOnes(); + } + + /// Returns the number of times the sign bit is replicated into the other + /// bits. + unsigned countMinSignBits() const { + if (isNonNegative()) + return countMinLeadingZeros(); + if (isNegative()) + return countMinLeadingOnes(); + // Every value has at least 1 sign bit. + return 1; + } + + /// Returns the maximum number of bits needed to represent all possible + /// signed values with these known bits. This is the inverse of the minimum + /// number of known sign bits. Examples for bitwidth 5: + /// 110?? --> 4 + /// 0000? --> 2 + unsigned countMaxSignificantBits() const { + return getBitWidth() - countMinSignBits() + 1; + } + + /// Returns the maximum number of trailing zero bits possible. + unsigned countMaxTrailingZeros() const { + return One.countTrailingZeros(); + } + + /// Returns the maximum number of trailing one bits possible. + unsigned countMaxTrailingOnes() const { + return Zero.countTrailingZeros(); + } + + /// Returns the maximum number of leading zero bits possible. + unsigned countMaxLeadingZeros() const { + return One.countLeadingZeros(); + } + + /// Returns the maximum number of leading one bits possible. + unsigned countMaxLeadingOnes() const { + return Zero.countLeadingZeros(); + } + + /// Returns the number of bits known to be one. + unsigned countMinPopulation() const { + return One.countPopulation(); + } + + /// Returns the maximum number of bits that could be one. + unsigned countMaxPopulation() const { + return getBitWidth() - Zero.countPopulation(); + } + + /// Returns the maximum number of bits needed to represent all possible + /// unsigned values with these known bits. This is the inverse of the + /// minimum number of leading zeros. + unsigned countMaxActiveBits() const { + return getBitWidth() - countMinLeadingZeros(); + } + + /// Create known bits from a known constant. + static KnownBits makeConstant(const APInt &C) { + return KnownBits(~C, C); + } + + /// Compute known bits common to LHS and RHS. + static KnownBits commonBits(const KnownBits &LHS, const KnownBits &RHS) { + return KnownBits(LHS.Zero & RHS.Zero, LHS.One & RHS.One); + } + + /// Return true if LHS and RHS have no common bits set. + static bool haveNoCommonBitsSet(const KnownBits &LHS, const KnownBits &RHS) { + return (LHS.Zero | RHS.Zero).isAllOnes(); + } + + /// Compute known bits resulting from adding LHS, RHS and a 1-bit Carry. + static KnownBits computeForAddCarry( + const KnownBits &LHS, const KnownBits &RHS, const KnownBits &Carry); + + /// Compute known bits resulting from adding LHS and RHS. + static KnownBits computeForAddSub(bool Add, bool NSW, const KnownBits &LHS, + KnownBits RHS); + + /// Compute known bits resulting from multiplying LHS and RHS. + static KnownBits mul(const KnownBits &LHS, const KnownBits &RHS, + bool NoUndefSelfMultiply = false); + + /// Compute known bits from sign-extended multiply-hi. + static KnownBits mulhs(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits from zero-extended multiply-hi. + static KnownBits mulhu(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for udiv(LHS, RHS). + static KnownBits udiv(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for urem(LHS, RHS). + static KnownBits urem(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for srem(LHS, RHS). + static KnownBits srem(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for umax(LHS, RHS). + static KnownBits umax(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for umin(LHS, RHS). + static KnownBits umin(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for smax(LHS, RHS). + static KnownBits smax(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for smin(LHS, RHS). + static KnownBits smin(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for shl(LHS, RHS). + /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS. + static KnownBits shl(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for lshr(LHS, RHS). + /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS. + static KnownBits lshr(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for ashr(LHS, RHS). + /// NOTE: RHS (shift amount) bitwidth doesn't need to be the same as LHS. + static KnownBits ashr(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_EQ result. + static std::optional<bool> eq(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_NE result. + static std::optional<bool> ne(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_UGT result. + static std::optional<bool> ugt(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_UGE result. + static std::optional<bool> uge(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_ULT result. + static std::optional<bool> ult(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_ULE result. + static std::optional<bool> ule(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_SGT result. + static std::optional<bool> sgt(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_SGE result. + static std::optional<bool> sge(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_SLT result. + static std::optional<bool> slt(const KnownBits &LHS, const KnownBits &RHS); + + /// Determine if these known bits always give the same ICMP_SLE result. + static std::optional<bool> sle(const KnownBits &LHS, const KnownBits &RHS); + + /// Update known bits based on ANDing with RHS. + KnownBits &operator&=(const KnownBits &RHS); + + /// Update known bits based on ORing with RHS. + KnownBits &operator|=(const KnownBits &RHS); + + /// Update known bits based on XORing with RHS. + KnownBits &operator^=(const KnownBits &RHS); + + /// Compute known bits for the absolute value. + KnownBits abs(bool IntMinIsPoison = false) const; + + KnownBits byteSwap() const { + return KnownBits(Zero.byteSwap(), One.byteSwap()); + } + + KnownBits reverseBits() const { + return KnownBits(Zero.reverseBits(), One.reverseBits()); + } + + bool operator==(const KnownBits &Other) const { + return Zero == Other.Zero && One == Other.One; + } + + bool operator!=(const KnownBits &Other) const { return !(*this == Other); } + + void print(raw_ostream &OS) const; + void dump() const; +}; + +inline KnownBits operator&(KnownBits LHS, const KnownBits &RHS) { + LHS &= RHS; + return LHS; +} + +inline KnownBits operator&(const KnownBits &LHS, KnownBits &&RHS) { + RHS &= LHS; + return std::move(RHS); +} + +inline KnownBits operator|(KnownBits LHS, const KnownBits &RHS) { + LHS |= RHS; + return LHS; +} + +inline KnownBits operator|(const KnownBits &LHS, KnownBits &&RHS) { + RHS |= LHS; + return std::move(RHS); +} + +inline KnownBits operator^(KnownBits LHS, const KnownBits &RHS) { + LHS ^= RHS; + return LHS; +} + +inline KnownBits operator^(const KnownBits &LHS, KnownBits &&RHS) { + RHS ^= LHS; + return std::move(RHS); +} + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/LEB128.h b/contrib/libs/llvm16/include/llvm/Support/LEB128.h new file mode 100644 index 00000000000..df428226bed --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/LEB128.h @@ -0,0 +1,219 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/LEB128.h - [SU]LEB128 utility functions -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares some utility functions for encoding SLEB128 and +// ULEB128 values. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_LEB128_H +#define LLVM_SUPPORT_LEB128_H + +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// Utility function to encode a SLEB128 value to an output stream. Returns +/// the length in bytes of the encoded value. +inline unsigned encodeSLEB128(int64_t Value, raw_ostream &OS, + 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)))); + 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 (Count < PadTo) { + uint8_t PadValue = Value < 0 ? 0x7f : 0x00; + for (; Count < PadTo - 1; ++Count) + OS << char(PadValue | 0x80); + OS << char(PadValue); + Count++; + } + return Count; +} + +/// 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 PadTo = 0) { + uint8_t *orig_p = p; + unsigned Count = 0; + bool More; + 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)))); + 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 (Count < PadTo) { + uint8_t PadValue = Value < 0 ? 0x7f : 0x00; + for (; Count < PadTo - 1; ++Count) + *p++ = (PadValue | 0x80); + *p++ = PadValue; + } + return (unsigned)(p - orig_p); +} + +/// Utility function to encode a ULEB128 value to an output stream. Returns +/// the length in bytes of the encoded value. +inline unsigned encodeULEB128(uint64_t Value, raw_ostream &OS, + unsigned PadTo = 0) { + unsigned Count = 0; + do { + uint8_t Byte = Value & 0x7f; + Value >>= 7; + 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 (Count < PadTo) { + for (; Count < PadTo - 1; ++Count) + OS << '\x80'; + OS << '\x00'; + Count++; + } + return 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 PadTo = 0) { + uint8_t *orig_p = p; + unsigned Count = 0; + do { + uint8_t Byte = Value & 0x7f; + Value >>= 7; + 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 (Count < PadTo) { + for (; Count < PadTo - 1; ++Count) + *p++ = '\x80'; + *p++ = '\x00'; + } + + return (unsigned)(p - orig_p); +} + +/// Utility function to decode a ULEB128 value. +inline uint64_t decodeULEB128(const uint8_t *p, unsigned *n = nullptr, + const uint8_t *end = nullptr, + const char **error = nullptr) { + const uint8_t *orig_p = p; + uint64_t Value = 0; + unsigned Shift = 0; + if (error) + *error = nullptr; + do { + if (p == end) { + if (error) + *error = "malformed uleb128, extends past end"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } + uint64_t Slice = *p & 0x7f; + if ((Shift >= 64 && Slice != 0) || Slice << Shift >> Shift != Slice) { + if (error) + *error = "uleb128 too big for uint64"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } + Value += Slice << Shift; + Shift += 7; + } while (*p++ >= 128); + if (n) + *n = (unsigned)(p - orig_p); + return Value; +} + +/// Utility function to decode a SLEB128 value. +inline int64_t decodeSLEB128(const uint8_t *p, unsigned *n = nullptr, + const uint8_t *end = nullptr, + const char **error = nullptr) { + const uint8_t *orig_p = p; + int64_t Value = 0; + unsigned Shift = 0; + uint8_t Byte; + if (error) + *error = nullptr; + do { + if (p == end) { + if (error) + *error = "malformed sleb128, extends past end"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } + Byte = *p; + uint64_t Slice = Byte & 0x7f; + if ((Shift >= 64 && Slice != (Value < 0 ? 0x7f : 0x00)) || + (Shift == 63 && Slice != 0 && Slice != 0x7f)) { + if (error) + *error = "sleb128 too big for int64"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } + Value |= Slice << Shift; + Shift += 7; + ++p; + } while (Byte >= 128); + // Sign extend negative numbers if needed. + if (Shift < 64 && (Byte & 0x40)) + Value |= (-1ULL) << Shift; + if (n) + *n = (unsigned)(p - orig_p); + return Value; +} + +/// Utility function to get the size of the ULEB128-encoded value. +extern unsigned getULEB128Size(uint64_t Value); + +/// Utility function to get the size of the SLEB128-encoded value. +extern unsigned getSLEB128Size(int64_t Value); + +} // namespace llvm + +#endif // LLVM_SUPPORT_LEB128_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/LICENSE.TXT b/contrib/libs/llvm16/include/llvm/Support/LICENSE.TXT new file mode 100644 index 00000000000..3f4dbf402b7 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/LICENSE.TXT @@ -0,0 +1,6 @@ +LLVM System Interface Library +------------------------------------------------------------------------------- + +Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +See https://llvm.org/LICENSE.txt for license information. +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception diff --git a/contrib/libs/llvm16/include/llvm/Support/LineIterator.h b/contrib/libs/llvm16/include/llvm/Support/LineIterator.h new file mode 100644 index 00000000000..f5c89d78c1f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/LineIterator.h @@ -0,0 +1,109 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- LineIterator.h - Iterator to read a text buffer's lines --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_LINEITERATOR_H +#define LLVM_SUPPORT_LINEITERATOR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/MemoryBufferRef.h" +#include <iterator> +#include <optional> + +namespace llvm { + +class MemoryBuffer; + +/// A forward iterator which reads text lines from a buffer. +/// +/// This class provides a forward iterator interface for reading one line at +/// a time from a buffer. When default constructed the iterator will be the +/// "end" iterator. +/// +/// The iterator is aware of what line number it is currently processing. It +/// strips blank lines by default, and comment lines given a comment-starting +/// character. +/// +/// Note that this iterator requires the buffer to be nul terminated. +class line_iterator { + std::optional<MemoryBufferRef> Buffer; + char CommentMarker = '\0'; + bool SkipBlanks = true; + + unsigned LineNumber = 1; + StringRef CurrentLine; + +public: + using iterator_category = std::forward_iterator_tag; + using value_type = StringRef; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + + /// Default construct an "end" iterator. + line_iterator() = default; + + /// Construct a new iterator around an unowned memory buffer. + explicit line_iterator(const MemoryBufferRef &Buffer, bool SkipBlanks = true, + char CommentMarker = '\0'); + + /// Construct a new iterator around some memory buffer. + explicit line_iterator(const MemoryBuffer &Buffer, bool SkipBlanks = true, + char CommentMarker = '\0'); + + /// Return true if we've reached EOF or are an "end" iterator. + bool is_at_eof() const { return !Buffer; } + + /// Return true if we're an "end" iterator or have reached EOF. + bool is_at_end() const { return is_at_eof(); } + + /// Return the current line number. May return any number at EOF. + int64_t line_number() const { return LineNumber; } + + /// Advance to the next (non-empty, non-comment) line. + line_iterator &operator++() { + advance(); + return *this; + } + line_iterator operator++(int) { + line_iterator tmp(*this); + advance(); + return tmp; + } + + /// Get the current line as a \c StringRef. + StringRef operator*() const { return CurrentLine; } + const StringRef *operator->() const { return &CurrentLine; } + + friend bool operator==(const line_iterator &LHS, const line_iterator &RHS) { + return LHS.Buffer == RHS.Buffer && + LHS.CurrentLine.begin() == RHS.CurrentLine.begin(); + } + + friend bool operator!=(const line_iterator &LHS, const line_iterator &RHS) { + return !(LHS == RHS); + } + +private: + /// Advance the iterator to the next line. + void advance(); +}; +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Locale.h b/contrib/libs/llvm16/include/llvm/Support/Locale.h new file mode 100644 index 00000000000..e830d2f9bd6 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Locale.h @@ -0,0 +1,28 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#ifndef LLVM_SUPPORT_LOCALE_H +#define LLVM_SUPPORT_LOCALE_H + +namespace llvm { +class StringRef; + +namespace sys { +namespace locale { + +int columnWidth(StringRef s); +bool isPrint(int c); + +} +} +} + +#endif // LLVM_SUPPORT_LOCALE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/LockFileManager.h b/contrib/libs/llvm16/include/llvm/Support/LockFileManager.h new file mode 100644 index 00000000000..18c53b416fd --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/LockFileManager.h @@ -0,0 +1,111 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- LockFileManager.h - File-level locking utility ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_LOCKFILEMANAGER_H +#define LLVM_SUPPORT_LOCKFILEMANAGER_H + +#include "llvm/ADT/SmallString.h" +#include <optional> +#include <system_error> +#include <utility> // for std::pair + +namespace llvm { +class StringRef; + +/// Class that manages the creation of a lock file to aid +/// implicit coordination between different processes. +/// +/// The implicit coordination works by creating a ".lock" file alongside +/// the file that we're coordinating for, using the atomicity of the file +/// system to ensure that only a single process can create that ".lock" file. +/// When the lock file is removed, the owning process has finished the +/// operation. +class LockFileManager { +public: + /// Describes the state of a lock file. + enum LockFileState { + /// The lock file has been created and is owned by this instance + /// of the object. + LFS_Owned, + /// The lock file already exists and is owned by some other + /// instance. + LFS_Shared, + /// An error occurred while trying to create or find the lock + /// file. + LFS_Error + }; + + /// Describes the result of waiting for the owner to release the lock. + enum WaitForUnlockResult { + /// The lock was released successfully. + Res_Success, + /// Owner died while holding the lock. + Res_OwnerDied, + /// Reached timeout while waiting for the owner to release the lock. + Res_Timeout + }; + +private: + SmallString<128> FileName; + SmallString<128> LockFileName; + SmallString<128> UniqueLockFileName; + + std::optional<std::pair<std::string, int>> Owner; + std::error_code ErrorCode; + std::string ErrorDiagMsg; + + LockFileManager(const LockFileManager &) = delete; + LockFileManager &operator=(const LockFileManager &) = delete; + + static std::optional<std::pair<std::string, int>> + readLockFile(StringRef LockFileName); + + static bool processStillExecuting(StringRef Hostname, int PID); + +public: + + LockFileManager(StringRef FileName); + ~LockFileManager(); + + /// Determine the state of the lock file. + LockFileState getState() const; + + operator LockFileState() const { return getState(); } + + /// For a shared lock, wait until the owner releases the lock. + /// Total timeout for the file to appear is ~1.5 minutes. + /// \param MaxSeconds the maximum total wait time in seconds. + WaitForUnlockResult waitForUnlock(const unsigned MaxSeconds = 90); + + /// Remove the lock file. This may delete a different lock file than + /// the one previously read if there is a race. + std::error_code unsafeRemoveLockFile(); + + /// Get error message, or "" if there is no error. + std::string getErrorMessage() const; + + /// Set error and error message + void setError(const std::error_code &EC, StringRef ErrorMsg = "") { + ErrorCode = EC; + ErrorDiagMsg = ErrorMsg.str(); + } +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_LOCKFILEMANAGER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/LowLevelTypeImpl.h b/contrib/libs/llvm16/include/llvm/Support/LowLevelTypeImpl.h new file mode 100644 index 00000000000..0552a8a733c --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/LowLevelTypeImpl.h @@ -0,0 +1,442 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//== llvm/Support/LowLevelTypeImpl.h --------------------------- -*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// Implement a low-level type suitable for MachineInstr level instruction +/// selection. +/// +/// For a type attached to a MachineInstr, we only care about 2 details: total +/// size and the number of vector lanes (if any). Accordingly, there are 4 +/// possible valid type-kinds: +/// +/// * `sN` for scalars and aggregates +/// * `<N x sM>` for vectors, which must have at least 2 elements. +/// * `pN` for pointers +/// +/// Other information required for correct selection is expected to be carried +/// by the opcode, or non-type flags. For example the distinction between G_ADD +/// and G_FADD for int/float or fast-math flags. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_LOWLEVELTYPEIMPL_H +#define LLVM_SUPPORT_LOWLEVELTYPEIMPL_H + +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MachineValueType.h" +#include <cassert> + +namespace llvm { + +class Type; +class raw_ostream; + +class LLT { +public: + /// Get a low-level scalar or aggregate "bag of bits". + static constexpr LLT scalar(unsigned SizeInBits) { + return LLT{/*isPointer=*/false, /*isVector=*/false, /*isScalar=*/true, + ElementCount::getFixed(0), SizeInBits, + /*AddressSpace=*/0}; + } + + /// Get a low-level pointer in the given address space. + static constexpr LLT pointer(unsigned AddressSpace, unsigned SizeInBits) { + assert(SizeInBits > 0 && "invalid pointer size"); + return LLT{/*isPointer=*/true, /*isVector=*/false, /*isScalar=*/false, + ElementCount::getFixed(0), SizeInBits, AddressSpace}; + } + + /// Get a low-level vector of some number of elements and element width. + static constexpr LLT vector(ElementCount EC, unsigned ScalarSizeInBits) { + assert(!EC.isScalar() && "invalid number of vector elements"); + return LLT{/*isPointer=*/false, /*isVector=*/true, /*isScalar=*/false, + EC, ScalarSizeInBits, /*AddressSpace=*/0}; + } + + /// Get a low-level vector of some number of elements and element type. + static constexpr LLT vector(ElementCount EC, LLT ScalarTy) { + assert(!EC.isScalar() && "invalid number of vector elements"); + assert(!ScalarTy.isVector() && "invalid vector element type"); + return LLT{ScalarTy.isPointer(), + /*isVector=*/true, + /*isScalar=*/false, + EC, + ScalarTy.getSizeInBits().getFixedValue(), + ScalarTy.isPointer() ? ScalarTy.getAddressSpace() : 0}; + } + + /// Get a low-level fixed-width vector of some number of elements and element + /// width. + static constexpr LLT fixed_vector(unsigned NumElements, + unsigned ScalarSizeInBits) { + return vector(ElementCount::getFixed(NumElements), ScalarSizeInBits); + } + + /// Get a low-level fixed-width vector of some number of elements and element + /// type. + static constexpr LLT fixed_vector(unsigned NumElements, LLT ScalarTy) { + return vector(ElementCount::getFixed(NumElements), ScalarTy); + } + + /// Get a low-level scalable vector of some number of elements and element + /// width. + static constexpr LLT scalable_vector(unsigned MinNumElements, + unsigned ScalarSizeInBits) { + return vector(ElementCount::getScalable(MinNumElements), ScalarSizeInBits); + } + + /// Get a low-level scalable vector of some number of elements and element + /// type. + static constexpr LLT scalable_vector(unsigned MinNumElements, LLT ScalarTy) { + return vector(ElementCount::getScalable(MinNumElements), ScalarTy); + } + + static constexpr LLT scalarOrVector(ElementCount EC, LLT ScalarTy) { + return EC.isScalar() ? ScalarTy : LLT::vector(EC, ScalarTy); + } + + static constexpr LLT scalarOrVector(ElementCount EC, uint64_t ScalarSize) { + assert(ScalarSize <= std::numeric_limits<unsigned>::max() && + "Not enough bits in LLT to represent size"); + return scalarOrVector(EC, LLT::scalar(static_cast<unsigned>(ScalarSize))); + } + + explicit constexpr LLT(bool isPointer, bool isVector, bool isScalar, + ElementCount EC, uint64_t SizeInBits, + unsigned AddressSpace) + : LLT() { + init(isPointer, isVector, isScalar, EC, SizeInBits, AddressSpace); + } + explicit constexpr LLT() + : IsScalar(false), IsPointer(false), IsVector(false), RawData(0) {} + + explicit LLT(MVT VT); + + constexpr bool isValid() const { return IsScalar || RawData != 0; } + + constexpr bool isScalar() const { return IsScalar; } + + constexpr bool isPointer() const { + return isValid() && IsPointer && !IsVector; + } + + constexpr bool isVector() const { return isValid() && IsVector; } + + /// Returns the number of elements in a vector LLT. Must only be called on + /// vector types. + constexpr uint16_t getNumElements() const { + if (isScalable()) + llvm::reportInvalidSizeRequest( + "Possible incorrect use of LLT::getNumElements() for " + "scalable vector. Scalable flag may be dropped, use " + "LLT::getElementCount() instead"); + return getElementCount().getKnownMinValue(); + } + + /// Returns true if the LLT is a scalable vector. Must only be called on + /// vector types. + constexpr bool isScalable() const { + assert(isVector() && "Expected a vector type"); + return IsPointer ? getFieldValue(PointerVectorScalableFieldInfo) + : getFieldValue(VectorScalableFieldInfo); + } + + constexpr ElementCount getElementCount() const { + assert(IsVector && "cannot get number of elements on scalar/aggregate"); + return ElementCount::get(IsPointer + ? getFieldValue(PointerVectorElementsFieldInfo) + : getFieldValue(VectorElementsFieldInfo), + isScalable()); + } + + /// Returns the total size of the type. Must only be called on sized types. + constexpr TypeSize getSizeInBits() const { + if (isPointer() || isScalar()) + return TypeSize::Fixed(getScalarSizeInBits()); + auto EC = getElementCount(); + return TypeSize(getScalarSizeInBits() * EC.getKnownMinValue(), + EC.isScalable()); + } + + /// Returns the total size of the type in bytes, i.e. number of whole bytes + /// needed to represent the size in bits. Must only be called on sized types. + constexpr TypeSize getSizeInBytes() const { + TypeSize BaseSize = getSizeInBits(); + return {(BaseSize.getKnownMinValue() + 7) / 8, BaseSize.isScalable()}; + } + + constexpr LLT getScalarType() const { + return isVector() ? getElementType() : *this; + } + + /// If this type is a vector, return a vector with the same number of elements + /// but the new element type. Otherwise, return the new element type. + constexpr LLT changeElementType(LLT NewEltTy) const { + return isVector() ? LLT::vector(getElementCount(), NewEltTy) : NewEltTy; + } + + /// If this type is a vector, return a vector with the same number of elements + /// but the new element size. Otherwise, return the new element type. Invalid + /// for pointer types. For pointer types, use changeElementType. + constexpr LLT changeElementSize(unsigned NewEltSize) const { + assert(!getScalarType().isPointer() && + "invalid to directly change element size for pointers"); + return isVector() ? LLT::vector(getElementCount(), NewEltSize) + : LLT::scalar(NewEltSize); + } + + /// Return a vector or scalar with the same element type and the new element + /// count. + constexpr LLT changeElementCount(ElementCount EC) const { + return LLT::scalarOrVector(EC, getScalarType()); + } + + /// Return a type that is \p Factor times smaller. Reduces the number of + /// elements if this is a vector, or the bitwidth for scalar/pointers. Does + /// not attempt to handle cases that aren't evenly divisible. + constexpr LLT divide(int Factor) const { + assert(Factor != 1); + assert((!isScalar() || getScalarSizeInBits() != 0) && + "cannot divide scalar of size zero"); + if (isVector()) { + assert(getElementCount().isKnownMultipleOf(Factor)); + return scalarOrVector(getElementCount().divideCoefficientBy(Factor), + getElementType()); + } + + assert(getScalarSizeInBits() % Factor == 0); + return scalar(getScalarSizeInBits() / Factor); + } + + /// Produce a vector type that is \p Factor times bigger, preserving the + /// element type. For a scalar or pointer, this will produce a new vector with + /// \p Factor elements. + constexpr LLT multiplyElements(int Factor) const { + if (isVector()) { + return scalarOrVector(getElementCount().multiplyCoefficientBy(Factor), + getElementType()); + } + + return fixed_vector(Factor, *this); + } + + constexpr bool isByteSized() const { + return getSizeInBits().isKnownMultipleOf(8); + } + + constexpr unsigned getScalarSizeInBits() const { + if (IsScalar) + return getFieldValue(ScalarSizeFieldInfo); + if (IsVector) { + if (!IsPointer) + return getFieldValue(VectorSizeFieldInfo); + else + return getFieldValue(PointerVectorSizeFieldInfo); + } else if (IsPointer) + return getFieldValue(PointerSizeFieldInfo); + else + llvm_unreachable("unexpected LLT"); + } + + constexpr unsigned getAddressSpace() const { + assert(RawData != 0 && "Invalid Type"); + assert(IsPointer && "cannot get address space of non-pointer type"); + if (!IsVector) + return getFieldValue(PointerAddressSpaceFieldInfo); + else + return getFieldValue(PointerVectorAddressSpaceFieldInfo); + } + + /// Returns the vector's element type. Only valid for vector types. + constexpr LLT getElementType() const { + assert(isVector() && "cannot get element type of scalar/aggregate"); + if (IsPointer) + return pointer(getAddressSpace(), getScalarSizeInBits()); + else + return scalar(getScalarSizeInBits()); + } + + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { + print(dbgs()); + dbgs() << '\n'; + } +#endif + + constexpr bool operator==(const LLT &RHS) const { + return IsPointer == RHS.IsPointer && IsVector == RHS.IsVector && + IsScalar == RHS.IsScalar && RHS.RawData == RawData; + } + + constexpr bool operator!=(const LLT &RHS) const { return !(*this == RHS); } + + friend struct DenseMapInfo<LLT>; + friend class GISelInstProfileBuilder; + +private: + /// LLT is packed into 64 bits as follows: + /// isScalar : 1 + /// isPointer : 1 + /// isVector : 1 + /// with 61 bits remaining for Kind-specific data, packed in bitfields + /// as described below. As there isn't a simple portable way to pack bits + /// into bitfields, here the different fields in the packed structure is + /// described in static const *Field variables. Each of these variables + /// is a 2-element array, with the first element describing the bitfield size + /// and the second element describing the bitfield offset. + typedef int BitFieldInfo[2]; + /// + /// This is how the bitfields are packed per Kind: + /// * Invalid: + /// gets encoded as RawData == 0, as that is an invalid encoding, since for + /// valid encodings, SizeInBits/SizeOfElement must be larger than 0. + /// * Non-pointer scalar (isPointer == 0 && isVector == 0): + /// SizeInBits: 32; + static const constexpr BitFieldInfo ScalarSizeFieldInfo{32, 0}; + /// * Pointer (isPointer == 1 && isVector == 0): + /// SizeInBits: 16; + /// AddressSpace: 24; + static const constexpr BitFieldInfo PointerSizeFieldInfo{16, 0}; + static const constexpr BitFieldInfo PointerAddressSpaceFieldInfo{ + 24, PointerSizeFieldInfo[0] + PointerSizeFieldInfo[1]}; + static_assert((PointerAddressSpaceFieldInfo[0] + + PointerAddressSpaceFieldInfo[1]) <= 61, + "Insufficient bits to encode all data"); + /// * Vector-of-non-pointer (isPointer == 0 && isVector == 1): + /// NumElements: 16; + /// SizeOfElement: 32; + /// Scalable: 1; + static const constexpr BitFieldInfo VectorElementsFieldInfo{16, 0}; + static const constexpr BitFieldInfo VectorSizeFieldInfo{ + 32, VectorElementsFieldInfo[0] + VectorElementsFieldInfo[1]}; + static const constexpr BitFieldInfo VectorScalableFieldInfo{ + 1, VectorSizeFieldInfo[0] + VectorSizeFieldInfo[1]}; + static_assert((VectorSizeFieldInfo[0] + VectorSizeFieldInfo[1]) <= 61, + "Insufficient bits to encode all data"); + /// * Vector-of-pointer (isPointer == 1 && isVector == 1): + /// NumElements: 16; + /// SizeOfElement: 16; + /// AddressSpace: 24; + /// Scalable: 1; + static const constexpr BitFieldInfo PointerVectorElementsFieldInfo{16, 0}; + static const constexpr BitFieldInfo PointerVectorSizeFieldInfo{ + 16, + PointerVectorElementsFieldInfo[1] + PointerVectorElementsFieldInfo[0]}; + static const constexpr BitFieldInfo PointerVectorAddressSpaceFieldInfo{ + 24, PointerVectorSizeFieldInfo[1] + PointerVectorSizeFieldInfo[0]}; + static const constexpr BitFieldInfo PointerVectorScalableFieldInfo{ + 1, PointerVectorAddressSpaceFieldInfo[0] + + PointerVectorAddressSpaceFieldInfo[1]}; + static_assert((PointerVectorAddressSpaceFieldInfo[0] + + PointerVectorAddressSpaceFieldInfo[1]) <= 61, + "Insufficient bits to encode all data"); + + uint64_t IsScalar : 1; + uint64_t IsPointer : 1; + uint64_t IsVector : 1; + uint64_t RawData : 61; + + static constexpr uint64_t getMask(const BitFieldInfo FieldInfo) { + const int FieldSizeInBits = FieldInfo[0]; + return (((uint64_t)1) << FieldSizeInBits) - 1; + } + static constexpr uint64_t maskAndShift(uint64_t Val, uint64_t Mask, + uint8_t Shift) { + assert(Val <= Mask && "Value too large for field"); + return (Val & Mask) << Shift; + } + static constexpr uint64_t maskAndShift(uint64_t Val, + const BitFieldInfo FieldInfo) { + return maskAndShift(Val, getMask(FieldInfo), FieldInfo[1]); + } + + constexpr uint64_t getFieldValue(const BitFieldInfo FieldInfo) const { + return getMask(FieldInfo) & (RawData >> FieldInfo[1]); + } + + constexpr void init(bool IsPointer, bool IsVector, bool IsScalar, + ElementCount EC, uint64_t SizeInBits, + unsigned AddressSpace) { + assert(SizeInBits <= std::numeric_limits<unsigned>::max() && + "Not enough bits in LLT to represent size"); + this->IsPointer = IsPointer; + this->IsVector = IsVector; + this->IsScalar = IsScalar; + if (IsScalar) + RawData = maskAndShift(SizeInBits, ScalarSizeFieldInfo); + else if (IsVector) { + assert(EC.isVector() && "invalid number of vector elements"); + if (!IsPointer) + RawData = + maskAndShift(EC.getKnownMinValue(), VectorElementsFieldInfo) | + maskAndShift(SizeInBits, VectorSizeFieldInfo) | + maskAndShift(EC.isScalable() ? 1 : 0, VectorScalableFieldInfo); + else + RawData = + maskAndShift(EC.getKnownMinValue(), + PointerVectorElementsFieldInfo) | + maskAndShift(SizeInBits, PointerVectorSizeFieldInfo) | + maskAndShift(AddressSpace, PointerVectorAddressSpaceFieldInfo) | + maskAndShift(EC.isScalable() ? 1 : 0, + PointerVectorScalableFieldInfo); + } else if (IsPointer) + RawData = maskAndShift(SizeInBits, PointerSizeFieldInfo) | + maskAndShift(AddressSpace, PointerAddressSpaceFieldInfo); + else + llvm_unreachable("unexpected LLT configuration"); + } + +public: + constexpr uint64_t getUniqueRAWLLTData() const { + return ((uint64_t)RawData) << 3 | ((uint64_t)IsScalar) << 2 | + ((uint64_t)IsPointer) << 1 | ((uint64_t)IsVector); + } +}; + +inline raw_ostream& operator<<(raw_ostream &OS, const LLT &Ty) { + Ty.print(OS); + return OS; +} + +template<> struct DenseMapInfo<LLT> { + static inline LLT getEmptyKey() { + LLT Invalid; + Invalid.IsPointer = true; + return Invalid; + } + static inline LLT getTombstoneKey() { + LLT Invalid; + Invalid.IsVector = true; + return Invalid; + } + static inline unsigned getHashValue(const LLT &Ty) { + uint64_t Val = Ty.getUniqueRAWLLTData(); + return DenseMapInfo<uint64_t>::getHashValue(Val); + } + static bool isEqual(const LLT &LHS, const LLT &RHS) { + return LHS == RHS; + } +}; + +} + +#endif // LLVM_SUPPORT_LOWLEVELTYPEIMPL_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MD5.h b/contrib/libs/llvm16/include/llvm/Support/MD5.h new file mode 100644 index 00000000000..e96c6f0c092 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MD5.h @@ -0,0 +1,133 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +/* -*- C++ -*- + * This code is derived from (original license follows): + * + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifndef LLVM_SUPPORT_MD5_H +#define LLVM_SUPPORT_MD5_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include <array> +#include <cstdint> + +namespace llvm { + +template <unsigned N> class SmallString; +template <typename T> class ArrayRef; + +class MD5 { +public: + struct MD5Result : public std::array<uint8_t, 16> { + SmallString<32> digest() const; + + uint64_t low() const { + // Our MD5 implementation returns the result in little endian, so the low + // word is first. + using namespace support; + return endian::read<uint64_t, little, unaligned>(data()); + } + + uint64_t high() const { + using namespace support; + return endian::read<uint64_t, little, unaligned>(data() + 8); + } + std::pair<uint64_t, uint64_t> words() const { + using namespace support; + return std::make_pair(high(), low()); + } + }; + + MD5(); + + /// Updates the hash for the byte stream provided. + void update(ArrayRef<uint8_t> Data); + + /// Updates the hash for the StringRef provided. + void update(StringRef Str); + + /// Finishes off the hash and puts the result in result. + void final(MD5Result &Result); + + /// Finishes off the hash, and returns the 16-byte hash data. + MD5Result final(); + + /// Finishes off the hash, and returns the 16-byte hash data. + /// This is suitable for getting the MD5 at any time without invalidating the + /// internal state, so that more calls can be made into `update`. + MD5Result result(); + + /// Translates the bytes in \p Res to a hex string that is + /// deposited into \p Str. The result will be of length 32. + static void stringifyResult(MD5Result &Result, SmallVectorImpl<char> &Str); + + /// Computes the hash for a given bytes. + static MD5Result hash(ArrayRef<uint8_t> Data); + +private: + // Any 32-bit or wider unsigned integer data type will do. + typedef uint32_t MD5_u32plus; + + // Internal State + struct { + MD5_u32plus a = 0x67452301; + MD5_u32plus b = 0xefcdab89; + MD5_u32plus c = 0x98badcfe; + MD5_u32plus d = 0x10325476; + MD5_u32plus hi = 0; + MD5_u32plus lo = 0; + uint8_t buffer[64]; + MD5_u32plus block[16]; + } InternalState; + + const uint8_t *body(ArrayRef<uint8_t> Data); +}; + +/// Helper to compute and return lower 64 bits of the given string's MD5 hash. +inline uint64_t MD5Hash(StringRef Str) { + using namespace support; + + MD5 Hash; + Hash.update(Str); + MD5::MD5Result Result; + Hash.final(Result); + // Return the least significant word. + return Result.low(); +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_MD5_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MSP430AttributeParser.h b/contrib/libs/llvm16/include/llvm/Support/MSP430AttributeParser.h new file mode 100644 index 00000000000..33ab30d5c71 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MSP430AttributeParser.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- MSP430AttributeParser.h - MSP430 Attribute Parser -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains support routines for parsing MSP430 ELF build attributes. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MSP430ATTRIBUTEPARSER_H +#define LLVM_SUPPORT_MSP430ATTRIBUTEPARSER_H + +#include "llvm/Support/ELFAttributeParser.h" +#include "llvm/Support/MSP430Attributes.h" + +namespace llvm { +class MSP430AttributeParser : public ELFAttributeParser { + struct DisplayHandler { + MSP430Attrs::AttrType Attribute; + Error (MSP430AttributeParser::*Routine)(MSP430Attrs::AttrType); + }; + static const std::array<DisplayHandler, 4> DisplayRoutines; + + Error parseISA(MSP430Attrs::AttrType Tag); + Error parseCodeModel(MSP430Attrs::AttrType Tag); + Error parseDataModel(MSP430Attrs::AttrType Tag); + Error parseEnumSize(MSP430Attrs::AttrType Tag); + + Error handler(uint64_t Tag, bool &Handled) override; + +public: + MSP430AttributeParser(ScopedPrinter *SW) + : ELFAttributeParser(SW, MSP430Attrs::getMSP430AttributeTags(), + "mspabi") {} + MSP430AttributeParser() + : ELFAttributeParser(MSP430Attrs::getMSP430AttributeTags(), "mspabi") {} +}; +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MSP430Attributes.h b/contrib/libs/llvm16/include/llvm/Support/MSP430Attributes.h new file mode 100644 index 00000000000..99e5c953db9 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MSP430Attributes.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- MSP430Attributes.h - MSP430 Attributes ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===-----------------------------------------------------------------------===// +/// +/// \file +/// This file contains enumerations for MSP430 ELF build attributes as +/// defined in the MSP430 ELF psABI specification. +/// +/// MSP430 ELF psABI specification +/// +/// https://www.ti.com/lit/pdf/slaa534 +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_MSP430ATTRIBUTES_H +#define LLVM_SUPPORT_MSP430ATTRIBUTES_H + +#include "llvm/Support/ELFAttributes.h" + +namespace llvm { +namespace MSP430Attrs { + +const TagNameMap &getMSP430AttributeTags(); + +enum AttrType : unsigned { + // Attribute types in ELF/.MSP430.attributes. + TagISA = 4, + TagCodeModel = 6, + TagDataModel = 8, + TagEnumSize = 10 +}; + +enum ISA { ISAMSP430 = 1, ISAMSP430X = 2 }; +enum CodeModel { CMSmall = 1, CMLarge = 2 }; +enum DataModel { DMSmall = 1, DMLarge = 2, DMRestricted = 3 }; +enum EnumSize { ESSmall = 1, ESInteger = 2, ESDontCare = 3 }; + +} // namespace MSP430Attrs +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MachineValueType.h b/contrib/libs/llvm16/include/llvm/Support/MachineValueType.h new file mode 100644 index 00000000000..1887bb0de31 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MachineValueType.h @@ -0,0 +1,1587 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- Support/MachineValueType.h - Machine-Level types ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the set of machine-level target independent types which +// legal values in the code generator use. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MACHINEVALUETYPE_H +#define LLVM_SUPPORT_MACHINEVALUETYPE_H + +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/TypeSize.h" +#include <cassert> + +namespace llvm { + + class Type; + + /// Machine Value Type. Every type that is supported natively by some + /// processor targeted by LLVM occurs here. This means that any legal value + /// type can be represented by an MVT. + class MVT { + public: + enum SimpleValueType : uint8_t { + // clang-format off + + // Simple value types that aren't explicitly part of this enumeration + // are considered extended value types. + INVALID_SIMPLE_VALUE_TYPE = 0, + + // If you change this numbering, you must change the values in + // ValueTypes.td as well! + Other = 1, // This is a non-standard value + i1 = 2, // This is a 1 bit integer value + i2 = 3, // This is a 2 bit integer value + i4 = 4, // This is a 4 bit integer value + i8 = 5, // This is an 8 bit integer value + i16 = 6, // This is a 16 bit integer value + i32 = 7, // This is a 32 bit integer value + i64 = 8, // This is a 64 bit integer value + i128 = 9, // This is a 128 bit integer value + + FIRST_INTEGER_VALUETYPE = i1, + LAST_INTEGER_VALUETYPE = i128, + + bf16 = 10, // This is a 16 bit brain floating point value + f16 = 11, // This is a 16 bit floating point value + f32 = 12, // This is a 32 bit floating point value + f64 = 13, // This is a 64 bit floating point value + f80 = 14, // This is a 80 bit floating point value + f128 = 15, // This is a 128 bit floating point value + ppcf128 = 16, // This is a PPC 128-bit floating point value + + FIRST_FP_VALUETYPE = bf16, + LAST_FP_VALUETYPE = ppcf128, + + v1i1 = 17, // 1 x i1 + v2i1 = 18, // 2 x i1 + v4i1 = 19, // 4 x i1 + v8i1 = 20, // 8 x i1 + v16i1 = 21, // 16 x i1 + v32i1 = 22, // 32 x i1 + v64i1 = 23, // 64 x i1 + v128i1 = 24, // 128 x i1 + v256i1 = 25, // 256 x i1 + v512i1 = 26, // 512 x i1 + v1024i1 = 27, // 1024 x i1 + v2048i1 = 28, // 2048 x i1 + + v128i2 = 29, // 128 x i2 + v256i2 = 30, // 256 x i2 + + v64i4 = 31, // 64 x i4 + v128i4 = 32, // 128 x i4 + + v1i8 = 33, // 1 x i8 + v2i8 = 34, // 2 x i8 + v4i8 = 35, // 4 x i8 + v8i8 = 36, // 8 x i8 + v16i8 = 37, // 16 x i8 + v32i8 = 38, // 32 x i8 + v64i8 = 39, // 64 x i8 + v128i8 = 40, // 128 x i8 + v256i8 = 41, // 256 x i8 + v512i8 = 42, // 512 x i8 + v1024i8 = 43, // 1024 x i8 + + v1i16 = 44, // 1 x i16 + v2i16 = 45, // 2 x i16 + v3i16 = 46, // 3 x i16 + v4i16 = 47, // 4 x i16 + v8i16 = 48, // 8 x i16 + v16i16 = 49, // 16 x i16 + v32i16 = 50, // 32 x i16 + v64i16 = 51, // 64 x i16 + v128i16 = 52, // 128 x i16 + v256i16 = 53, // 256 x i16 + v512i16 = 54, // 512 x i16 + + v1i32 = 55, // 1 x i32 + v2i32 = 56, // 2 x i32 + v3i32 = 57, // 3 x i32 + v4i32 = 58, // 4 x i32 + v5i32 = 59, // 5 x i32 + v6i32 = 60, // 6 x i32 + v7i32 = 61, // 7 x i32 + v8i32 = 62, // 8 x i32 + v9i32 = 63, // 9 x i32 + v10i32 = 64, // 10 x i32 + v11i32 = 65, // 11 x i32 + v12i32 = 66, // 12 x i32 + v16i32 = 67, // 16 x i32 + v32i32 = 68, // 32 x i32 + v64i32 = 69, // 64 x i32 + v128i32 = 70, // 128 x i32 + v256i32 = 71, // 256 x i32 + v512i32 = 72, // 512 x i32 + v1024i32 = 73, // 1024 x i32 + v2048i32 = 74, // 2048 x i32 + + v1i64 = 75, // 1 x i64 + v2i64 = 76, // 2 x i64 + v3i64 = 77, // 3 x i64 + v4i64 = 78, // 4 x i64 + v8i64 = 79, // 8 x i64 + v16i64 = 80, // 16 x i64 + v32i64 = 81, // 32 x i64 + v64i64 = 82, // 64 x i64 + v128i64 = 83, // 128 x i64 + v256i64 = 84, // 256 x i64 + + v1i128 = 85, // 1 x i128 + + FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE = v1i1, + LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE = v1i128, + + v1f16 = 86, // 1 x f16 + v2f16 = 87, // 2 x f16 + v3f16 = 88, // 3 x f16 + v4f16 = 89, // 4 x f16 + v8f16 = 90, // 8 x f16 + v16f16 = 91, // 16 x f16 + v32f16 = 92, // 32 x f16 + v64f16 = 93, // 64 x f16 + v128f16 = 94, // 128 x f16 + v256f16 = 95, // 256 x f16 + v512f16 = 96, // 512 x f16 + + v2bf16 = 97, // 2 x bf16 + v3bf16 = 98, // 3 x bf16 + v4bf16 = 99, // 4 x bf16 + v8bf16 = 100, // 8 x bf16 + v16bf16 = 101, // 16 x bf16 + v32bf16 = 102, // 32 x bf16 + v64bf16 = 103, // 64 x bf16 + v128bf16 = 104, // 128 x bf16 + + v1f32 = 105, // 1 x f32 + v2f32 = 106, // 2 x f32 + v3f32 = 107, // 3 x f32 + v4f32 = 108, // 4 x f32 + v5f32 = 109, // 5 x f32 + v6f32 = 110, // 6 x f32 + v7f32 = 111, // 7 x f32 + v8f32 = 112, // 8 x f32 + v9f32 = 113, // 9 x f32 + v10f32 = 114, // 10 x f32 + v11f32 = 115, // 11 x f32 + v12f32 = 116, // 12 x f32 + v16f32 = 117, // 16 x f32 + + v32f32 = 118, // 32 x f32 + v64f32 = 119, // 64 x f32 + v128f32 = 120, // 128 x f32 + v256f32 = 121, // 256 x f32 + v512f32 = 122, // 512 x f32 + v1024f32 = 123, // 1024 x f32 + v2048f32 = 124, // 2048 x f32 + + v1f64 = 125, // 1 x f64 + v2f64 = 126, // 2 x f64 + v3f64 = 127, // 3 x f64 + v4f64 = 128, // 4 x f64 + v8f64 = 129, // 8 x f64 + v16f64 = 130, // 16 x f64 + v32f64 = 131, // 32 x f64 + v64f64 = 132, // 64 x f64 + v128f64 = 133, // 128 x f64 + v256f64 = 134, // 256 x f64 + + FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE = v1f16, + LAST_FP_FIXEDLEN_VECTOR_VALUETYPE = v256f64, + + FIRST_FIXEDLEN_VECTOR_VALUETYPE = v1i1, + LAST_FIXEDLEN_VECTOR_VALUETYPE = v256f64, + + nxv1i1 = 135, // n x 1 x i1 + nxv2i1 = 136, // n x 2 x i1 + nxv4i1 = 137, // n x 4 x i1 + nxv8i1 = 138, // n x 8 x i1 + nxv16i1 = 139, // n x 16 x i1 + nxv32i1 = 140, // n x 32 x i1 + nxv64i1 = 141, // n x 64 x i1 + + nxv1i8 = 142, // n x 1 x i8 + nxv2i8 = 143, // n x 2 x i8 + nxv4i8 = 144, // n x 4 x i8 + nxv8i8 = 145, // n x 8 x i8 + nxv16i8 = 146, // n x 16 x i8 + nxv32i8 = 147, // n x 32 x i8 + nxv64i8 = 148, // n x 64 x i8 + + nxv1i16 = 149, // n x 1 x i16 + nxv2i16 = 150, // n x 2 x i16 + nxv4i16 = 151, // n x 4 x i16 + nxv8i16 = 152, // n x 8 x i16 + nxv16i16 = 153, // n x 16 x i16 + nxv32i16 = 154, // n x 32 x i16 + + nxv1i32 = 155, // n x 1 x i32 + nxv2i32 = 156, // n x 2 x i32 + nxv4i32 = 157, // n x 4 x i32 + nxv8i32 = 158, // n x 8 x i32 + nxv16i32 = 159, // n x 16 x i32 + nxv32i32 = 160, // n x 32 x i32 + + nxv1i64 = 161, // n x 1 x i64 + nxv2i64 = 162, // n x 2 x i64 + nxv4i64 = 163, // n x 4 x i64 + nxv8i64 = 164, // n x 8 x i64 + nxv16i64 = 165, // n x 16 x i64 + nxv32i64 = 166, // n x 32 x i64 + + FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE = nxv1i1, + LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE = nxv32i64, + + nxv1f16 = 167, // n x 1 x f16 + nxv2f16 = 168, // n x 2 x f16 + nxv4f16 = 169, // n x 4 x f16 + nxv8f16 = 170, // n x 8 x f16 + nxv16f16 = 171, // n x 16 x f16 + nxv32f16 = 172, // n x 32 x f16 + + nxv1bf16 = 173, // n x 1 x bf16 + nxv2bf16 = 174, // n x 2 x bf16 + nxv4bf16 = 175, // n x 4 x bf16 + nxv8bf16 = 176, // n x 8 x bf16 + nxv16bf16 = 177, // n x 16 x bf16 + nxv32bf16 = 178, // n x 32 x bf16 + + nxv1f32 = 179, // n x 1 x f32 + nxv2f32 = 180, // n x 2 x f32 + nxv4f32 = 181, // n x 4 x f32 + nxv8f32 = 182, // n x 8 x f32 + nxv16f32 = 183, // n x 16 x f32 + + nxv1f64 = 184, // n x 1 x f64 + nxv2f64 = 185, // n x 2 x f64 + nxv4f64 = 186, // n x 4 x f64 + nxv8f64 = 187, // n x 8 x f64 + + FIRST_FP_SCALABLE_VECTOR_VALUETYPE = nxv1f16, + LAST_FP_SCALABLE_VECTOR_VALUETYPE = nxv8f64, + + FIRST_SCALABLE_VECTOR_VALUETYPE = nxv1i1, + LAST_SCALABLE_VECTOR_VALUETYPE = nxv8f64, + + FIRST_VECTOR_VALUETYPE = v1i1, + LAST_VECTOR_VALUETYPE = nxv8f64, + + x86mmx = 188, // This is an X86 MMX value + + Glue = 189, // This glues nodes together during pre-RA sched + + isVoid = 190, // This has no value + + Untyped = 191, // This value takes a register, but has + // unspecified type. The register class + // will be determined by the opcode. + + funcref = 192, // WebAssembly's funcref type + externref = 193, // WebAssembly's externref type + x86amx = 194, // This is an X86 AMX value + i64x8 = 195, // 8 Consecutive GPRs (AArch64) + + FIRST_VALUETYPE = 1, // This is always the beginning of the list. + LAST_VALUETYPE = i64x8, // This always remains at the end of the list. + VALUETYPE_SIZE = LAST_VALUETYPE + 1, + + // This is the current maximum for LAST_VALUETYPE. + // MVT::MAX_ALLOWED_VALUETYPE is used for asserts and to size bit vectors + // This value must be a multiple of 32. + MAX_ALLOWED_VALUETYPE = 224, + + // A value of type llvm::TokenTy + token = 248, + + // This is MDNode or MDString. + Metadata = 249, + + // An int value the size of the pointer of the current + // target to any address space. This must only be used internal to + // tblgen. Other than for overloading, we treat iPTRAny the same as iPTR. + iPTRAny = 250, + + // A vector with any length and element size. This is used + // for intrinsics that have overloadings based on vector types. + // This is only for tblgen's consumption! + vAny = 251, + + // Any floating-point or vector floating-point value. This is used + // for intrinsics that have overloadings based on floating-point types. + // This is only for tblgen's consumption! + fAny = 252, + + // An integer or vector integer value of any bit width. This is + // used for intrinsics that have overloadings based on integer bit widths. + // This is only for tblgen's consumption! + iAny = 253, + + // An int value the size of the pointer of the current + // target. This should only be used internal to tblgen! + iPTR = 254, + + // Any type. This is used for intrinsics that have overloadings. + // This is only for tblgen's consumption! + Any = 255 + + // clang-format on + }; + + SimpleValueType SimpleTy = INVALID_SIMPLE_VALUE_TYPE; + + constexpr MVT() = default; + constexpr MVT(SimpleValueType SVT) : SimpleTy(SVT) {} + + bool operator>(const MVT& S) const { return SimpleTy > S.SimpleTy; } + bool operator<(const MVT& S) const { return SimpleTy < S.SimpleTy; } + bool operator==(const MVT& S) const { return SimpleTy == S.SimpleTy; } + bool operator!=(const MVT& S) const { return SimpleTy != S.SimpleTy; } + bool operator>=(const MVT& S) const { return SimpleTy >= S.SimpleTy; } + bool operator<=(const MVT& S) const { return SimpleTy <= S.SimpleTy; } + + /// Return true if this is a valid simple valuetype. + bool isValid() const { + return (SimpleTy >= MVT::FIRST_VALUETYPE && + SimpleTy <= MVT::LAST_VALUETYPE); + } + + /// Return true if this is a FP or a vector FP type. + bool isFloatingPoint() const { + return ((SimpleTy >= MVT::FIRST_FP_VALUETYPE && + SimpleTy <= MVT::LAST_FP_VALUETYPE) || + (SimpleTy >= MVT::FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_FP_FIXEDLEN_VECTOR_VALUETYPE) || + (SimpleTy >= MVT::FIRST_FP_SCALABLE_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_FP_SCALABLE_VECTOR_VALUETYPE)); + } + + /// Return true if this is an integer or a vector integer type. + bool isInteger() const { + return ((SimpleTy >= MVT::FIRST_INTEGER_VALUETYPE && + SimpleTy <= MVT::LAST_INTEGER_VALUETYPE) || + (SimpleTy >= MVT::FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE) || + (SimpleTy >= MVT::FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE)); + } + + /// Return true if this is an integer, not including vectors. + bool isScalarInteger() const { + return (SimpleTy >= MVT::FIRST_INTEGER_VALUETYPE && + SimpleTy <= MVT::LAST_INTEGER_VALUETYPE); + } + + /// Return true if this is a vector value type. + bool isVector() const { + return (SimpleTy >= MVT::FIRST_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_VECTOR_VALUETYPE); + } + + /// Return true if this is a vector value type where the + /// runtime length is machine dependent + bool isScalableVector() const { + return (SimpleTy >= MVT::FIRST_SCALABLE_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_SCALABLE_VECTOR_VALUETYPE); + } + + bool isFixedLengthVector() const { + return (SimpleTy >= MVT::FIRST_FIXEDLEN_VECTOR_VALUETYPE && + SimpleTy <= MVT::LAST_FIXEDLEN_VECTOR_VALUETYPE); + } + + /// Return true if this is a 16-bit vector type. + bool is16BitVector() const { + return (SimpleTy == MVT::v2i8 || SimpleTy == MVT::v1i16 || + SimpleTy == MVT::v16i1 || SimpleTy == MVT::v1f16); + } + + /// Return true if this is a 32-bit vector type. + bool is32BitVector() const { + return (SimpleTy == MVT::v32i1 || SimpleTy == MVT::v4i8 || + SimpleTy == MVT::v2i16 || SimpleTy == MVT::v1i32 || + SimpleTy == MVT::v2f16 || SimpleTy == MVT::v2bf16 || + SimpleTy == MVT::v1f32); + } + + /// Return true if this is a 64-bit vector type. + bool is64BitVector() const { + return (SimpleTy == MVT::v64i1 || SimpleTy == MVT::v8i8 || + SimpleTy == MVT::v4i16 || SimpleTy == MVT::v2i32 || + SimpleTy == MVT::v1i64 || SimpleTy == MVT::v4f16 || + SimpleTy == MVT::v4bf16 ||SimpleTy == MVT::v2f32 || + SimpleTy == MVT::v1f64); + } + + /// Return true if this is a 128-bit vector type. + bool is128BitVector() const { + return (SimpleTy == MVT::v128i1 || SimpleTy == MVT::v16i8 || + SimpleTy == MVT::v8i16 || SimpleTy == MVT::v4i32 || + SimpleTy == MVT::v2i64 || SimpleTy == MVT::v1i128 || + SimpleTy == MVT::v8f16 || SimpleTy == MVT::v8bf16 || + SimpleTy == MVT::v4f32 || SimpleTy == MVT::v2f64); + } + + /// Return true if this is a 256-bit vector type. + bool is256BitVector() const { + return (SimpleTy == MVT::v16f16 || SimpleTy == MVT::v16bf16 || + SimpleTy == MVT::v8f32 || SimpleTy == MVT::v4f64 || + SimpleTy == MVT::v32i8 || SimpleTy == MVT::v16i16 || + SimpleTy == MVT::v8i32 || SimpleTy == MVT::v4i64 || + SimpleTy == MVT::v256i1 || SimpleTy == MVT::v128i2 || + SimpleTy == MVT::v64i4); + } + + /// Return true if this is a 512-bit vector type. + bool is512BitVector() const { + return (SimpleTy == MVT::v32f16 || SimpleTy == MVT::v32bf16 || + SimpleTy == MVT::v16f32 || SimpleTy == MVT::v8f64 || + SimpleTy == MVT::v512i1 || SimpleTy == MVT::v256i2 || + SimpleTy == MVT::v128i4 || SimpleTy == MVT::v64i8 || + SimpleTy == MVT::v32i16 || SimpleTy == MVT::v16i32 || + SimpleTy == MVT::v8i64); + } + + /// Return true if this is a 1024-bit vector type. + bool is1024BitVector() const { + return (SimpleTy == MVT::v1024i1 || SimpleTy == MVT::v128i8 || + SimpleTy == MVT::v64i16 || SimpleTy == MVT::v32i32 || + SimpleTy == MVT::v16i64 || SimpleTy == MVT::v64f16 || + SimpleTy == MVT::v32f32 || SimpleTy == MVT::v16f64 || + SimpleTy == MVT::v64bf16); + } + + /// Return true if this is a 2048-bit vector type. + bool is2048BitVector() const { + return (SimpleTy == MVT::v256i8 || SimpleTy == MVT::v128i16 || + SimpleTy == MVT::v64i32 || SimpleTy == MVT::v32i64 || + SimpleTy == MVT::v128f16 || SimpleTy == MVT::v64f32 || + SimpleTy == MVT::v32f64 || SimpleTy == MVT::v128bf16 || + SimpleTy == MVT::v2048i1); + } + + /// Return true if this is an overloaded type for TableGen. + bool isOverloaded() const { + return (SimpleTy == MVT::Any || SimpleTy == MVT::iAny || + SimpleTy == MVT::fAny || SimpleTy == MVT::vAny || + SimpleTy == MVT::iPTRAny); + } + + /// Return a vector with the same number of elements as this vector, but + /// with the element type converted to an integer type with the same + /// bitwidth. + MVT changeVectorElementTypeToInteger() const { + MVT EltTy = getVectorElementType(); + MVT IntTy = MVT::getIntegerVT(EltTy.getSizeInBits()); + MVT VecTy = MVT::getVectorVT(IntTy, getVectorElementCount()); + assert(VecTy.SimpleTy != MVT::INVALID_SIMPLE_VALUE_TYPE && + "Simple vector VT not representable by simple integer vector VT!"); + return VecTy; + } + + /// Return a VT for a vector type whose attributes match ourselves + /// with the exception of the element type that is chosen by the caller. + MVT changeVectorElementType(MVT EltVT) const { + MVT VecTy = MVT::getVectorVT(EltVT, getVectorElementCount()); + assert(VecTy.SimpleTy != MVT::INVALID_SIMPLE_VALUE_TYPE && + "Simple vector VT not representable by simple integer vector VT!"); + return VecTy; + } + + /// Return the type converted to an equivalently sized integer or vector + /// with integer element type. Similar to changeVectorElementTypeToInteger, + /// but also handles scalars. + MVT changeTypeToInteger() { + if (isVector()) + return changeVectorElementTypeToInteger(); + return MVT::getIntegerVT(getSizeInBits()); + } + + /// Return a VT for a vector type with the same element type but + /// half the number of elements. + MVT getHalfNumVectorElementsVT() const { + MVT EltVT = getVectorElementType(); + auto EltCnt = getVectorElementCount(); + assert(EltCnt.isKnownEven() && "Splitting vector, but not in half!"); + return getVectorVT(EltVT, EltCnt.divideCoefficientBy(2)); + } + + /// Returns true if the given vector is a power of 2. + bool isPow2VectorType() const { + unsigned NElts = getVectorMinNumElements(); + return !(NElts & (NElts - 1)); + } + + /// Widens the length of the given vector MVT up to the nearest power of 2 + /// and returns that type. + MVT getPow2VectorType() const { + if (isPow2VectorType()) + return *this; + + ElementCount NElts = getVectorElementCount(); + unsigned NewMinCount = 1 << Log2_32_Ceil(NElts.getKnownMinValue()); + NElts = ElementCount::get(NewMinCount, NElts.isScalable()); + return MVT::getVectorVT(getVectorElementType(), NElts); + } + + /// If this is a vector, return the element type, otherwise return this. + MVT getScalarType() const { + return isVector() ? getVectorElementType() : *this; + } + + MVT getVectorElementType() const { + // clang-format off + switch (SimpleTy) { + default: + llvm_unreachable("Not a vector MVT!"); + case v1i1: + case v2i1: + case v4i1: + case v8i1: + case v16i1: + case v32i1: + case v64i1: + case v128i1: + case v256i1: + case v512i1: + case v1024i1: + case v2048i1: + case nxv1i1: + case nxv2i1: + case nxv4i1: + case nxv8i1: + case nxv16i1: + case nxv32i1: + case nxv64i1: return i1; + case v128i2: + case v256i2: return i2; + case v64i4: + case v128i4: return i4; + case v1i8: + case v2i8: + case v4i8: + case v8i8: + case v16i8: + case v32i8: + case v64i8: + case v128i8: + case v256i8: + case v512i8: + case v1024i8: + case nxv1i8: + case nxv2i8: + case nxv4i8: + case nxv8i8: + case nxv16i8: + case nxv32i8: + case nxv64i8: return i8; + case v1i16: + case v2i16: + case v3i16: + case v4i16: + case v8i16: + case v16i16: + case v32i16: + case v64i16: + case v128i16: + case v256i16: + case v512i16: + case nxv1i16: + case nxv2i16: + case nxv4i16: + case nxv8i16: + case nxv16i16: + case nxv32i16: return i16; + case v1i32: + case v2i32: + case v3i32: + case v4i32: + case v5i32: + case v6i32: + case v7i32: + case v8i32: + case v9i32: + case v10i32: + case v11i32: + case v12i32: + case v16i32: + case v32i32: + case v64i32: + case v128i32: + case v256i32: + case v512i32: + case v1024i32: + case v2048i32: + case nxv1i32: + case nxv2i32: + case nxv4i32: + case nxv8i32: + case nxv16i32: + case nxv32i32: return i32; + case v1i64: + case v2i64: + case v3i64: + case v4i64: + case v8i64: + case v16i64: + case v32i64: + case v64i64: + case v128i64: + case v256i64: + case nxv1i64: + case nxv2i64: + case nxv4i64: + case nxv8i64: + case nxv16i64: + case nxv32i64: return i64; + case v1i128: return i128; + case v1f16: + case v2f16: + case v3f16: + case v4f16: + case v8f16: + case v16f16: + case v32f16: + case v64f16: + case v128f16: + case v256f16: + case v512f16: + case nxv1f16: + case nxv2f16: + case nxv4f16: + case nxv8f16: + case nxv16f16: + case nxv32f16: return f16; + case v2bf16: + case v3bf16: + case v4bf16: + case v8bf16: + case v16bf16: + case v32bf16: + case v64bf16: + case v128bf16: + case nxv1bf16: + case nxv2bf16: + case nxv4bf16: + case nxv8bf16: + case nxv16bf16: + case nxv32bf16: return bf16; + case v1f32: + case v2f32: + case v3f32: + case v4f32: + case v5f32: + case v6f32: + case v7f32: + case v8f32: + case v9f32: + case v10f32: + case v11f32: + case v12f32: + case v16f32: + case v32f32: + case v64f32: + case v128f32: + case v256f32: + case v512f32: + case v1024f32: + case v2048f32: + case nxv1f32: + case nxv2f32: + case nxv4f32: + case nxv8f32: + case nxv16f32: return f32; + case v1f64: + case v2f64: + case v3f64: + case v4f64: + case v8f64: + case v16f64: + case v32f64: + case v64f64: + case v128f64: + case v256f64: + case nxv1f64: + case nxv2f64: + case nxv4f64: + case nxv8f64: return f64; + } + // clang-format on + } + + /// Given a vector type, return the minimum number of elements it contains. + unsigned getVectorMinNumElements() const { + switch (SimpleTy) { + default: + llvm_unreachable("Not a vector MVT!"); + case v2048i1: + case v2048i32: + case v2048f32: return 2048; + case v1024i1: + case v1024i8: + case v1024i32: + case v1024f32: return 1024; + case v512i1: + case v512i8: + case v512i16: + case v512i32: + case v512f16: + case v512f32: return 512; + case v256i1: + case v256i2: + case v256i8: + case v256i16: + case v256f16: + case v256i32: + case v256i64: + case v256f32: + case v256f64: return 256; + case v128i1: + case v128i2: + case v128i4: + case v128i8: + case v128i16: + case v128i32: + case v128i64: + case v128f16: + case v128bf16: + case v128f32: + case v128f64: return 128; + case v64i1: + case v64i4: + case v64i8: + case v64i16: + case v64i32: + case v64i64: + case v64f16: + case v64bf16: + case v64f32: + case v64f64: + case nxv64i1: + case nxv64i8: return 64; + case v32i1: + case v32i8: + case v32i16: + case v32i32: + case v32i64: + case v32f16: + case v32bf16: + case v32f32: + case v32f64: + case nxv32i1: + case nxv32i8: + case nxv32i16: + case nxv32i32: + case nxv32i64: + case nxv32f16: + case nxv32bf16: return 32; + case v16i1: + case v16i8: + case v16i16: + case v16i32: + case v16i64: + case v16f16: + case v16bf16: + case v16f32: + case v16f64: + case nxv16i1: + case nxv16i8: + case nxv16i16: + case nxv16i32: + case nxv16i64: + case nxv16f16: + case nxv16bf16: + case nxv16f32: return 16; + case v12i32: + case v12f32: return 12; + case v11i32: + case v11f32: return 11; + case v10i32: + case v10f32: return 10; + case v9i32: + case v9f32: return 9; + case v8i1: + case v8i8: + case v8i16: + case v8i32: + case v8i64: + case v8f16: + case v8bf16: + case v8f32: + case v8f64: + case nxv8i1: + case nxv8i8: + case nxv8i16: + case nxv8i32: + case nxv8i64: + case nxv8f16: + case nxv8bf16: + case nxv8f32: + case nxv8f64: return 8; + case v7i32: + case v7f32: return 7; + case v6i32: + case v6f32: return 6; + case v5i32: + case v5f32: return 5; + case v4i1: + case v4i8: + case v4i16: + case v4i32: + case v4i64: + case v4f16: + case v4bf16: + case v4f32: + case v4f64: + case nxv4i1: + case nxv4i8: + case nxv4i16: + case nxv4i32: + case nxv4i64: + case nxv4f16: + case nxv4bf16: + case nxv4f32: + case nxv4f64: return 4; + case v3i16: + case v3i32: + case v3i64: + case v3f16: + case v3bf16: + case v3f32: + case v3f64: return 3; + case v2i1: + case v2i8: + case v2i16: + case v2i32: + case v2i64: + case v2f16: + case v2bf16: + case v2f32: + case v2f64: + case nxv2i1: + case nxv2i8: + case nxv2i16: + case nxv2i32: + case nxv2i64: + case nxv2f16: + case nxv2bf16: + case nxv2f32: + case nxv2f64: return 2; + case v1i1: + case v1i8: + case v1i16: + case v1i32: + case v1i64: + case v1i128: + case v1f16: + case v1f32: + case v1f64: + case nxv1i1: + case nxv1i8: + case nxv1i16: + case nxv1i32: + case nxv1i64: + case nxv1f16: + case nxv1bf16: + case nxv1f32: + case nxv1f64: return 1; + } + } + + ElementCount getVectorElementCount() const { + return ElementCount::get(getVectorMinNumElements(), isScalableVector()); + } + + unsigned getVectorNumElements() const { + if (isScalableVector()) + llvm::reportInvalidSizeRequest( + "Possible incorrect use of MVT::getVectorNumElements() for " + "scalable vector. Scalable flag may be dropped, use " + "MVT::getVectorElementCount() instead"); + return getVectorMinNumElements(); + } + + /// Returns the size of the specified MVT in bits. + /// + /// If the value type is a scalable vector type, the scalable property will + /// be set and the runtime size will be a positive integer multiple of the + /// base size. + TypeSize getSizeInBits() const { + switch (SimpleTy) { + default: + llvm_unreachable("getSizeInBits called on extended MVT."); + case Other: + llvm_unreachable("Value type is non-standard value, Other."); + case iPTR: + llvm_unreachable("Value type size is target-dependent. Ask TLI."); + case iPTRAny: + case iAny: + case fAny: + case vAny: + case Any: + llvm_unreachable("Value type is overloaded."); + case token: + llvm_unreachable("Token type is a sentinel that cannot be used " + "in codegen and has no size"); + case Metadata: + llvm_unreachable("Value type is metadata."); + case i1: + case v1i1: return TypeSize::Fixed(1); + case nxv1i1: return TypeSize::Scalable(1); + case i2: + case v2i1: return TypeSize::Fixed(2); + case nxv2i1: return TypeSize::Scalable(2); + case i4: + case v4i1: return TypeSize::Fixed(4); + case nxv4i1: return TypeSize::Scalable(4); + case i8 : + case v1i8: + case v8i1: return TypeSize::Fixed(8); + case nxv1i8: + case nxv8i1: return TypeSize::Scalable(8); + case i16 : + case f16: + case bf16: + case v16i1: + case v2i8: + case v1i16: + case v1f16: return TypeSize::Fixed(16); + case nxv16i1: + case nxv2i8: + case nxv1i16: + case nxv1bf16: + case nxv1f16: return TypeSize::Scalable(16); + case f32 : + case i32 : + case v32i1: + case v4i8: + case v2i16: + case v2f16: + case v2bf16: + case v1f32: + case v1i32: return TypeSize::Fixed(32); + case nxv32i1: + case nxv4i8: + case nxv2i16: + case nxv1i32: + case nxv2f16: + case nxv2bf16: + case nxv1f32: return TypeSize::Scalable(32); + case v3i16: + case v3f16: + case v3bf16: return TypeSize::Fixed(48); + case x86mmx: + case f64 : + case i64 : + case v64i1: + case v8i8: + case v4i16: + case v2i32: + case v1i64: + case v4f16: + case v4bf16: + case v2f32: + case v1f64: return TypeSize::Fixed(64); + case nxv64i1: + case nxv8i8: + case nxv4i16: + case nxv2i32: + case nxv1i64: + case nxv4f16: + case nxv4bf16: + case nxv2f32: + case nxv1f64: return TypeSize::Scalable(64); + case f80 : return TypeSize::Fixed(80); + case v3i32: + case v3f32: return TypeSize::Fixed(96); + case f128: + case ppcf128: + case i128: + case v128i1: + case v16i8: + case v8i16: + case v4i32: + case v2i64: + case v1i128: + case v8f16: + case v8bf16: + case v4f32: + case v2f64: return TypeSize::Fixed(128); + case nxv16i8: + case nxv8i16: + case nxv4i32: + case nxv2i64: + case nxv8f16: + case nxv8bf16: + case nxv4f32: + case nxv2f64: return TypeSize::Scalable(128); + case v5i32: + case v5f32: return TypeSize::Fixed(160); + case v6i32: + case v3i64: + case v6f32: + case v3f64: return TypeSize::Fixed(192); + case v7i32: + case v7f32: return TypeSize::Fixed(224); + case v256i1: + case v128i2: + case v64i4: + case v32i8: + case v16i16: + case v8i32: + case v4i64: + case v16f16: + case v16bf16: + case v8f32: + case v4f64: return TypeSize::Fixed(256); + case nxv32i8: + case nxv16i16: + case nxv8i32: + case nxv4i64: + case nxv16f16: + case nxv16bf16: + case nxv8f32: + case nxv4f64: return TypeSize::Scalable(256); + case v9i32: + case v9f32: return TypeSize::Fixed(288); + case v10i32: + case v10f32: return TypeSize::Fixed(320); + case v11i32: + case v11f32: return TypeSize::Fixed(352); + case v12i32: + case v12f32: return TypeSize::Fixed(384); + case i64x8: + case v512i1: + case v256i2: + case v128i4: + case v64i8: + case v32i16: + case v16i32: + case v8i64: + case v32f16: + case v32bf16: + case v16f32: + case v8f64: return TypeSize::Fixed(512); + case nxv64i8: + case nxv32i16: + case nxv16i32: + case nxv8i64: + case nxv32f16: + case nxv32bf16: + case nxv16f32: + case nxv8f64: return TypeSize::Scalable(512); + case v1024i1: + case v128i8: + case v64i16: + case v32i32: + case v16i64: + case v64f16: + case v64bf16: + case v32f32: + case v16f64: return TypeSize::Fixed(1024); + case nxv32i32: + case nxv16i64: return TypeSize::Scalable(1024); + case v2048i1: + case v256i8: + case v128i16: + case v64i32: + case v32i64: + case v128f16: + case v128bf16: + case v64f32: + case v32f64: return TypeSize::Fixed(2048); + case nxv32i64: return TypeSize::Scalable(2048); + case v512i8: + case v256i16: + case v128i32: + case v64i64: + case v256f16: + case v128f32: + case v64f64: return TypeSize::Fixed(4096); + case v1024i8: + case v512i16: + case v256i32: + case v128i64: + case v512f16: + case v256f32: + case x86amx: + case v128f64: return TypeSize::Fixed(8192); + case v512i32: + case v256i64: + case v512f32: + case v256f64: return TypeSize::Fixed(16384); + case v1024i32: + case v1024f32: return TypeSize::Fixed(32768); + case v2048i32: + case v2048f32: return TypeSize::Fixed(65536); + case funcref: + case externref: return TypeSize::Fixed(0); // opaque type + } + } + + /// Return the size of the specified fixed width value type in bits. The + /// function will assert if the type is scalable. + uint64_t getFixedSizeInBits() const { + return getSizeInBits().getFixedValue(); + } + + uint64_t getScalarSizeInBits() const { + return getScalarType().getSizeInBits().getFixedValue(); + } + + /// Return the number of bytes overwritten by a store of the specified value + /// type. + /// + /// If the value type is a scalable vector type, the scalable property will + /// be set and the runtime size will be a positive integer multiple of the + /// base size. + TypeSize getStoreSize() const { + TypeSize BaseSize = getSizeInBits(); + return {(BaseSize.getKnownMinValue() + 7) / 8, BaseSize.isScalable()}; + } + + // Return the number of bytes overwritten by a store of this value type or + // this value type's element type in the case of a vector. + uint64_t getScalarStoreSize() const { + return getScalarType().getStoreSize().getFixedValue(); + } + + /// Return the number of bits overwritten by a store of the specified value + /// type. + /// + /// If the value type is a scalable vector type, the scalable property will + /// be set and the runtime size will be a positive integer multiple of the + /// base size. + TypeSize getStoreSizeInBits() const { + return getStoreSize() * 8; + } + + /// Returns true if the number of bits for the type is a multiple of an + /// 8-bit byte. + bool isByteSized() const { return getSizeInBits().isKnownMultipleOf(8); } + + /// Return true if we know at compile time this has more bits than VT. + bool knownBitsGT(MVT VT) const { + return TypeSize::isKnownGT(getSizeInBits(), VT.getSizeInBits()); + } + + /// Return true if we know at compile time this has more than or the same + /// bits as VT. + bool knownBitsGE(MVT VT) const { + return TypeSize::isKnownGE(getSizeInBits(), VT.getSizeInBits()); + } + + /// Return true if we know at compile time this has fewer bits than VT. + bool knownBitsLT(MVT VT) const { + return TypeSize::isKnownLT(getSizeInBits(), VT.getSizeInBits()); + } + + /// Return true if we know at compile time this has fewer than or the same + /// bits as VT. + bool knownBitsLE(MVT VT) const { + return TypeSize::isKnownLE(getSizeInBits(), VT.getSizeInBits()); + } + + /// Return true if this has more bits than VT. + bool bitsGT(MVT VT) const { + assert(isScalableVector() == VT.isScalableVector() && + "Comparison between scalable and fixed types"); + return knownBitsGT(VT); + } + + /// Return true if this has no less bits than VT. + bool bitsGE(MVT VT) const { + assert(isScalableVector() == VT.isScalableVector() && + "Comparison between scalable and fixed types"); + return knownBitsGE(VT); + } + + /// Return true if this has less bits than VT. + bool bitsLT(MVT VT) const { + assert(isScalableVector() == VT.isScalableVector() && + "Comparison between scalable and fixed types"); + return knownBitsLT(VT); + } + + /// Return true if this has no more bits than VT. + bool bitsLE(MVT VT) const { + assert(isScalableVector() == VT.isScalableVector() && + "Comparison between scalable and fixed types"); + return knownBitsLE(VT); + } + + static MVT getFloatingPointVT(unsigned BitWidth) { + switch (BitWidth) { + default: + llvm_unreachable("Bad bit width!"); + case 16: + return MVT::f16; + case 32: + return MVT::f32; + case 64: + return MVT::f64; + case 80: + return MVT::f80; + case 128: + return MVT::f128; + } + } + + static MVT getIntegerVT(unsigned BitWidth) { + switch (BitWidth) { + default: + return (MVT::SimpleValueType)(MVT::INVALID_SIMPLE_VALUE_TYPE); + case 1: + return MVT::i1; + case 2: + return MVT::i2; + case 4: + return MVT::i4; + case 8: + return MVT::i8; + case 16: + return MVT::i16; + case 32: + return MVT::i32; + case 64: + return MVT::i64; + case 128: + return MVT::i128; + } + } + + static MVT getVectorVT(MVT VT, unsigned NumElements) { + // clang-format off + switch (VT.SimpleTy) { + default: + break; + case MVT::i1: + if (NumElements == 1) return MVT::v1i1; + if (NumElements == 2) return MVT::v2i1; + if (NumElements == 4) return MVT::v4i1; + if (NumElements == 8) return MVT::v8i1; + if (NumElements == 16) return MVT::v16i1; + if (NumElements == 32) return MVT::v32i1; + if (NumElements == 64) return MVT::v64i1; + if (NumElements == 128) return MVT::v128i1; + if (NumElements == 256) return MVT::v256i1; + if (NumElements == 512) return MVT::v512i1; + if (NumElements == 1024) return MVT::v1024i1; + if (NumElements == 2048) return MVT::v2048i1; + break; + case MVT::i2: + if (NumElements == 128) return MVT::v128i2; + if (NumElements == 256) return MVT::v256i2; + break; + case MVT::i4: + if (NumElements == 64) return MVT::v64i4; + if (NumElements == 128) return MVT::v128i4; + break; + case MVT::i8: + if (NumElements == 1) return MVT::v1i8; + if (NumElements == 2) return MVT::v2i8; + if (NumElements == 4) return MVT::v4i8; + if (NumElements == 8) return MVT::v8i8; + if (NumElements == 16) return MVT::v16i8; + if (NumElements == 32) return MVT::v32i8; + if (NumElements == 64) return MVT::v64i8; + if (NumElements == 128) return MVT::v128i8; + if (NumElements == 256) return MVT::v256i8; + if (NumElements == 512) return MVT::v512i8; + if (NumElements == 1024) return MVT::v1024i8; + break; + case MVT::i16: + if (NumElements == 1) return MVT::v1i16; + if (NumElements == 2) return MVT::v2i16; + if (NumElements == 3) return MVT::v3i16; + if (NumElements == 4) return MVT::v4i16; + if (NumElements == 8) return MVT::v8i16; + if (NumElements == 16) return MVT::v16i16; + if (NumElements == 32) return MVT::v32i16; + if (NumElements == 64) return MVT::v64i16; + if (NumElements == 128) return MVT::v128i16; + if (NumElements == 256) return MVT::v256i16; + if (NumElements == 512) return MVT::v512i16; + break; + case MVT::i32: + if (NumElements == 1) return MVT::v1i32; + if (NumElements == 2) return MVT::v2i32; + if (NumElements == 3) return MVT::v3i32; + if (NumElements == 4) return MVT::v4i32; + if (NumElements == 5) return MVT::v5i32; + if (NumElements == 6) return MVT::v6i32; + if (NumElements == 7) return MVT::v7i32; + if (NumElements == 8) return MVT::v8i32; + if (NumElements == 9) return MVT::v9i32; + if (NumElements == 10) return MVT::v10i32; + if (NumElements == 11) return MVT::v11i32; + if (NumElements == 12) return MVT::v12i32; + if (NumElements == 16) return MVT::v16i32; + if (NumElements == 32) return MVT::v32i32; + if (NumElements == 64) return MVT::v64i32; + if (NumElements == 128) return MVT::v128i32; + if (NumElements == 256) return MVT::v256i32; + if (NumElements == 512) return MVT::v512i32; + if (NumElements == 1024) return MVT::v1024i32; + if (NumElements == 2048) return MVT::v2048i32; + break; + case MVT::i64: + if (NumElements == 1) return MVT::v1i64; + if (NumElements == 2) return MVT::v2i64; + if (NumElements == 3) return MVT::v3i64; + if (NumElements == 4) return MVT::v4i64; + if (NumElements == 8) return MVT::v8i64; + if (NumElements == 16) return MVT::v16i64; + if (NumElements == 32) return MVT::v32i64; + if (NumElements == 64) return MVT::v64i64; + if (NumElements == 128) return MVT::v128i64; + if (NumElements == 256) return MVT::v256i64; + break; + case MVT::i128: + if (NumElements == 1) return MVT::v1i128; + break; + case MVT::f16: + if (NumElements == 1) return MVT::v1f16; + if (NumElements == 2) return MVT::v2f16; + if (NumElements == 3) return MVT::v3f16; + if (NumElements == 4) return MVT::v4f16; + if (NumElements == 8) return MVT::v8f16; + if (NumElements == 16) return MVT::v16f16; + if (NumElements == 32) return MVT::v32f16; + if (NumElements == 64) return MVT::v64f16; + if (NumElements == 128) return MVT::v128f16; + if (NumElements == 256) return MVT::v256f16; + if (NumElements == 512) return MVT::v512f16; + break; + case MVT::bf16: + if (NumElements == 2) return MVT::v2bf16; + if (NumElements == 3) return MVT::v3bf16; + if (NumElements == 4) return MVT::v4bf16; + if (NumElements == 8) return MVT::v8bf16; + if (NumElements == 16) return MVT::v16bf16; + if (NumElements == 32) return MVT::v32bf16; + if (NumElements == 64) return MVT::v64bf16; + if (NumElements == 128) return MVT::v128bf16; + break; + case MVT::f32: + if (NumElements == 1) return MVT::v1f32; + if (NumElements == 2) return MVT::v2f32; + if (NumElements == 3) return MVT::v3f32; + if (NumElements == 4) return MVT::v4f32; + if (NumElements == 5) return MVT::v5f32; + if (NumElements == 6) return MVT::v6f32; + if (NumElements == 7) return MVT::v7f32; + if (NumElements == 8) return MVT::v8f32; + if (NumElements == 9) return MVT::v9f32; + if (NumElements == 10) return MVT::v10f32; + if (NumElements == 11) return MVT::v11f32; + if (NumElements == 12) return MVT::v12f32; + if (NumElements == 16) return MVT::v16f32; + if (NumElements == 32) return MVT::v32f32; + if (NumElements == 64) return MVT::v64f32; + if (NumElements == 128) return MVT::v128f32; + if (NumElements == 256) return MVT::v256f32; + if (NumElements == 512) return MVT::v512f32; + if (NumElements == 1024) return MVT::v1024f32; + if (NumElements == 2048) return MVT::v2048f32; + break; + case MVT::f64: + if (NumElements == 1) return MVT::v1f64; + if (NumElements == 2) return MVT::v2f64; + if (NumElements == 3) return MVT::v3f64; + if (NumElements == 4) return MVT::v4f64; + if (NumElements == 8) return MVT::v8f64; + if (NumElements == 16) return MVT::v16f64; + if (NumElements == 32) return MVT::v32f64; + if (NumElements == 64) return MVT::v64f64; + if (NumElements == 128) return MVT::v128f64; + if (NumElements == 256) return MVT::v256f64; + break; + } + return (MVT::SimpleValueType)(MVT::INVALID_SIMPLE_VALUE_TYPE); + // clang-format on + } + + static MVT getScalableVectorVT(MVT VT, unsigned NumElements) { + switch(VT.SimpleTy) { + default: + break; + case MVT::i1: + if (NumElements == 1) return MVT::nxv1i1; + if (NumElements == 2) return MVT::nxv2i1; + if (NumElements == 4) return MVT::nxv4i1; + if (NumElements == 8) return MVT::nxv8i1; + if (NumElements == 16) return MVT::nxv16i1; + if (NumElements == 32) return MVT::nxv32i1; + if (NumElements == 64) return MVT::nxv64i1; + break; + case MVT::i8: + if (NumElements == 1) return MVT::nxv1i8; + if (NumElements == 2) return MVT::nxv2i8; + if (NumElements == 4) return MVT::nxv4i8; + if (NumElements == 8) return MVT::nxv8i8; + if (NumElements == 16) return MVT::nxv16i8; + if (NumElements == 32) return MVT::nxv32i8; + if (NumElements == 64) return MVT::nxv64i8; + break; + case MVT::i16: + if (NumElements == 1) return MVT::nxv1i16; + if (NumElements == 2) return MVT::nxv2i16; + if (NumElements == 4) return MVT::nxv4i16; + if (NumElements == 8) return MVT::nxv8i16; + if (NumElements == 16) return MVT::nxv16i16; + if (NumElements == 32) return MVT::nxv32i16; + break; + case MVT::i32: + if (NumElements == 1) return MVT::nxv1i32; + if (NumElements == 2) return MVT::nxv2i32; + if (NumElements == 4) return MVT::nxv4i32; + if (NumElements == 8) return MVT::nxv8i32; + if (NumElements == 16) return MVT::nxv16i32; + if (NumElements == 32) return MVT::nxv32i32; + break; + case MVT::i64: + if (NumElements == 1) return MVT::nxv1i64; + if (NumElements == 2) return MVT::nxv2i64; + if (NumElements == 4) return MVT::nxv4i64; + if (NumElements == 8) return MVT::nxv8i64; + if (NumElements == 16) return MVT::nxv16i64; + if (NumElements == 32) return MVT::nxv32i64; + break; + case MVT::f16: + if (NumElements == 1) return MVT::nxv1f16; + if (NumElements == 2) return MVT::nxv2f16; + if (NumElements == 4) return MVT::nxv4f16; + if (NumElements == 8) return MVT::nxv8f16; + if (NumElements == 16) return MVT::nxv16f16; + if (NumElements == 32) return MVT::nxv32f16; + break; + case MVT::bf16: + if (NumElements == 1) return MVT::nxv1bf16; + if (NumElements == 2) return MVT::nxv2bf16; + if (NumElements == 4) return MVT::nxv4bf16; + if (NumElements == 8) return MVT::nxv8bf16; + if (NumElements == 16) return MVT::nxv16bf16; + if (NumElements == 32) return MVT::nxv32bf16; + break; + case MVT::f32: + if (NumElements == 1) return MVT::nxv1f32; + if (NumElements == 2) return MVT::nxv2f32; + if (NumElements == 4) return MVT::nxv4f32; + if (NumElements == 8) return MVT::nxv8f32; + if (NumElements == 16) return MVT::nxv16f32; + break; + case MVT::f64: + if (NumElements == 1) return MVT::nxv1f64; + if (NumElements == 2) return MVT::nxv2f64; + if (NumElements == 4) return MVT::nxv4f64; + if (NumElements == 8) return MVT::nxv8f64; + break; + } + return (MVT::SimpleValueType)(MVT::INVALID_SIMPLE_VALUE_TYPE); + } + + static MVT getVectorVT(MVT VT, unsigned NumElements, bool IsScalable) { + if (IsScalable) + return getScalableVectorVT(VT, NumElements); + return getVectorVT(VT, NumElements); + } + + static MVT getVectorVT(MVT VT, ElementCount EC) { + if (EC.isScalable()) + return getScalableVectorVT(VT, EC.getKnownMinValue()); + return getVectorVT(VT, EC.getKnownMinValue()); + } + + /// Return the value type corresponding to the specified type. This returns + /// all pointers as iPTR. If HandleUnknown is true, unknown types are + /// returned as Other, otherwise they are invalid. + static MVT getVT(Type *Ty, bool HandleUnknown = false); + + public: + /// SimpleValueType Iteration + /// @{ + static auto all_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_VALUETYPE, MVT::LAST_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto integer_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_INTEGER_VALUETYPE, + MVT::LAST_INTEGER_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto fp_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_FP_VALUETYPE, MVT::LAST_FP_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_VECTOR_VALUETYPE, + MVT::LAST_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto fixedlen_vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_FIXEDLEN_VECTOR_VALUETYPE, + MVT::LAST_FIXEDLEN_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto scalable_vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_SCALABLE_VECTOR_VALUETYPE, + MVT::LAST_SCALABLE_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto integer_fixedlen_vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE, + MVT::LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto fp_fixedlen_vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE, + MVT::LAST_FP_FIXEDLEN_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto integer_scalable_vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE, + MVT::LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + + static auto fp_scalable_vector_valuetypes() { + return enum_seq_inclusive(MVT::FIRST_FP_SCALABLE_VECTOR_VALUETYPE, + MVT::LAST_FP_SCALABLE_VECTOR_VALUETYPE, + force_iteration_on_noniterable_enum); + } + /// @} + }; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_MACHINEVALUETYPE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ManagedStatic.h b/contrib/libs/llvm16/include/llvm/Support/ManagedStatic.h new file mode 100644 index 00000000000..6a16ad63045 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ManagedStatic.h @@ -0,0 +1,136 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ManagedStatic.h - Static Global wrapper ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the ManagedStatic class and the llvm_shutdown() function. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MANAGEDSTATIC_H +#define LLVM_SUPPORT_MANAGEDSTATIC_H + +#include <atomic> +#include <cstddef> + +namespace llvm { + +/// object_creator - Helper method for ManagedStatic. +template <class C> struct object_creator { + static void *call() { return new C(); } +}; + +/// object_deleter - Helper method for ManagedStatic. +/// +template <typename T> struct object_deleter { + static void call(void *Ptr) { delete (T *)Ptr; } +}; +template <typename T, size_t N> struct object_deleter<T[N]> { + static void call(void *Ptr) { delete[](T *)Ptr; } +}; + +// ManagedStatic must be initialized to zero, and it must *not* have a dynamic +// initializer because managed statics are often created while running other +// dynamic initializers. In standard C++11, the best way to accomplish this is +// with a constexpr default constructor. However, different versions of the +// Visual C++ compiler have had bugs where, even though the constructor may be +// constexpr, a dynamic initializer may be emitted depending on optimization +// settings. For the affected versions of MSVC, use the old linker +// initialization pattern of not providing a constructor and leaving the fields +// uninitialized. See http://llvm.org/PR41367 for details. +#if !defined(_MSC_VER) || (_MSC_VER >= 1925) || defined(__clang__) +#define LLVM_USE_CONSTEXPR_CTOR +#endif + +/// ManagedStaticBase - Common base class for ManagedStatic instances. +class ManagedStaticBase { +protected: +#ifdef LLVM_USE_CONSTEXPR_CTOR + mutable std::atomic<void *> Ptr{}; + mutable void (*DeleterFn)(void *) = nullptr; + mutable const ManagedStaticBase *Next = nullptr; +#else + // This should only be used as a static variable, which guarantees that this + // will be zero initialized. + mutable std::atomic<void *> Ptr; + mutable void (*DeleterFn)(void *); + mutable const ManagedStaticBase *Next; +#endif + + void RegisterManagedStatic(void *(*creator)(), void (*deleter)(void*)) const; + +public: +#ifdef LLVM_USE_CONSTEXPR_CTOR + constexpr ManagedStaticBase() = default; +#endif + + /// isConstructed - Return true if this object has not been created yet. + bool isConstructed() const { return Ptr != nullptr; } + + void destroy() const; +}; + +/// ManagedStatic - This transparently changes the behavior of global statics to +/// be lazily constructed on demand (good for reducing startup times of dynamic +/// libraries that link in LLVM components) and for making destruction be +/// explicit through the llvm_shutdown() function call. +/// +template <class C, class Creator = object_creator<C>, + class Deleter = object_deleter<C>> +class ManagedStatic : public ManagedStaticBase { +public: + // Accessors. + C &operator*() { + void *Tmp = Ptr.load(std::memory_order_acquire); + if (!Tmp) + RegisterManagedStatic(Creator::call, Deleter::call); + + return *static_cast<C *>(Ptr.load(std::memory_order_relaxed)); + } + + C *operator->() { return &**this; } + + const C &operator*() const { + void *Tmp = Ptr.load(std::memory_order_acquire); + if (!Tmp) + RegisterManagedStatic(Creator::call, Deleter::call); + + return *static_cast<C *>(Ptr.load(std::memory_order_relaxed)); + } + + const C *operator->() const { return &**this; } + + // Extract the instance, leaving the ManagedStatic uninitialized. The + // user is then responsible for the lifetime of the returned instance. + C *claim() { + return static_cast<C *>(Ptr.exchange(nullptr)); + } +}; + +/// llvm_shutdown - Deallocate and destroy all ManagedStatic variables. +void llvm_shutdown(); + +/// llvm_shutdown_obj - This is a simple helper class that calls +/// llvm_shutdown() when it is destroyed. +struct llvm_shutdown_obj { + llvm_shutdown_obj() = default; + ~llvm_shutdown_obj() { llvm_shutdown(); } +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_MANAGEDSTATIC_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MathExtras.h b/contrib/libs/llvm16/include/llvm/Support/MathExtras.h new file mode 100644 index 00000000000..4c04aa41c76 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MathExtras.h @@ -0,0 +1,787 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains some functions that are useful for math stuff. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MATHEXTRAS_H +#define LLVM_SUPPORT_MATHEXTRAS_H + +#include "llvm/ADT/bit.h" +#include "llvm/Support/Compiler.h" +#include <cassert> +#include <climits> +#include <cstdint> +#include <cstring> +#include <limits> +#include <type_traits> + +namespace llvm { + +/// The behavior an operation has on an input of 0. +enum ZeroBehavior { + /// The returned value is undefined. + ZB_Undefined, + /// The returned value is numeric_limits<T>::max() + ZB_Max +}; + +/// Mathematical constants. +namespace numbers { +// TODO: Track C++20 std::numbers. +// TODO: Favor using the hexadecimal FP constants (requires C++17). +constexpr double e = 2.7182818284590452354, // (0x1.5bf0a8b145749P+1) https://oeis.org/A001113 + egamma = .57721566490153286061, // (0x1.2788cfc6fb619P-1) https://oeis.org/A001620 + ln2 = .69314718055994530942, // (0x1.62e42fefa39efP-1) https://oeis.org/A002162 + ln10 = 2.3025850929940456840, // (0x1.24bb1bbb55516P+1) https://oeis.org/A002392 + log2e = 1.4426950408889634074, // (0x1.71547652b82feP+0) + log10e = .43429448190325182765, // (0x1.bcb7b1526e50eP-2) + pi = 3.1415926535897932385, // (0x1.921fb54442d18P+1) https://oeis.org/A000796 + inv_pi = .31830988618379067154, // (0x1.45f306bc9c883P-2) https://oeis.org/A049541 + sqrtpi = 1.7724538509055160273, // (0x1.c5bf891b4ef6bP+0) https://oeis.org/A002161 + inv_sqrtpi = .56418958354775628695, // (0x1.20dd750429b6dP-1) https://oeis.org/A087197 + sqrt2 = 1.4142135623730950488, // (0x1.6a09e667f3bcdP+0) https://oeis.org/A00219 + inv_sqrt2 = .70710678118654752440, // (0x1.6a09e667f3bcdP-1) + sqrt3 = 1.7320508075688772935, // (0x1.bb67ae8584caaP+0) https://oeis.org/A002194 + inv_sqrt3 = .57735026918962576451, // (0x1.279a74590331cP-1) + phi = 1.6180339887498948482; // (0x1.9e3779b97f4a8P+0) https://oeis.org/A001622 +constexpr float ef = 2.71828183F, // (0x1.5bf0a8P+1) https://oeis.org/A001113 + egammaf = .577215665F, // (0x1.2788d0P-1) https://oeis.org/A001620 + ln2f = .693147181F, // (0x1.62e430P-1) https://oeis.org/A002162 + ln10f = 2.30258509F, // (0x1.26bb1cP+1) https://oeis.org/A002392 + log2ef = 1.44269504F, // (0x1.715476P+0) + log10ef = .434294482F, // (0x1.bcb7b2P-2) + pif = 3.14159265F, // (0x1.921fb6P+1) https://oeis.org/A000796 + inv_pif = .318309886F, // (0x1.45f306P-2) https://oeis.org/A049541 + sqrtpif = 1.77245385F, // (0x1.c5bf8aP+0) https://oeis.org/A002161 + inv_sqrtpif = .564189584F, // (0x1.20dd76P-1) https://oeis.org/A087197 + sqrt2f = 1.41421356F, // (0x1.6a09e6P+0) https://oeis.org/A002193 + inv_sqrt2f = .707106781F, // (0x1.6a09e6P-1) + sqrt3f = 1.73205081F, // (0x1.bb67aeP+0) https://oeis.org/A002194 + inv_sqrt3f = .577350269F, // (0x1.279a74P-1) + phif = 1.61803399F; // (0x1.9e377aP+0) https://oeis.org/A001622 +} // namespace numbers + +/// Count number of 0's from the least significant bit to the most +/// stopping at the first 1. +/// +/// Only unsigned integral types are allowed. +/// +/// Returns std::numeric_limits<T>::digits on an input of 0. +template <typename T> unsigned countTrailingZeros(T Val) { + static_assert(std::is_unsigned_v<T>, + "Only unsigned integral types are allowed."); + return llvm::countr_zero(Val); +} + +/// Count number of 0's from the most significant bit to the least +/// stopping at the first 1. +/// +/// Only unsigned integral types are allowed. +/// +/// Returns std::numeric_limits<T>::digits on an input of 0. +template <typename T> unsigned countLeadingZeros(T Val) { + static_assert(std::is_unsigned_v<T>, + "Only unsigned integral types are allowed."); + return llvm::countl_zero(Val); +} + +/// Get the index of the first set bit starting from the least +/// significant bit. +/// +/// Only unsigned integral types are allowed. +/// +/// \param ZB the behavior on an input of 0. +template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) { + if (ZB == ZB_Max && Val == 0) + return std::numeric_limits<T>::max(); + + return llvm::countr_zero(Val); +} + +/// Create a bitmask with the N right-most bits set to 1, and all other +/// bits set to 0. Only unsigned types are allowed. +template <typename T> T maskTrailingOnes(unsigned N) { + static_assert(std::is_unsigned<T>::value, "Invalid type!"); + const unsigned Bits = CHAR_BIT * sizeof(T); + assert(N <= Bits && "Invalid bit index"); + return N == 0 ? 0 : (T(-1) >> (Bits - N)); +} + +/// Create a bitmask with the N left-most bits set to 1, and all other +/// bits set to 0. Only unsigned types are allowed. +template <typename T> T maskLeadingOnes(unsigned N) { + return ~maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N); +} + +/// Create a bitmask with the N right-most bits set to 0, and all other +/// bits set to 1. Only unsigned types are allowed. +template <typename T> T maskTrailingZeros(unsigned N) { + return maskLeadingOnes<T>(CHAR_BIT * sizeof(T) - N); +} + +/// Create a bitmask with the N left-most bits set to 0, and all other +/// bits set to 1. Only unsigned types are allowed. +template <typename T> T maskLeadingZeros(unsigned N) { + return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N); +} + +/// Get the index of the last set bit starting from the least +/// significant bit. +/// +/// Only unsigned integral types are allowed. +/// +/// \param ZB the behavior on an input of 0. +template <typename T> T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) { + if (ZB == ZB_Max && Val == 0) + return std::numeric_limits<T>::max(); + + // Use ^ instead of - because both gcc and llvm can remove the associated ^ + // in the __builtin_clz intrinsic on x86. + return llvm::countl_zero(Val) ^ (std::numeric_limits<T>::digits - 1); +} + +/// Macro compressed bit reversal table for 256 bits. +/// +/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable +static const unsigned char BitReverseTable256[256] = { +#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64 +#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16) +#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4) + R6(0), R6(2), R6(1), R6(3) +#undef R2 +#undef R4 +#undef R6 +}; + +/// Reverse the bits in \p Val. +template <typename T> T reverseBits(T Val) { +#if __has_builtin(__builtin_bitreverse8) + if constexpr (std::is_same_v<T, uint8_t>) + return __builtin_bitreverse8(Val); +#endif +#if __has_builtin(__builtin_bitreverse16) + if constexpr (std::is_same_v<T, uint16_t>) + return __builtin_bitreverse16(Val); +#endif +#if __has_builtin(__builtin_bitreverse32) + if constexpr (std::is_same_v<T, uint32_t>) + return __builtin_bitreverse32(Val); +#endif +#if __has_builtin(__builtin_bitreverse64) + if constexpr (std::is_same_v<T, uint64_t>) + return __builtin_bitreverse64(Val); +#endif + + unsigned char in[sizeof(Val)]; + unsigned char out[sizeof(Val)]; + std::memcpy(in, &Val, sizeof(Val)); + for (unsigned i = 0; i < sizeof(Val); ++i) + out[(sizeof(Val) - i) - 1] = BitReverseTable256[in[i]]; + std::memcpy(&Val, out, sizeof(Val)); + return Val; +} + +// NOTE: The following support functions use the _32/_64 extensions instead of +// type overloading so that signed and unsigned integers can be used without +// ambiguity. + +/// Return the high 32 bits of a 64 bit value. +constexpr inline uint32_t Hi_32(uint64_t Value) { + return static_cast<uint32_t>(Value >> 32); +} + +/// Return the low 32 bits of a 64 bit value. +constexpr inline uint32_t Lo_32(uint64_t Value) { + return static_cast<uint32_t>(Value); +} + +/// Make a 64-bit integer from a high / low pair of 32-bit integers. +constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { + return ((uint64_t)High << 32) | (uint64_t)Low; +} + +/// Checks if an integer fits into the given bit width. +template <unsigned N> constexpr inline bool isInt(int64_t x) { + if constexpr (N == 8) + return static_cast<int8_t>(x) == x; + if constexpr (N == 16) + return static_cast<int16_t>(x) == x; + if constexpr (N == 32) + return static_cast<int32_t>(x) == x; + if constexpr (N < 64) + return -(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1)); + (void)x; // MSVC v19.25 warns that x is unused. + return true; +} + +/// Checks if a signed integer is an N bit number shifted left by S. +template <unsigned N, unsigned S> +constexpr inline bool isShiftedInt(int64_t x) { + static_assert( + N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number."); + static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide."); + return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0); +} + +/// Checks if an unsigned integer fits into the given bit width. +template <unsigned N> constexpr inline bool isUInt(uint64_t x) { + static_assert(N > 0, "isUInt<0> doesn't make sense"); + if constexpr (N == 8) + return static_cast<uint8_t>(x) == x; + if constexpr (N == 16) + return static_cast<uint16_t>(x) == x; + if constexpr (N == 32) + return static_cast<uint32_t>(x) == x; + if constexpr (N < 64) + return x < (UINT64_C(1) << (N)); + (void)x; // MSVC v19.25 warns that x is unused. + return true; +} + +/// Checks if a unsigned integer is an N bit number shifted left by S. +template <unsigned N, unsigned S> +constexpr inline bool isShiftedUInt(uint64_t x) { + static_assert( + N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)"); + static_assert(N + S <= 64, + "isShiftedUInt<N, S> with N + S > 64 is too wide."); + // Per the two static_asserts above, S must be strictly less than 64. So + // 1 << S is not undefined behavior. + return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0); +} + +/// Gets the maximum value for a N-bit unsigned integer. +inline uint64_t maxUIntN(uint64_t N) { + assert(N > 0 && N <= 64 && "integer width out of range"); + + // uint64_t(1) << 64 is undefined behavior, so we can't do + // (uint64_t(1) << N) - 1 + // without checking first that N != 64. But this works and doesn't have a + // branch. + return UINT64_MAX >> (64 - N); +} + +/// Gets the minimum value for a N-bit signed integer. +inline int64_t minIntN(int64_t N) { + assert(N > 0 && N <= 64 && "integer width out of range"); + + return UINT64_C(1) + ~(UINT64_C(1) << (N - 1)); +} + +/// Gets the maximum value for a N-bit signed integer. +inline int64_t maxIntN(int64_t N) { + assert(N > 0 && N <= 64 && "integer width out of range"); + + // This relies on two's complement wraparound when N == 64, so we convert to + // int64_t only at the very end to avoid UB. + return (UINT64_C(1) << (N - 1)) - 1; +} + +/// Checks if an unsigned integer fits into the given (dynamic) bit width. +inline bool isUIntN(unsigned N, uint64_t x) { + return N >= 64 || x <= maxUIntN(N); +} + +/// Checks if an signed integer fits into the given (dynamic) bit width. +inline bool isIntN(unsigned N, int64_t x) { + return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N)); +} + +/// Return true if the argument is a non-empty sequence of ones starting at the +/// least significant bit with the remainder zero (32 bit version). +/// Ex. isMask_32(0x0000FFFFU) == true. +constexpr inline bool isMask_32(uint32_t Value) { + return Value && ((Value + 1) & Value) == 0; +} + +/// Return true if the argument is a non-empty sequence of ones starting at the +/// least significant bit with the remainder zero (64 bit version). +constexpr inline bool isMask_64(uint64_t Value) { + return Value && ((Value + 1) & Value) == 0; +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true. +constexpr inline bool isShiftedMask_32(uint32_t Value) { + return Value && isMask_32((Value - 1) | Value); +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (64 bit version.) +constexpr inline bool isShiftedMask_64(uint64_t Value) { + return Value && isMask_64((Value - 1) | Value); +} + +/// Return true if the argument is a power of two > 0. +/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.) +constexpr inline bool isPowerOf2_32(uint32_t Value) { + return llvm::has_single_bit(Value); +} + +/// Return true if the argument is a power of two > 0 (64 bit edition.) +constexpr inline bool isPowerOf2_64(uint64_t Value) { + return llvm::has_single_bit(Value); +} + +/// Count the number of ones from the most significant bit to the first +/// zero bit. +/// +/// Ex. countLeadingOnes(0xFF0FFF00) == 8. +/// Only unsigned integral types are allowed. +/// +/// Returns std::numeric_limits<T>::digits on an input of all ones. +template <typename T> unsigned countLeadingOnes(T Value) { + static_assert(std::is_unsigned_v<T>, + "Only unsigned integral types are allowed."); + return llvm::countl_one<T>(Value); +} + +/// Count the number of ones from the least significant bit to the first +/// zero bit. +/// +/// Ex. countTrailingOnes(0x00FF00FF) == 8. +/// Only unsigned integral types are allowed. +/// +/// Returns std::numeric_limits<T>::digits on an input of all ones. +template <typename T> unsigned countTrailingOnes(T Value) { + static_assert(std::is_unsigned_v<T>, + "Only unsigned integral types are allowed."); + return llvm::countr_one<T>(Value); +} + +/// Count the number of set bits in a value. +/// Ex. countPopulation(0xF000F000) = 8 +/// Returns 0 if the word is zero. +template <typename T> +inline unsigned countPopulation(T Value) { + static_assert(std::is_unsigned_v<T>, + "Only unsigned integral types are allowed."); + return (unsigned)llvm::popcount(Value); +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true. +/// If true, \p MaskIdx will specify the index of the lowest set bit and \p +/// MaskLen is updated to specify the length of the mask, else neither are +/// updated. +inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx, + unsigned &MaskLen) { + if (!isShiftedMask_32(Value)) + return false; + MaskIdx = llvm::countr_zero(Value); + MaskLen = llvm::popcount(Value); + return true; +} + +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index +/// of the lowest set bit and \p MaskLen is updated to specify the length of the +/// mask, else neither are updated. +inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx, + unsigned &MaskLen) { + if (!isShiftedMask_64(Value)) + return false; + MaskIdx = llvm::countr_zero(Value); + MaskLen = llvm::popcount(Value); + return true; +} + +/// Compile time Log2. +/// Valid only for positive powers of two. +template <size_t kValue> constexpr inline size_t CTLog2() { + static_assert(kValue > 0 && llvm::isPowerOf2_64(kValue), + "Value is not a valid power of 2"); + return 1 + CTLog2<kValue / 2>(); +} + +template <> constexpr inline size_t CTLog2<1>() { return 0; } + +/// Return the floor log base 2 of the specified value, -1 if the value is zero. +/// (32 bit edition.) +/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2 +inline unsigned Log2_32(uint32_t Value) { + return 31 - llvm::countl_zero(Value); +} + +/// Return the floor log base 2 of the specified value, -1 if the value is zero. +/// (64 bit edition.) +inline unsigned Log2_64(uint64_t Value) { + return 63 - llvm::countl_zero(Value); +} + +/// Return the ceil log base 2 of the specified value, 32 if the value is zero. +/// (32 bit edition). +/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3 +inline unsigned Log2_32_Ceil(uint32_t Value) { + return 32 - llvm::countl_zero(Value - 1); +} + +/// Return the ceil log base 2 of the specified value, 64 if the value is zero. +/// (64 bit edition.) +inline unsigned Log2_64_Ceil(uint64_t Value) { + return 64 - llvm::countl_zero(Value - 1); +} + +/// This function takes a 64-bit integer and returns the bit equivalent double. +inline double BitsToDouble(uint64_t Bits) { + static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes"); + return llvm::bit_cast<double>(Bits); +} + +/// This function takes a 32-bit integer and returns the bit equivalent float. +inline float BitsToFloat(uint32_t Bits) { + static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes"); + return llvm::bit_cast<float>(Bits); +} + +/// This function takes a double and returns the bit equivalent 64-bit integer. +/// Note that copying doubles around changes the bits of NaNs on some hosts, +/// notably x86, so this routine cannot be used if these bits are needed. +inline uint64_t DoubleToBits(double Double) { + static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes"); + return llvm::bit_cast<uint64_t>(Double); +} + +/// This function takes a float and returns the bit equivalent 32-bit integer. +/// Note that copying floats around changes the bits of NaNs on some hosts, +/// notably x86, so this routine cannot be used if these bits are needed. +inline uint32_t FloatToBits(float Float) { + static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes"); + return llvm::bit_cast<uint32_t>(Float); +} + +/// A and B are either alignments or offsets. Return the minimum alignment that +/// may be assumed after adding the two together. +constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) { + // The largest power of 2 that divides both A and B. + // + // Replace "-Value" by "1+~Value" in the following commented code to avoid + // MSVC warning C4146 + // return (A | B) & -(A | B); + return (A | B) & (1 + ~(A | B)); +} + +/// Returns the next power of two (in 64-bits) that is strictly greater than A. +/// Returns zero on overflow. +constexpr inline uint64_t NextPowerOf2(uint64_t A) { + A |= (A >> 1); + A |= (A >> 2); + A |= (A >> 4); + A |= (A >> 8); + A |= (A >> 16); + A |= (A >> 32); + return A + 1; +} + +/// Returns the power of two which is less than or equal to the given value. +/// Essentially, it is a floor operation across the domain of powers of two. +inline uint64_t PowerOf2Floor(uint64_t A) { + return llvm::bit_floor(A); +} + +/// Returns the power of two which is greater than or equal to the given value. +/// Essentially, it is a ceil operation across the domain of powers of two. +inline uint64_t PowerOf2Ceil(uint64_t A) { + if (!A) + return 0; + return NextPowerOf2(A - 1); +} + +/// Returns the next integer (mod 2**64) that is greater than or equal to +/// \p Value and is a multiple of \p Align. \p Align must be non-zero. +/// +/// Examples: +/// \code +/// alignTo(5, 8) = 8 +/// alignTo(17, 8) = 24 +/// alignTo(~0LL, 8) = 0 +/// alignTo(321, 255) = 510 +/// \endcode +inline uint64_t alignTo(uint64_t Value, uint64_t Align) { + assert(Align != 0u && "Align can't be 0."); + return (Value + Align - 1) / Align * Align; +} + +inline uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) { + assert(Align != 0 && (Align & (Align - 1)) == 0 && + "Align must be a power of 2"); + return (Value + Align - 1) & -Align; +} + +/// If non-zero \p Skew is specified, the return value will be a minimal integer +/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for +/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p +/// Skew mod \p A'. \p Align must be non-zero. +/// +/// Examples: +/// \code +/// alignTo(5, 8, 7) = 7 +/// alignTo(17, 8, 1) = 17 +/// alignTo(~0LL, 8, 3) = 3 +/// alignTo(321, 255, 42) = 552 +/// \endcode +inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew) { + assert(Align != 0u && "Align can't be 0."); + Skew %= Align; + return alignTo(Value - Skew, Align) + Skew; +} + +/// Returns the next integer (mod 2**64) that is greater than or equal to +/// \p Value and is a multiple of \c Align. \c Align must be non-zero. +template <uint64_t Align> constexpr inline uint64_t alignTo(uint64_t Value) { + static_assert(Align != 0u, "Align must be non-zero"); + 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; +} + +/// Returns the integer nearest(Numerator / Denominator). +inline uint64_t divideNearest(uint64_t Numerator, uint64_t Denominator) { + return (Numerator + (Denominator / 2)) / Denominator; +} + +/// Returns the largest uint64_t less than or equal to \p Value and is +/// \p Skew mod \p Align. \p Align must be non-zero +inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) { + assert(Align != 0u && "Align can't be 0."); + Skew %= Align; + return (Value - Skew) / Align * Align + Skew; +} + +/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. +/// Requires 0 < B <= 32. +template <unsigned B> constexpr inline int32_t SignExtend32(uint32_t X) { + static_assert(B > 0, "Bit width can't be 0."); + static_assert(B <= 32, "Bit width out of range."); + return int32_t(X << (32 - B)) >> (32 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. +/// Requires 0 < B <= 32. +inline int32_t SignExtend32(uint32_t X, unsigned B) { + assert(B > 0 && "Bit width can't be 0."); + assert(B <= 32 && "Bit width out of range."); + return int32_t(X << (32 - B)) >> (32 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. +/// Requires 0 < B <= 64. +template <unsigned B> constexpr inline int64_t SignExtend64(uint64_t x) { + static_assert(B > 0, "Bit width can't be 0."); + static_assert(B <= 64, "Bit width out of range."); + return int64_t(x << (64 - B)) >> (64 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. +/// Requires 0 < B <= 64. +inline int64_t SignExtend64(uint64_t X, unsigned B) { + assert(B > 0 && "Bit width can't be 0."); + assert(B <= 64 && "Bit width out of range."); + return int64_t(X << (64 - B)) >> (64 - B); +} + +/// Subtract two unsigned integers, X and Y, of type T and return the absolute +/// value of the result. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, T> AbsoluteDifference(T X, T Y) { + return X > Y ? (X - Y) : (Y - X); +} + +/// Add two unsigned integers, X and Y, of type T. Clamp the result to the +/// maximum representable value of T on overflow. ResultOverflowed indicates if +/// the result is larger than the maximum representable value of type T. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, T> +SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) { + bool Dummy; + bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; + // Hacker's Delight, p. 29 + T Z = X + Y; + Overflowed = (Z < X || Z < Y); + if (Overflowed) + return std::numeric_limits<T>::max(); + else + return Z; +} + +/// Add multiple unsigned integers of type T. Clamp the result to the +/// maximum representable value of T on overflow. +template <class T, class... Ts> +std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z, + Ts... Args) { + bool Overflowed = false; + T XY = SaturatingAdd(X, Y, &Overflowed); + if (Overflowed) + return SaturatingAdd(std::numeric_limits<T>::max(), T(1), Args...); + return SaturatingAdd(XY, Z, Args...); +} + +/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the +/// maximum representable value of T on overflow. ResultOverflowed indicates if +/// the result is larger than the maximum representable value of type T. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, T> +SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { + bool Dummy; + bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; + + // Hacker's Delight, p. 30 has a different algorithm, but we don't use that + // because it fails for uint16_t (where multiplication can have undefined + // behavior due to promotion to int), and requires a division in addition + // to the multiplication. + + Overflowed = false; + + // Log2(Z) would be either Log2Z or Log2Z + 1. + // Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z + // will necessarily be less than Log2Max as desired. + int Log2Z = Log2_64(X) + Log2_64(Y); + const T Max = std::numeric_limits<T>::max(); + int Log2Max = Log2_64(Max); + if (Log2Z < Log2Max) { + return X * Y; + } + if (Log2Z > Log2Max) { + Overflowed = true; + return Max; + } + + // We're going to use the top bit, and maybe overflow one + // bit past it. Multiply all but the bottom bit then add + // that on at the end. + T Z = (X >> 1) * Y; + if (Z & ~(Max >> 1)) { + Overflowed = true; + return Max; + } + Z <<= 1; + if (X & 1) + return SaturatingAdd(Z, Y, ResultOverflowed); + + return Z; +} + +/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to +/// the product. Clamp the result to the maximum representable value of T on +/// overflow. ResultOverflowed indicates if the result is larger than the +/// maximum representable value of type T. +template <typename T> +std::enable_if_t<std::is_unsigned<T>::value, T> +SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) { + bool Dummy; + bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; + + T Product = SaturatingMultiply(X, Y, &Overflowed); + if (Overflowed) + return Product; + + return SaturatingAdd(A, Product, &Overflowed); +} + +/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC. +extern const float huge_valf; + + +/// Add two signed integers, computing the two's complement truncated result, +/// returning true if overflow occurred. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, T> AddOverflow(T X, T Y, T &Result) { +#if __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(X, Y, &Result); +#else + // Perform the unsigned addition. + using U = std::make_unsigned_t<T>; + const U UX = static_cast<U>(X); + const U UY = static_cast<U>(Y); + const U UResult = UX + UY; + + // Convert to signed. + Result = static_cast<T>(UResult); + + // Adding two positive numbers should result in a positive number. + if (X > 0 && Y > 0) + return Result <= 0; + // Adding two negatives should result in a negative number. + if (X < 0 && Y < 0) + return Result >= 0; + return false; +#endif +} + +/// Subtract two signed integers, computing the two's complement truncated +/// result, returning true if an overflow ocurred. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, T> SubOverflow(T X, T Y, T &Result) { +#if __has_builtin(__builtin_sub_overflow) + return __builtin_sub_overflow(X, Y, &Result); +#else + // Perform the unsigned addition. + using U = std::make_unsigned_t<T>; + const U UX = static_cast<U>(X); + const U UY = static_cast<U>(Y); + const U UResult = UX - UY; + + // Convert to signed. + Result = static_cast<T>(UResult); + + // Subtracting a positive number from a negative results in a negative number. + if (X <= 0 && Y > 0) + return Result >= 0; + // Subtracting a negative number from a positive results in a positive number. + if (X >= 0 && Y < 0) + return Result <= 0; + return false; +#endif +} + +/// Multiply two signed integers, computing the two's complement truncated +/// result, returning true if an overflow ocurred. +template <typename T> +std::enable_if_t<std::is_signed<T>::value, T> MulOverflow(T X, T Y, T &Result) { + // Perform the unsigned multiplication on absolute values. + using U = std::make_unsigned_t<T>; + const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X); + const U UY = Y < 0 ? (0 - static_cast<U>(Y)) : static_cast<U>(Y); + const U UResult = UX * UY; + + // Convert to signed. + const bool IsNegative = (X < 0) ^ (Y < 0); + Result = IsNegative ? (0 - UResult) : UResult; + + // If any of the args was 0, result is 0 and no overflow occurs. + if (UX == 0 || UY == 0) + return false; + + // UX and UY are in [1, 2^n], where n is the number of digits. + // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for + // positive) divided by an argument compares to the other. + if (IsNegative) + return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY; + else + return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY; +} + +} // End llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MemAlloc.h b/contrib/libs/llvm16/include/llvm/Support/MemAlloc.h new file mode 100644 index 00000000000..b39554baf8b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MemAlloc.h @@ -0,0 +1,98 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- MemAlloc.h - Memory allocation functions -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines counterparts of C library allocation functions defined in +/// the namespace 'std'. The new allocation functions crash on allocation +/// failure instead of returning null pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MEMALLOC_H +#define LLVM_SUPPORT_MEMALLOC_H + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstdlib> + +namespace llvm { + +LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) { + void *Result = std::malloc(Sz); + if (Result == nullptr) { + // It is implementation-defined whether allocation occurs if the space + // requested is zero (ISO/IEC 9899:2018 7.22.3). Retry, requesting + // non-zero, if the space requested was zero. + if (Sz == 0) + return safe_malloc(1); + report_bad_alloc_error("Allocation failed"); + } + return Result; +} + +LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_calloc(size_t Count, + size_t Sz) { + void *Result = std::calloc(Count, Sz); + if (Result == nullptr) { + // It is implementation-defined whether allocation occurs if the space + // requested is zero (ISO/IEC 9899:2018 7.22.3). Retry, requesting + // non-zero, if the space requested was zero. + if (Count == 0 || Sz == 0) + return safe_malloc(1); + report_bad_alloc_error("Allocation failed"); + } + return Result; +} + +LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_realloc(void *Ptr, size_t Sz) { + void *Result = std::realloc(Ptr, Sz); + if (Result == nullptr) { + // It is implementation-defined whether allocation occurs if the space + // requested is zero (ISO/IEC 9899:2018 7.22.3). Retry, requesting + // non-zero, if the space requested was zero. + if (Sz == 0) + return safe_malloc(1); + report_bad_alloc_error("Allocation failed"); + } + return Result; +} + +/// Allocate a buffer of memory with the given size and alignment. +/// +/// When the compiler supports aligned operator new, this will use it to to +/// handle even over-aligned allocations. +/// +/// However, this doesn't make any attempt to leverage the fancier techniques +/// like posix_memalign due to portability. It is mostly intended to allow +/// compatibility with platforms that, after aligned allocation was added, use +/// reduced default alignment. +LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void * +allocate_buffer(size_t Size, size_t Alignment); + +/// Deallocate a buffer of memory with the given size and alignment. +/// +/// If supported, this will used the sized delete operator. Also if supported, +/// this will pass the alignment to the delete operator. +/// +/// The pointer must have been allocated with the corresponding new operator, +/// most likely using the above helper. +void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment); + +} // namespace llvm +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Memory.h b/contrib/libs/llvm16/include/llvm/Support/Memory.h new file mode 100644 index 00000000000..3556eeed6f7 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Memory.h @@ -0,0 +1,192 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Memory.h - Memory Support -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::Memory class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MEMORY_H +#define LLVM_SUPPORT_MEMORY_H + +#include "llvm/Support/DataTypes.h" +#include <system_error> + +namespace llvm { + +// Forward declare raw_ostream: it is used for debug dumping below. +class raw_ostream; + +namespace sys { + + /// This class encapsulates the notion of a memory block which has an address + /// and a size. It is used by the Memory class (a friend) as the result of + /// various memory allocation operations. + /// @see Memory + /// Memory block abstraction. + class MemoryBlock { + public: + MemoryBlock() : Address(nullptr), AllocatedSize(0) {} + MemoryBlock(void *addr, size_t allocatedSize) + : Address(addr), AllocatedSize(allocatedSize) {} + void *base() const { return Address; } + /// The size as it was allocated. This is always greater or equal to the + /// size that was originally requested. + size_t allocatedSize() const { return AllocatedSize; } + + private: + void *Address; ///< Address of first byte of memory area + size_t AllocatedSize; ///< Size, in bytes of the memory area + unsigned Flags = 0; + friend class Memory; + }; + + /// This class provides various memory handling functions that manipulate + /// MemoryBlock instances. + /// @since 1.4 + /// An abstraction for memory operations. + class Memory { + public: + enum ProtectionFlags { + MF_READ = 0x1000000, + MF_WRITE = 0x2000000, + MF_EXEC = 0x4000000, + MF_RWE_MASK = 0x7000000, + + /// The \p MF_HUGE_HINT flag is used to indicate that the request for + /// a memory block should be satisfied with large pages if possible. + /// This is only a hint and small pages will be used as fallback. + /// + /// The presence or absence of this flag in the returned memory block + /// is (at least currently) *not* a reliable indicator that the memory + /// block will use or will not use large pages. On some systems a request + /// without this flag can be backed by large pages without this flag being + /// set, and on some other systems a request with this flag can fallback + /// to small pages without this flag being cleared. + MF_HUGE_HINT = 0x0000001 + }; + + /// This method allocates a block of memory that is suitable for loading + /// 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. + /// The actual allocated address is not guaranteed to be near the requested + /// address. + /// \p Flags is used to set the initial protection flags for the block + /// of the memory. + /// \p EC [out] returns an object describing any error that occurs. + /// + /// This method may allocate more than the number of bytes requested. The + /// actual number of bytes allocated is indicated in the returned + /// MemoryBlock. + /// + /// The start of the allocated block must be aligned with the + /// system allocation granularity (64K on Windows, page size on Linux). + /// If the address following \p NearBlock is not so aligned, it will be + /// rounded up to the next allocation granularity boundary. + /// + /// \r a non-null MemoryBlock if the function was successful, + /// otherwise a null MemoryBlock is with \p EC describing the error. + /// + /// Allocate mapped memory. + static MemoryBlock allocateMappedMemory(size_t NumBytes, + const MemoryBlock *const NearBlock, + unsigned Flags, + std::error_code &EC); + + /// This method releases a block of memory that was allocated with the + /// allocateMappedMemory method. It should not be used to release any + /// memory block allocated any other way. + /// \p Block describes the memory to be released. + /// + /// \r error_success if the function was successful, or an error_code + /// describing the failure if an error occurred. + /// + /// Release mapped memory. + static std::error_code releaseMappedMemory(MemoryBlock &Block); + + /// This method sets the protection flags for a block of memory to the + /// state specified by /p Flags. The behavior is not specified if the + /// memory was not allocated using the allocateMappedMemory method. + /// \p Block describes the memory block to be protected. + /// \p Flags specifies the new protection state to be assigned to the block. + /// + /// If \p Flags is MF_WRITE, the actual behavior varies + /// with the operating system (i.e. MF_READ | MF_WRITE on Windows) and the + /// target architecture (i.e. MF_WRITE -> MF_READ | MF_WRITE on i386). + /// + /// \r error_success if the function was successful, or an error_code + /// describing the failure if an error occurred. + /// + /// Set memory protection state. + static std::error_code protectMappedMemory(const MemoryBlock &Block, + unsigned Flags); + + /// 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); + }; + + /// Owning version of MemoryBlock. + class OwningMemoryBlock { + public: + OwningMemoryBlock() = default; + explicit OwningMemoryBlock(MemoryBlock M) : M(M) {} + OwningMemoryBlock(OwningMemoryBlock &&Other) { + M = Other.M; + Other.M = MemoryBlock(); + } + OwningMemoryBlock& operator=(OwningMemoryBlock &&Other) { + M = Other.M; + Other.M = MemoryBlock(); + return *this; + } + ~OwningMemoryBlock() { + if (M.base()) + Memory::releaseMappedMemory(M); + } + void *base() const { return M.base(); } + /// The size as it was allocated. This is always greater or equal to the + /// size that was originally requested. + size_t allocatedSize() const { return M.allocatedSize(); } + MemoryBlock getMemoryBlock() const { return M; } + std::error_code release() { + std::error_code EC; + if (M.base()) { + EC = Memory::releaseMappedMemory(M); + M = MemoryBlock(); + } + return EC; + } + private: + MemoryBlock M; + }; + +#ifndef NDEBUG + /// Debugging output for Memory::ProtectionFlags. + raw_ostream &operator<<(raw_ostream &OS, const Memory::ProtectionFlags &PF); + + /// Debugging output for MemoryBlock. + raw_ostream &operator<<(raw_ostream &OS, const MemoryBlock &MB); +#endif // ifndef NDEBUG + } // end namespace sys + } // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MemoryBuffer.h b/contrib/libs/llvm16/include/llvm/Support/MemoryBuffer.h new file mode 100644 index 00000000000..7b6a4fe8ed6 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MemoryBuffer.h @@ -0,0 +1,302 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- MemoryBuffer.h - Memory Buffer Interface ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the MemoryBuffer interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MEMORYBUFFER_H +#define LLVM_SUPPORT_MEMORYBUFFER_H + +#include "llvm-c/Types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBufferRef.h" +#include <cstddef> +#include <cstdint> +#include <memory> + +namespace llvm { +namespace sys { +namespace fs { +// Duplicated from FileSystem.h to avoid a dependency. +#if defined(_WIN32) +// A Win32 HANDLE is a typedef of void* +using file_t = void *; +#else +using file_t = int; +#endif +} // namespace fs +} // namespace sys + +/// This interface provides simple read-only access to a block of memory, and +/// provides simple methods for reading files and standard input into a memory +/// buffer. In addition to basic access to the characters in the file, this +/// interface guarantees you can read one character past the end of the file, +/// and that this character will read as '\0'. +/// +/// The '\0' guarantee is needed to support an optimization -- it's intended to +/// be more efficient for clients which are reading all the data to stop +/// reading when they encounter a '\0' than to continually check the file +/// position to see if it has reached the end of the file. +class MemoryBuffer { + const char *BufferStart; // Start of the buffer. + const char *BufferEnd; // End of the buffer. + +protected: + MemoryBuffer() = default; + + void init(const char *BufStart, const char *BufEnd, + bool RequiresNullTerminator); + +public: + MemoryBuffer(const MemoryBuffer &) = delete; + MemoryBuffer &operator=(const MemoryBuffer &) = delete; + virtual ~MemoryBuffer(); + + const char *getBufferStart() const { return BufferStart; } + const char *getBufferEnd() const { return BufferEnd; } + size_t getBufferSize() const { return BufferEnd-BufferStart; } + + StringRef getBuffer() const { + return StringRef(BufferStart, getBufferSize()); + } + + /// Return an identifier for this buffer, typically the filename it was read + /// from. + virtual StringRef getBufferIdentifier() const { return "Unknown buffer"; } + + /// For read-only MemoryBuffer_MMap, mark the buffer as unused in the near + /// future and the kernel can free resources associated with it. Further + /// access is supported but may be expensive. This calls + /// madvise(MADV_DONTNEED) on read-only file mappings on *NIX systems. This + /// function should not be called on a writable buffer. + virtual void dontNeedIfMmap() {} + + /// Open the specified file as a MemoryBuffer, returning a new MemoryBuffer + /// if successful, otherwise returning null. + /// + /// \param IsText Set to true to indicate that the file should be read in + /// text mode. + /// + /// \param IsVolatile Set to true to indicate that the contents of the file + /// can change outside the user's control, e.g. when libclang tries to parse + /// while the user is editing/updating the file or if the file is on an NFS. + /// + /// \param Alignment Set to indicate that the buffer should be aligned to at + /// least the specified alignment. + static ErrorOr<std::unique_ptr<MemoryBuffer>> + getFile(const Twine &Filename, bool IsText = false, + bool RequiresNullTerminator = true, bool IsVolatile = false, + std::optional<Align> Alignment = std::nullopt); + + /// Read all of the specified file into a MemoryBuffer as a stream + /// (i.e. until EOF reached). This is useful for special files that + /// look like a regular file but have 0 size (e.g. /proc/cpuinfo on Linux). + static ErrorOr<std::unique_ptr<MemoryBuffer>> + getFileAsStream(const Twine &Filename); + + /// Given an already-open file descriptor, map some slice of it into a + /// MemoryBuffer. The slice is specified by an \p Offset and \p MapSize. + /// Since this is in the middle of a file, the buffer is not null terminated. + static ErrorOr<std::unique_ptr<MemoryBuffer>> + getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize, + int64_t Offset, bool IsVolatile = false, + std::optional<Align> Alignment = std::nullopt); + + /// Given an already-open file descriptor, read the file and return a + /// MemoryBuffer. + /// + /// \param IsVolatile Set to true to indicate that the contents of the file + /// can change outside the user's control, e.g. when libclang tries to parse + /// while the user is editing/updating the file or if the file is on an NFS. + /// + /// \param Alignment Set to indicate that the buffer should be aligned to at + /// least the specified alignment. + static ErrorOr<std::unique_ptr<MemoryBuffer>> + getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, + bool RequiresNullTerminator = true, bool IsVolatile = false, + std::optional<Align> Alignment = std::nullopt); + + /// Open the specified memory range as a MemoryBuffer. Note that InputData + /// must be null terminated if RequiresNullTerminator is true. + static std::unique_ptr<MemoryBuffer> + getMemBuffer(StringRef InputData, StringRef BufferName = "", + bool RequiresNullTerminator = true); + + static std::unique_ptr<MemoryBuffer> + getMemBuffer(MemoryBufferRef Ref, bool RequiresNullTerminator = true); + + /// Open the specified memory range as a MemoryBuffer, copying the contents + /// and taking ownership of it. InputData does not have to be null terminated. + static std::unique_ptr<MemoryBuffer> + getMemBufferCopy(StringRef InputData, const Twine &BufferName = ""); + + /// Read all of stdin into a file buffer, and return it. + static ErrorOr<std::unique_ptr<MemoryBuffer>> getSTDIN(); + + /// Open the specified file as a MemoryBuffer, or open stdin if the Filename + /// is "-". + static ErrorOr<std::unique_ptr<MemoryBuffer>> + getFileOrSTDIN(const Twine &Filename, bool IsText = false, + bool RequiresNullTerminator = true, + std::optional<Align> Alignment = std::nullopt); + + /// 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, + std::optional<Align> Alignment = std::nullopt); + + //===--------------------------------------------------------------------===// + // Provided for performance analysis. + //===--------------------------------------------------------------------===// + + /// The kind of memory backing used to support the MemoryBuffer. + enum BufferKind { + MemoryBuffer_Malloc, + MemoryBuffer_MMap + }; + + /// Return information on the memory mechanism used to support the + /// MemoryBuffer. + virtual BufferKind getBufferKind() const = 0; + + MemoryBufferRef getMemBufferRef() const; +}; + +/// This class is an extension of MemoryBuffer, which allows copy-on-write +/// access to the underlying contents. It only supports creation methods that +/// are guaranteed to produce a writable buffer. For example, mapping a file +/// read-only is not supported. +class WritableMemoryBuffer : public MemoryBuffer { +protected: + WritableMemoryBuffer() = default; + +public: + using MemoryBuffer::getBuffer; + using MemoryBuffer::getBufferEnd; + using MemoryBuffer::getBufferStart; + + // const_cast is well-defined here, because the underlying buffer is + // guaranteed to have been initialized with a mutable buffer. + char *getBufferStart() { + return const_cast<char *>(MemoryBuffer::getBufferStart()); + } + char *getBufferEnd() { + return const_cast<char *>(MemoryBuffer::getBufferEnd()); + } + MutableArrayRef<char> getBuffer() { + return {getBufferStart(), getBufferEnd()}; + } + + static ErrorOr<std::unique_ptr<WritableMemoryBuffer>> + getFile(const Twine &Filename, bool IsVolatile = false, + std::optional<Align> Alignment = std::nullopt); + + /// Map a subrange of the specified file as a WritableMemoryBuffer. + static ErrorOr<std::unique_ptr<WritableMemoryBuffer>> + getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, + bool IsVolatile = false, + std::optional<Align> Alignment = std::nullopt); + + /// Allocate a new MemoryBuffer of the specified size that is not initialized. + /// Note that the caller should initialize the memory allocated by this + /// method. The memory is owned by the MemoryBuffer object. + /// + /// \param Alignment Set to indicate that the buffer should be aligned to at + /// least the specified alignment. + static std::unique_ptr<WritableMemoryBuffer> + getNewUninitMemBuffer(size_t Size, const Twine &BufferName = "", + std::optional<Align> Alignment = std::nullopt); + + /// Allocate a new zero-initialized MemoryBuffer of the specified size. Note + /// that the caller need not initialize the memory allocated by this method. + /// The memory is owned by the MemoryBuffer object. + static std::unique_ptr<WritableMemoryBuffer> + getNewMemBuffer(size_t Size, const Twine &BufferName = ""); + +private: + // Hide these base class factory function so one can't write + // WritableMemoryBuffer::getXXX() + // and be surprised that he got a read-only Buffer. + using MemoryBuffer::getFileAsStream; + using MemoryBuffer::getFileOrSTDIN; + using MemoryBuffer::getMemBuffer; + using MemoryBuffer::getMemBufferCopy; + using MemoryBuffer::getOpenFile; + using MemoryBuffer::getOpenFileSlice; + using MemoryBuffer::getSTDIN; +}; + +/// This class is an extension of MemoryBuffer, which allows write access to +/// the underlying contents and committing those changes to the original source. +/// It only supports creation methods that are guaranteed to produce a writable +/// buffer. For example, mapping a file read-only is not supported. +class WriteThroughMemoryBuffer : public MemoryBuffer { +protected: + WriteThroughMemoryBuffer() = default; + +public: + using MemoryBuffer::getBuffer; + using MemoryBuffer::getBufferEnd; + using MemoryBuffer::getBufferStart; + + // const_cast is well-defined here, because the underlying buffer is + // guaranteed to have been initialized with a mutable buffer. + char *getBufferStart() { + return const_cast<char *>(MemoryBuffer::getBufferStart()); + } + char *getBufferEnd() { + return const_cast<char *>(MemoryBuffer::getBufferEnd()); + } + MutableArrayRef<char> getBuffer() { + return {getBufferStart(), getBufferEnd()}; + } + + static ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>> + getFile(const Twine &Filename, int64_t FileSize = -1); + + /// Map a subrange of the specified file as a ReadWriteMemoryBuffer. + static ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>> + getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset); + +private: + // Hide these base class factory function so one can't write + // WritableMemoryBuffer::getXXX() + // and be surprised that he got a read-only Buffer. + using MemoryBuffer::getFileAsStream; + using MemoryBuffer::getFileOrSTDIN; + using MemoryBuffer::getMemBuffer; + using MemoryBuffer::getMemBufferCopy; + using MemoryBuffer::getOpenFile; + using MemoryBuffer::getOpenFileSlice; + using MemoryBuffer::getSTDIN; +}; + +// Create wrappers for C Binding types (see CBindingWrapping.h). +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(MemoryBuffer, LLVMMemoryBufferRef) + +} // end namespace llvm + +#endif // LLVM_SUPPORT_MEMORYBUFFER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/MemoryBufferRef.h b/contrib/libs/llvm16/include/llvm/Support/MemoryBufferRef.h new file mode 100644 index 00000000000..cc387b7fb7f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/MemoryBufferRef.h @@ -0,0 +1,67 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- MemoryBufferRef.h - Memory Buffer Reference --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the MemoryBuffer interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MEMORYBUFFERREF_H +#define LLVM_SUPPORT_MEMORYBUFFERREF_H + +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +class MemoryBuffer; + +class MemoryBufferRef { + StringRef Buffer; + StringRef Identifier; + +public: + MemoryBufferRef() = default; + MemoryBufferRef(const MemoryBuffer &Buffer); + MemoryBufferRef(StringRef Buffer, StringRef Identifier) + : Buffer(Buffer), Identifier(Identifier) {} + + StringRef getBuffer() const { return Buffer; } + StringRef getBufferIdentifier() const { return Identifier; } + + const char *getBufferStart() const { return Buffer.begin(); } + const char *getBufferEnd() const { return Buffer.end(); } + size_t getBufferSize() const { return Buffer.size(); } + + /// Check pointer identity (not value) of identifier and data. + friend bool operator==(const MemoryBufferRef &LHS, + const MemoryBufferRef &RHS) { + return LHS.Buffer.begin() == RHS.Buffer.begin() && + LHS.Buffer.end() == RHS.Buffer.end() && + LHS.Identifier.begin() == RHS.Identifier.begin() && + LHS.Identifier.end() == RHS.Identifier.end(); + } + + friend bool operator!=(const MemoryBufferRef &LHS, + const MemoryBufferRef &RHS) { + return !(LHS == RHS); + } +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_MEMORYBUFFERREF_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ModRef.h b/contrib/libs/llvm16/include/llvm/Support/ModRef.h new file mode 100644 index 00000000000..0cb74cb4caf --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ModRef.h @@ -0,0 +1,272 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- ModRef.h - Memory effect modelling ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Definitions of ModRefInfo and MemoryEffects, which are used to +// describe the memory effects of instructions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_MODREF_H +#define LLVM_IR_MODREF_H + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// Flags indicating whether a memory access modifies or references memory. +/// +/// This is no access at all, a modification, a reference, or both +/// a modification and a reference. +enum class ModRefInfo : uint8_t { + /// The access neither references nor modifies the value stored in memory. + NoModRef = 0, + /// The access may reference the value stored in memory. + Ref = 1, + /// The access may modify the value stored in memory. + Mod = 2, + /// The access may reference and may modify the value stored in memory. + ModRef = Ref | Mod, + LLVM_MARK_AS_BITMASK_ENUM(ModRef), +}; + +[[nodiscard]] inline bool isNoModRef(const ModRefInfo MRI) { + return MRI == ModRefInfo::NoModRef; +} +[[nodiscard]] inline bool isModOrRefSet(const ModRefInfo MRI) { + return MRI != ModRefInfo::NoModRef; +} +[[nodiscard]] inline bool isModAndRefSet(const ModRefInfo MRI) { + return MRI == ModRefInfo::ModRef; +} +[[nodiscard]] inline bool isModSet(const ModRefInfo MRI) { + return static_cast<int>(MRI) & static_cast<int>(ModRefInfo::Mod); +} +[[nodiscard]] inline bool isRefSet(const ModRefInfo MRI) { + return static_cast<int>(MRI) & static_cast<int>(ModRefInfo::Ref); +} + +/// Debug print ModRefInfo. +raw_ostream &operator<<(raw_ostream &OS, ModRefInfo MR); + +/// Summary of how a function affects memory in the program. +/// +/// Loads from constant globals are not considered memory accesses for this +/// interface. Also, functions may freely modify stack space local to their +/// invocation without having to report it through these interfaces. +class MemoryEffects { +public: + /// The locations at which a function might access memory. + enum Location { + /// Access to memory via argument pointers. + ArgMem = 0, + /// Memory that is inaccessible via LLVM IR. + InaccessibleMem = 1, + /// Any other memory. + Other = 2, + }; + +private: + uint32_t Data = 0; + + static constexpr uint32_t BitsPerLoc = 2; + static constexpr uint32_t LocMask = (1 << BitsPerLoc) - 1; + + static uint32_t getLocationPos(Location Loc) { + return (uint32_t)Loc * BitsPerLoc; + } + + MemoryEffects(uint32_t Data) : Data(Data) {} + + void setModRef(Location Loc, ModRefInfo MR) { + Data &= ~(LocMask << getLocationPos(Loc)); + Data |= static_cast<uint32_t>(MR) << getLocationPos(Loc); + } + + friend raw_ostream &operator<<(raw_ostream &OS, MemoryEffects RMRB); + +public: + /// Returns iterator over all supported location kinds. + static auto locations() { + return enum_seq_inclusive(Location::ArgMem, Location::Other, + force_iteration_on_noniterable_enum); + } + + /// Create MemoryEffects that can access only the given location with the + /// given ModRefInfo. + MemoryEffects(Location Loc, ModRefInfo MR) { setModRef(Loc, MR); } + + /// Create MemoryEffects that can access any location with the given + /// ModRefInfo. + explicit MemoryEffects(ModRefInfo MR) { + for (Location Loc : locations()) + setModRef(Loc, MR); + } + + /// Create MemoryEffects that can read and write any memory. + static MemoryEffects unknown() { + return MemoryEffects(ModRefInfo::ModRef); + } + + /// Create MemoryEffects that cannot read or write any memory. + static MemoryEffects none() { + return MemoryEffects(ModRefInfo::NoModRef); + } + + /// Create MemoryEffects that can read any memory. + static MemoryEffects readOnly() { + return MemoryEffects(ModRefInfo::Ref); + } + + /// Create MemoryEffects that can write any memory. + static MemoryEffects writeOnly() { + return MemoryEffects(ModRefInfo::Mod); + } + + /// Create MemoryEffects that can only access argument memory. + static MemoryEffects argMemOnly(ModRefInfo MR = ModRefInfo::ModRef) { + return MemoryEffects(ArgMem, MR); + } + + /// Create MemoryEffects that can only access inaccessible memory. + static MemoryEffects inaccessibleMemOnly(ModRefInfo MR = ModRefInfo::ModRef) { + return MemoryEffects(InaccessibleMem, MR); + } + + /// Create MemoryEffects that can only access inaccessible or argument memory. + static MemoryEffects + inaccessibleOrArgMemOnly(ModRefInfo MR = ModRefInfo::ModRef) { + MemoryEffects FRMB = none(); + FRMB.setModRef(ArgMem, MR); + FRMB.setModRef(InaccessibleMem, MR); + return FRMB; + } + + /// Create MemoryEffects from an encoded integer value (used by memory + /// attribute). + static MemoryEffects createFromIntValue(uint32_t Data) { + return MemoryEffects(Data); + } + + /// Convert MemoryEffects into an encoded integer value (used by memory + /// attribute). + uint32_t toIntValue() const { + return Data; + } + + /// Get ModRefInfo for the given Location. + ModRefInfo getModRef(Location Loc) const { + return ModRefInfo((Data >> getLocationPos(Loc)) & LocMask); + } + + /// Get new MemoryEffects with modified ModRefInfo for Loc. + MemoryEffects getWithModRef(Location Loc, ModRefInfo MR) const { + MemoryEffects ME = *this; + ME.setModRef(Loc, MR); + return ME; + } + + /// Get new MemoryEffects with NoModRef on the given Loc. + MemoryEffects getWithoutLoc(Location Loc) const { + MemoryEffects ME = *this; + ME.setModRef(Loc, ModRefInfo::NoModRef); + return ME; + } + + /// Get ModRefInfo for any location. + ModRefInfo getModRef() const { + ModRefInfo MR = ModRefInfo::NoModRef; + for (Location Loc : locations()) + MR |= getModRef(Loc); + return MR; + } + + /// Whether this function accesses no memory. + bool doesNotAccessMemory() const { return Data == 0; } + + /// Whether this function only (at most) reads memory. + bool onlyReadsMemory() const { return !isModSet(getModRef()); } + + /// Whether this function only (at most) writes memory. + bool onlyWritesMemory() const { return !isRefSet(getModRef()); } + + /// Whether this function only (at most) accesses argument memory. + bool onlyAccessesArgPointees() const { + return getWithoutLoc(ArgMem).doesNotAccessMemory(); + } + + /// Whether this function may access argument memory. + bool doesAccessArgPointees() const { + return isModOrRefSet(getModRef(ArgMem)); + } + + /// Whether this function only (at most) accesses inaccessible memory. + bool onlyAccessesInaccessibleMem() const { + return getWithoutLoc(InaccessibleMem).doesNotAccessMemory(); + } + + /// Whether this function only (at most) accesses argument and inaccessible + /// memory. + bool onlyAccessesInaccessibleOrArgMem() const { + return isNoModRef(getModRef(Other)); + } + + /// Intersect with other MemoryEffects. + MemoryEffects operator&(MemoryEffects Other) const { + return MemoryEffects(Data & Other.Data); + } + + /// Intersect (in-place) with other MemoryEffects. + MemoryEffects &operator&=(MemoryEffects Other) { + Data &= Other.Data; + return *this; + } + + /// Union with other MemoryEffects. + MemoryEffects operator|(MemoryEffects Other) const { + return MemoryEffects(Data | Other.Data); + } + + /// Union (in-place) with other MemoryEffects. + MemoryEffects &operator|=(MemoryEffects Other) { + Data |= Other.Data; + return *this; + } + + /// Check whether this is the same as other MemoryEffects. + bool operator==(MemoryEffects Other) const { + return Data == Other.Data; + } + + /// Check whether this is different from other MemoryEffects. + bool operator!=(MemoryEffects Other) const { + return !operator==(Other); + } +}; + +/// Debug print MemoryEffects. +raw_ostream &operator<<(raw_ostream &OS, MemoryEffects RMRB); + +// Legacy alias. +using FunctionModRefBehavior = MemoryEffects; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Mutex.h b/contrib/libs/llvm16/include/llvm/Support/Mutex.h new file mode 100644 index 00000000000..bda13ed60b3 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Mutex.h @@ -0,0 +1,86 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Mutex.h - Mutex Operating System Concept -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::Mutex class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MUTEX_H +#define LLVM_SUPPORT_MUTEX_H + +#include "llvm/Support/Threading.h" +#include <cassert> +#include <mutex> + +namespace llvm +{ + namespace sys + { + /// SmartMutex - A mutex with a compile time constant parameter that + /// indicates whether this mutex should become a no-op when we're not + /// running in multithreaded mode. + template<bool mt_only> + class SmartMutex { + std::recursive_mutex impl; + unsigned acquired = 0; + + public: + bool lock() { + if (!mt_only || llvm_is_multithreaded()) { + impl.lock(); + return true; + } + // Single-threaded debugging code. This would be racy in + // multithreaded mode, but provides not basic checks in single + // threaded mode. + ++acquired; + return true; + } + + bool unlock() { + if (!mt_only || llvm_is_multithreaded()) { + impl.unlock(); + return true; + } + // Single-threaded debugging code. This would be racy in + // multithreaded mode, but provides not basic checks in single + // threaded mode. + assert(acquired && "Lock not acquired before release!"); + --acquired; + return true; + } + + bool try_lock() { + if (!mt_only || llvm_is_multithreaded()) + return impl.try_lock(); + return true; + } + }; + + /// Mutex - A standard, always enforced mutex. + typedef SmartMutex<false> Mutex; + + template <bool mt_only> + using SmartScopedLock = std::lock_guard<SmartMutex<mt_only>>; + + typedef SmartScopedLock<false> ScopedLock; + } +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/NativeFormatting.h b/contrib/libs/llvm16/include/llvm/Support/NativeFormatting.h new file mode 100644 index 00000000000..62f2d0c517a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/NativeFormatting.h @@ -0,0 +1,58 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- NativeFormatting.h - Low level formatting helpers ---------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_NATIVEFORMATTING_H +#define LLVM_SUPPORT_NATIVEFORMATTING_H + +#include <cstdint> +#include <optional> + +namespace llvm { +class raw_ostream; +enum class FloatStyle { Exponent, ExponentUpper, Fixed, Percent }; +enum class IntegerStyle { + Integer, + Number, +}; +enum class HexPrintStyle { Upper, Lower, PrefixUpper, PrefixLower }; + +size_t getDefaultPrecision(FloatStyle Style); + +bool isPrefixedHexStyle(HexPrintStyle S); + +void write_integer(raw_ostream &S, unsigned int N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, int N, size_t MinDigits, IntegerStyle Style); +void write_integer(raw_ostream &S, unsigned long N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, long N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, long long N, size_t MinDigits, + IntegerStyle Style); + +void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, + std::optional<size_t> Width = std::nullopt); +void write_double(raw_ostream &S, double D, FloatStyle Style, + std::optional<size_t> Precision = std::nullopt); +} + +#endif + + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/OnDiskHashTable.h b/contrib/libs/llvm16/include/llvm/Support/OnDiskHashTable.h new file mode 100644 index 00000000000..e3f98064244 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/OnDiskHashTable.h @@ -0,0 +1,626 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- OnDiskHashTable.h - On-Disk Hash Table Implementation --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines facilities for reading and writing on-disk hash tables. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_ONDISKHASHTABLE_H +#define LLVM_SUPPORT_ONDISKHASHTABLE_H + +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstdlib> + +namespace llvm { + +/// Generates an on disk hash table. +/// +/// This needs an \c Info that handles storing values into the hash table's +/// payload and computes the hash for a given key. This should provide the +/// following interface: +/// +/// \code +/// class ExampleInfo { +/// public: +/// typedef ExampleKey key_type; // Must be copy constructible +/// typedef ExampleKey &key_type_ref; +/// typedef ExampleData data_type; // Must be copy constructible +/// typedef ExampleData &data_type_ref; +/// typedef uint32_t hash_value_type; // The type the hash function returns. +/// typedef uint32_t offset_type; // The type for offsets into the table. +/// +/// /// Calculate the hash for Key +/// static hash_value_type ComputeHash(key_type_ref Key); +/// /// Return the lengths, in bytes, of the given Key/Data pair. +/// static std::pair<offset_type, offset_type> +/// EmitKeyDataLength(raw_ostream &Out, key_type_ref Key, data_type_ref Data); +/// /// Write Key to Out. KeyLen is the length from EmitKeyDataLength. +/// static void EmitKey(raw_ostream &Out, key_type_ref Key, +/// offset_type KeyLen); +/// /// Write Data to Out. DataLen is the length from EmitKeyDataLength. +/// static void EmitData(raw_ostream &Out, key_type_ref Key, +/// data_type_ref Data, offset_type DataLen); +/// /// Determine if two keys are equal. Optional, only needed by contains. +/// static bool EqualKey(key_type_ref Key1, key_type_ref Key2); +/// }; +/// \endcode +template <typename Info> class OnDiskChainedHashTableGenerator { + /// A single item in the hash table. + class Item { + public: + typename Info::key_type Key; + typename Info::data_type Data; + Item *Next; + const typename Info::hash_value_type Hash; + + Item(typename Info::key_type_ref Key, typename Info::data_type_ref Data, + Info &InfoObj) + : Key(Key), Data(Data), Next(nullptr), Hash(InfoObj.ComputeHash(Key)) {} + }; + + typedef typename Info::offset_type offset_type; + offset_type NumBuckets; + offset_type NumEntries; + llvm::SpecificBumpPtrAllocator<Item> BA; + + /// A linked list of values in a particular hash bucket. + struct Bucket { + offset_type Off; + unsigned Length; + Item *Head; + }; + + Bucket *Buckets; + +private: + /// Insert an item into the appropriate hash bucket. + void insert(Bucket *Buckets, size_t Size, Item *E) { + Bucket &B = Buckets[E->Hash & (Size - 1)]; + E->Next = B.Head; + ++B.Length; + B.Head = E; + } + + /// Resize the hash table, moving the old entries into the new buckets. + void resize(size_t NewSize) { + Bucket *NewBuckets = static_cast<Bucket *>( + safe_calloc(NewSize, sizeof(Bucket))); + // Populate NewBuckets with the old entries. + for (size_t I = 0; I < NumBuckets; ++I) + for (Item *E = Buckets[I].Head; E;) { + Item *N = E->Next; + E->Next = nullptr; + insert(NewBuckets, NewSize, E); + E = N; + } + + free(Buckets); + NumBuckets = NewSize; + Buckets = NewBuckets; + } + +public: + /// Insert an entry into the table. + void insert(typename Info::key_type_ref Key, + typename Info::data_type_ref Data) { + Info InfoObj; + insert(Key, Data, InfoObj); + } + + /// Insert an entry into the table. + /// + /// Uses the provided Info instead of a stack allocated one. + void insert(typename Info::key_type_ref Key, + typename Info::data_type_ref Data, Info &InfoObj) { + ++NumEntries; + if (4 * NumEntries >= 3 * NumBuckets) + resize(NumBuckets * 2); + insert(Buckets, NumBuckets, new (BA.Allocate()) Item(Key, Data, InfoObj)); + } + + /// Determine whether an entry has been inserted. + bool contains(typename Info::key_type_ref Key, Info &InfoObj) { + unsigned Hash = InfoObj.ComputeHash(Key); + for (Item *I = Buckets[Hash & (NumBuckets - 1)].Head; I; I = I->Next) + if (I->Hash == Hash && InfoObj.EqualKey(I->Key, Key)) + return true; + return false; + } + + /// Emit the table to Out, which must not be at offset 0. + offset_type Emit(raw_ostream &Out) { + Info InfoObj; + return Emit(Out, InfoObj); + } + + /// Emit the table to Out, which must not be at offset 0. + /// + /// Uses the provided Info instead of a stack allocated one. + offset_type Emit(raw_ostream &Out, Info &InfoObj) { + using namespace llvm::support; + endian::Writer LE(Out, little); + + // Now we're done adding entries, resize the bucket list if it's + // significantly too large. (This only happens if the number of + // entries is small and we're within our initial allocation of + // 64 buckets.) We aim for an occupancy ratio in [3/8, 3/4). + // + // As a special case, if there are two or fewer entries, just + // form a single bucket. A linear scan is fine in that case, and + // this is very common in C++ class lookup tables. This also + // guarantees we produce at least one bucket for an empty table. + // + // FIXME: Try computing a perfect hash function at this point. + unsigned TargetNumBuckets = + NumEntries <= 2 ? 1 : NextPowerOf2(NumEntries * 4 / 3); + if (TargetNumBuckets != NumBuckets) + resize(TargetNumBuckets); + + // Emit the payload of the table. + for (offset_type I = 0; I < NumBuckets; ++I) { + Bucket &B = Buckets[I]; + if (!B.Head) + continue; + + // Store the offset for the data of this bucket. + B.Off = Out.tell(); + assert(B.Off && "Cannot write a bucket at offset 0. Please add padding."); + + // Write out the number of items in the bucket. + LE.write<uint16_t>(B.Length); + assert(B.Length != 0 && "Bucket has a head but zero length?"); + + // Write out the entries in the bucket. + for (Item *I = B.Head; I; I = I->Next) { + LE.write<typename Info::hash_value_type>(I->Hash); + const std::pair<offset_type, offset_type> &Len = + InfoObj.EmitKeyDataLength(Out, I->Key, I->Data); +#ifdef NDEBUG + InfoObj.EmitKey(Out, I->Key, Len.first); + InfoObj.EmitData(Out, I->Key, I->Data, Len.second); +#else + // In asserts mode, check that the users length matches the data they + // wrote. + uint64_t KeyStart = Out.tell(); + InfoObj.EmitKey(Out, I->Key, Len.first); + uint64_t DataStart = Out.tell(); + InfoObj.EmitData(Out, I->Key, I->Data, Len.second); + uint64_t End = Out.tell(); + assert(offset_type(DataStart - KeyStart) == Len.first && + "key length does not match bytes written"); + assert(offset_type(End - DataStart) == Len.second && + "data length does not match bytes written"); +#endif + } + } + + // Pad with zeros so that we can start the hashtable at an aligned address. + offset_type TableOff = Out.tell(); + uint64_t N = offsetToAlignment(TableOff, Align(alignof(offset_type))); + TableOff += N; + while (N--) + LE.write<uint8_t>(0); + + // Emit the hashtable itself. + LE.write<offset_type>(NumBuckets); + LE.write<offset_type>(NumEntries); + for (offset_type I = 0; I < NumBuckets; ++I) + LE.write<offset_type>(Buckets[I].Off); + + return TableOff; + } + + OnDiskChainedHashTableGenerator() { + NumEntries = 0; + NumBuckets = 64; + // Note that we do not need to run the constructors of the individual + // Bucket objects since 'calloc' returns bytes that are all 0. + Buckets = static_cast<Bucket *>(safe_calloc(NumBuckets, sizeof(Bucket))); + } + + ~OnDiskChainedHashTableGenerator() { std::free(Buckets); } +}; + +/// Provides lookup on an on disk hash table. +/// +/// This needs an \c Info that handles reading values from the hash table's +/// payload and computes the hash for a given key. This should provide the +/// following interface: +/// +/// \code +/// class ExampleLookupInfo { +/// public: +/// typedef ExampleData data_type; +/// typedef ExampleInternalKey internal_key_type; // The stored key type. +/// typedef ExampleKey external_key_type; // The type to pass to find(). +/// typedef uint32_t hash_value_type; // The type the hash function returns. +/// typedef uint32_t offset_type; // The type for offsets into the table. +/// +/// /// Compare two keys for equality. +/// static bool EqualKey(internal_key_type &Key1, internal_key_type &Key2); +/// /// Calculate the hash for the given key. +/// static hash_value_type ComputeHash(internal_key_type &IKey); +/// /// Translate from the semantic type of a key in the hash table to the +/// /// type that is actually stored and used for hashing and comparisons. +/// /// The internal and external types are often the same, in which case this +/// /// can simply return the passed in value. +/// static const internal_key_type &GetInternalKey(external_key_type &EKey); +/// /// Read the key and data length from Buffer, leaving it pointing at the +/// /// following byte. +/// static std::pair<offset_type, offset_type> +/// ReadKeyDataLength(const unsigned char *&Buffer); +/// /// Read the key from Buffer, given the KeyLen as reported from +/// /// ReadKeyDataLength. +/// const internal_key_type &ReadKey(const unsigned char *Buffer, +/// offset_type KeyLen); +/// /// Read the data for Key from Buffer, given the DataLen as reported from +/// /// ReadKeyDataLength. +/// data_type ReadData(StringRef Key, const unsigned char *Buffer, +/// offset_type DataLen); +/// }; +/// \endcode +template <typename Info> class OnDiskChainedHashTable { + const typename Info::offset_type NumBuckets; + const typename Info::offset_type NumEntries; + const unsigned char *const Buckets; + const unsigned char *const Base; + Info InfoObj; + +public: + typedef Info InfoType; + typedef typename Info::internal_key_type internal_key_type; + typedef typename Info::external_key_type external_key_type; + typedef typename Info::data_type data_type; + typedef typename Info::hash_value_type hash_value_type; + typedef typename Info::offset_type offset_type; + + OnDiskChainedHashTable(offset_type NumBuckets, offset_type NumEntries, + const unsigned char *Buckets, + const unsigned char *Base, + const Info &InfoObj = Info()) + : NumBuckets(NumBuckets), NumEntries(NumEntries), Buckets(Buckets), + Base(Base), InfoObj(InfoObj) { + assert((reinterpret_cast<uintptr_t>(Buckets) & 0x3) == 0 && + "'buckets' must have a 4-byte alignment"); + } + + /// Read the number of buckets and the number of entries from a hash table + /// produced by OnDiskHashTableGenerator::Emit, and advance the Buckets + /// pointer past them. + static std::pair<offset_type, offset_type> + readNumBucketsAndEntries(const unsigned char *&Buckets) { + assert((reinterpret_cast<uintptr_t>(Buckets) & 0x3) == 0 && + "buckets should be 4-byte aligned."); + using namespace llvm::support; + offset_type NumBuckets = + endian::readNext<offset_type, little, aligned>(Buckets); + offset_type NumEntries = + endian::readNext<offset_type, little, aligned>(Buckets); + return std::make_pair(NumBuckets, NumEntries); + } + + offset_type getNumBuckets() const { return NumBuckets; } + offset_type getNumEntries() const { return NumEntries; } + const unsigned char *getBase() const { return Base; } + const unsigned char *getBuckets() const { return Buckets; } + + bool isEmpty() const { return NumEntries == 0; } + + class iterator { + internal_key_type Key; + const unsigned char *const Data; + const offset_type Len; + Info *InfoObj; + + public: + iterator() : Key(), Data(nullptr), Len(0), InfoObj(nullptr) {} + iterator(const internal_key_type K, const unsigned char *D, offset_type L, + Info *InfoObj) + : Key(K), Data(D), Len(L), InfoObj(InfoObj) {} + + data_type operator*() const { return InfoObj->ReadData(Key, Data, Len); } + + const unsigned char *getDataPtr() const { return Data; } + offset_type getDataLen() const { return Len; } + + bool operator==(const iterator &X) const { return X.Data == Data; } + bool operator!=(const iterator &X) const { return X.Data != Data; } + }; + + /// Look up the stored data for a particular key. + iterator find(const external_key_type &EKey, Info *InfoPtr = nullptr) { + const internal_key_type &IKey = InfoObj.GetInternalKey(EKey); + hash_value_type KeyHash = InfoObj.ComputeHash(IKey); + return find_hashed(IKey, KeyHash, InfoPtr); + } + + /// Look up the stored data for a particular key with a known hash. + iterator find_hashed(const internal_key_type &IKey, hash_value_type KeyHash, + Info *InfoPtr = nullptr) { + using namespace llvm::support; + + if (!InfoPtr) + InfoPtr = &InfoObj; + + // Each bucket is just an offset into the hash table file. + offset_type Idx = KeyHash & (NumBuckets - 1); + const unsigned char *Bucket = Buckets + sizeof(offset_type) * Idx; + + offset_type Offset = endian::readNext<offset_type, little, aligned>(Bucket); + if (Offset == 0) + return iterator(); // Empty bucket. + const unsigned char *Items = Base + Offset; + + // 'Items' starts with a 16-bit unsigned integer representing the + // number of items in this bucket. + unsigned Len = endian::readNext<uint16_t, little, unaligned>(Items); + + for (unsigned i = 0; i < Len; ++i) { + // Read the hash. + hash_value_type ItemHash = + endian::readNext<hash_value_type, little, unaligned>(Items); + + // Determine the length of the key and the data. + const std::pair<offset_type, offset_type> &L = + Info::ReadKeyDataLength(Items); + offset_type ItemLen = L.first + L.second; + + // Compare the hashes. If they are not the same, skip the entry entirely. + if (ItemHash != KeyHash) { + Items += ItemLen; + continue; + } + + // Read the key. + const internal_key_type &X = + InfoPtr->ReadKey((const unsigned char *const)Items, L.first); + + // If the key doesn't match just skip reading the value. + if (!InfoPtr->EqualKey(X, IKey)) { + Items += ItemLen; + continue; + } + + // The key matches! + return iterator(X, Items + L.first, L.second, InfoPtr); + } + + return iterator(); + } + + iterator end() const { return iterator(); } + + Info &getInfoObj() { return InfoObj; } + + /// Create the hash table. + /// + /// \param Buckets is the beginning of the hash table itself, which follows + /// the payload of entire structure. This is the value returned by + /// OnDiskHashTableGenerator::Emit. + /// + /// \param Base is the point from which all offsets into the structure are + /// based. This is offset 0 in the stream that was used when Emitting the + /// table. + static OnDiskChainedHashTable *Create(const unsigned char *Buckets, + const unsigned char *const Base, + const Info &InfoObj = Info()) { + assert(Buckets > Base); + auto NumBucketsAndEntries = readNumBucketsAndEntries(Buckets); + return new OnDiskChainedHashTable<Info>(NumBucketsAndEntries.first, + NumBucketsAndEntries.second, + Buckets, Base, InfoObj); + } +}; + +/// Provides lookup and iteration over an on disk hash table. +/// +/// \copydetails llvm::OnDiskChainedHashTable +template <typename Info> +class OnDiskIterableChainedHashTable : public OnDiskChainedHashTable<Info> { + const unsigned char *Payload; + +public: + typedef OnDiskChainedHashTable<Info> base_type; + typedef typename base_type::internal_key_type internal_key_type; + typedef typename base_type::external_key_type external_key_type; + typedef typename base_type::data_type data_type; + typedef typename base_type::hash_value_type hash_value_type; + typedef typename base_type::offset_type offset_type; + +private: + /// Iterates over all of the keys in the table. + class iterator_base { + const unsigned char *Ptr; + offset_type NumItemsInBucketLeft; + offset_type NumEntriesLeft; + + public: + typedef external_key_type value_type; + + iterator_base(const unsigned char *const Ptr, offset_type NumEntries) + : Ptr(Ptr), NumItemsInBucketLeft(0), NumEntriesLeft(NumEntries) {} + iterator_base() + : Ptr(nullptr), NumItemsInBucketLeft(0), NumEntriesLeft(0) {} + + friend bool operator==(const iterator_base &X, const iterator_base &Y) { + return X.NumEntriesLeft == Y.NumEntriesLeft; + } + friend bool operator!=(const iterator_base &X, const iterator_base &Y) { + return X.NumEntriesLeft != Y.NumEntriesLeft; + } + + /// Move to the next item. + void advance() { + using namespace llvm::support; + if (!NumItemsInBucketLeft) { + // 'Items' starts with a 16-bit unsigned integer representing the + // number of items in this bucket. + NumItemsInBucketLeft = + endian::readNext<uint16_t, little, unaligned>(Ptr); + } + Ptr += sizeof(hash_value_type); // Skip the hash. + // Determine the length of the key and the data. + const std::pair<offset_type, offset_type> &L = + Info::ReadKeyDataLength(Ptr); + Ptr += L.first + L.second; + assert(NumItemsInBucketLeft); + --NumItemsInBucketLeft; + assert(NumEntriesLeft); + --NumEntriesLeft; + } + + /// Get the start of the item as written by the trait (after the hash and + /// immediately before the key and value length). + const unsigned char *getItem() const { + return Ptr + (NumItemsInBucketLeft ? 0 : 2) + sizeof(hash_value_type); + } + }; + +public: + OnDiskIterableChainedHashTable(offset_type NumBuckets, offset_type NumEntries, + const unsigned char *Buckets, + const unsigned char *Payload, + const unsigned char *Base, + const Info &InfoObj = Info()) + : base_type(NumBuckets, NumEntries, Buckets, Base, InfoObj), + Payload(Payload) {} + + /// Iterates over all of the keys in the table. + class key_iterator : public iterator_base { + Info *InfoObj; + + public: + typedef external_key_type value_type; + + key_iterator(const unsigned char *const Ptr, offset_type NumEntries, + Info *InfoObj) + : iterator_base(Ptr, NumEntries), InfoObj(InfoObj) {} + key_iterator() : iterator_base(), InfoObj() {} + + key_iterator &operator++() { + this->advance(); + return *this; + } + key_iterator operator++(int) { // Postincrement + key_iterator tmp = *this; + ++*this; + return tmp; + } + + internal_key_type getInternalKey() const { + auto *LocalPtr = this->getItem(); + + // Determine the length of the key and the data. + auto L = Info::ReadKeyDataLength(LocalPtr); + + // Read the key. + return InfoObj->ReadKey(LocalPtr, L.first); + } + + value_type operator*() const { + return InfoObj->GetExternalKey(getInternalKey()); + } + }; + + key_iterator key_begin() { + return key_iterator(Payload, this->getNumEntries(), &this->getInfoObj()); + } + key_iterator key_end() { return key_iterator(); } + + iterator_range<key_iterator> keys() { + return make_range(key_begin(), key_end()); + } + + /// Iterates over all the entries in the table, returning the data. + class data_iterator : public iterator_base { + Info *InfoObj; + + public: + typedef data_type value_type; + + data_iterator(const unsigned char *const Ptr, offset_type NumEntries, + Info *InfoObj) + : iterator_base(Ptr, NumEntries), InfoObj(InfoObj) {} + data_iterator() : iterator_base(), InfoObj() {} + + data_iterator &operator++() { // Preincrement + this->advance(); + return *this; + } + data_iterator operator++(int) { // Postincrement + data_iterator tmp = *this; + ++*this; + return tmp; + } + + value_type operator*() const { + auto *LocalPtr = this->getItem(); + + // Determine the length of the key and the data. + auto L = Info::ReadKeyDataLength(LocalPtr); + + // Read the key. + const internal_key_type &Key = InfoObj->ReadKey(LocalPtr, L.first); + return InfoObj->ReadData(Key, LocalPtr + L.first, L.second); + } + }; + + data_iterator data_begin() { + return data_iterator(Payload, this->getNumEntries(), &this->getInfoObj()); + } + data_iterator data_end() { return data_iterator(); } + + iterator_range<data_iterator> data() { + return make_range(data_begin(), data_end()); + } + + /// Create the hash table. + /// + /// \param Buckets is the beginning of the hash table itself, which follows + /// the payload of entire structure. This is the value returned by + /// OnDiskHashTableGenerator::Emit. + /// + /// \param Payload is the beginning of the data contained in the table. This + /// is Base plus any padding or header data that was stored, ie, the offset + /// that the stream was at when calling Emit. + /// + /// \param Base is the point from which all offsets into the structure are + /// based. This is offset 0 in the stream that was used when Emitting the + /// table. + static OnDiskIterableChainedHashTable * + Create(const unsigned char *Buckets, const unsigned char *const Payload, + const unsigned char *const Base, const Info &InfoObj = Info()) { + assert(Buckets > Base); + auto NumBucketsAndEntries = + OnDiskIterableChainedHashTable<Info>::readNumBucketsAndEntries(Buckets); + return new OnDiskIterableChainedHashTable<Info>( + NumBucketsAndEntries.first, NumBucketsAndEntries.second, + Buckets, Payload, Base, InfoObj); + } +}; + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/OptimizedStructLayout.h b/contrib/libs/llvm16/include/llvm/Support/OptimizedStructLayout.h new file mode 100644 index 00000000000..eff64a12a12 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/OptimizedStructLayout.h @@ -0,0 +1,154 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- OptimizedStructLayout.h - Struct layout algorithm ---------*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides an interface for laying out a sequence of fields +/// as a struct in a way that attempts to minimizes the total space +/// requirements of the struct while still satisfying the layout +/// requirements of the individual fields. The resulting layout may be +/// substantially more compact than simply laying out the fields in their +/// original order. +/// +/// Fields may be pre-assigned fixed offsets. They may also be given sizes +/// that are not multiples of their alignments. There is no currently no +/// way to describe that a field has interior padding that other fields may +/// be allocated into. +/// +/// This algorithm does not claim to be "optimal" for several reasons: +/// +/// - First, it does not guarantee that the result is minimal in size. +/// There is no known efficient algoorithm to achieve minimality for +/// unrestricted inputs. Nonetheless, this algorithm +/// +/// - Second, there are other ways that a struct layout could be optimized +/// besides space usage, such as locality. This layout may have a mixed +/// impact on locality: less overall memory may be used, but adjacent +/// fields in the original array may be moved further from one another. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_OPTIMIZEDSTRUCTLAYOUT_H +#define LLVM_SUPPORT_OPTIMIZEDSTRUCTLAYOUT_H + +#include "llvm/Support/Alignment.h" +#include "llvm/ADT/ArrayRef.h" +#include <utility> + +namespace llvm { + +/// A field in a structure. +struct OptimizedStructLayoutField { + /// A special value for Offset indicating that the field can be moved + /// anywhere. + static constexpr uint64_t FlexibleOffset = ~(uint64_t)0; + + OptimizedStructLayoutField(const void *Id, uint64_t Size, Align Alignment, + uint64_t FixedOffset = FlexibleOffset) + : Offset(FixedOffset), Size(Size), Id(Id), Alignment(Alignment) { + assert(Size > 0 && "adding an empty field to the layout"); + } + + /// The offset of this field in the final layout. If this is + /// initialized to FlexibleOffset, layout will overwrite it with + /// the assigned offset of the field. + uint64_t Offset; + + /// The required size of this field in bytes. Does not have to be + /// a multiple of Alignment. Must be non-zero. + uint64_t Size; + + /// A opaque value which uniquely identifies this field. + const void *Id; + + /// Private scratch space for the algorithm. The implementation + /// must treat this as uninitialized memory on entry. + void *Scratch; + + /// The required alignment of this field. + Align Alignment; + + /// Return true if this field has been assigned a fixed offset. + /// After layout, this will be true of all the fields. + bool hasFixedOffset() const { + return (Offset != FlexibleOffset); + } + + /// Given that this field has a fixed offset, return the offset + /// of the first byte following it. + uint64_t getEndOffset() const { + assert(hasFixedOffset()); + return Offset + Size; + } +}; + +/// Compute a layout for a struct containing the given fields, making a +/// best-effort attempt to minimize the amount of space required. +/// +/// Two features are supported which require a more careful solution +/// than the well-known "sort by decreasing alignment" solution: +/// +/// - Fields may be assigned a fixed offset in the layout. If there are +/// gaps among the fixed-offset fields, the algorithm may attempt +/// to allocate flexible-offset fields into those gaps. If that's +/// undesirable, the caller should "block out" those gaps by e.g. +/// just creating a single fixed-offset field that represents the +/// entire "header". +/// +/// - The size of a field is not required to be a multiple of, or even +/// greater than, the field's required alignment. The only constraint +/// on fields is that they must not be zero-sized. +/// +/// To simplify the implementation, any fixed-offset fields in the +/// layout must appear at the start of the field array, and they must +/// be ordered by increasing offset. +/// +/// The algorithm will produce a guaranteed-minimal layout with no +/// interior padding in the following "C-style" case: +/// +/// - every field's size is a multiple of its required alignment and +/// - either no fields have initially fixed offsets, or the fixed-offset +/// fields have no interior padding and end at an offset that is at +/// least as aligned as all the flexible-offset fields. +/// +/// Otherwise, while the algorithm will make a best-effort attempt to +/// avoid padding, it cannot guarantee a minimal layout, as there is +/// no known efficient algorithm for doing so. +/// +/// The layout produced by this algorithm may not be stable across LLVM +/// releases. Do not use this anywhere where ABI stability is required. +/// +/// Flexible-offset fields with the same size and alignment will be ordered +/// the same way they were in the initial array. Otherwise the current +/// algorithm makes no effort to preserve the initial order of +/// flexible-offset fields. +/// +/// On return, all fields will have been assigned a fixed offset, and the +/// array will be sorted in order of ascending offsets. Note that this +/// means that the fixed-offset fields may no longer form a strict prefix +/// if there's any padding before they end. +/// +/// The return value is the total size of the struct and its required +/// alignment. Note that the total size is not rounded up to a multiple +/// of the required alignment; clients which require this can do so easily. +std::pair<uint64_t, Align> performOptimizedStructLayout( + MutableArrayRef<OptimizedStructLayoutField> Fields); + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/PGOOptions.h b/contrib/libs/llvm16/include/llvm/Support/PGOOptions.h new file mode 100644 index 00000000000..4a65ebd4621 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/PGOOptions.h @@ -0,0 +1,76 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===------ PGOOptions.h -- PGO option tunables ----------------*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// Define option tunables for PGO. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PGOOPTIONS_H +#define LLVM_SUPPORT_PGOOPTIONS_H + +#include "llvm/Support/Error.h" + +namespace llvm { + +/// A struct capturing PGO tunables. +struct PGOOptions { + enum PGOAction { NoAction, IRInstr, IRUse, SampleUse }; + enum CSPGOAction { NoCSAction, CSIRInstr, CSIRUse }; + PGOOptions(std::string ProfileFile = "", std::string CSProfileGenFile = "", + std::string ProfileRemappingFile = "", PGOAction Action = NoAction, + CSPGOAction CSAction = NoCSAction, + bool DebugInfoForProfiling = false, + bool PseudoProbeForProfiling = false) + : ProfileFile(ProfileFile), CSProfileGenFile(CSProfileGenFile), + ProfileRemappingFile(ProfileRemappingFile), Action(Action), + CSAction(CSAction), DebugInfoForProfiling(DebugInfoForProfiling || + (Action == SampleUse && + !PseudoProbeForProfiling)), + PseudoProbeForProfiling(PseudoProbeForProfiling) { + // Note, we do allow ProfileFile.empty() for Action=IRUse LTO can + // callback with IRUse action without ProfileFile. + + // If there is a CSAction, PGOAction cannot be IRInstr or SampleUse. + assert(this->CSAction == NoCSAction || + (this->Action != IRInstr && this->Action != SampleUse)); + + // For CSIRInstr, CSProfileGenFile also needs to be nonempty. + assert(this->CSAction != CSIRInstr || !this->CSProfileGenFile.empty()); + + // If CSAction is CSIRUse, PGOAction needs to be IRUse as they share + // a profile. + assert(this->CSAction != CSIRUse || this->Action == IRUse); + + // If neither Action nor CSAction, DebugInfoForProfiling or + // PseudoProbeForProfiling needs to be true. + assert(this->Action != NoAction || this->CSAction != NoCSAction || + this->DebugInfoForProfiling || this->PseudoProbeForProfiling); + } + std::string ProfileFile; + std::string CSProfileGenFile; + std::string ProfileRemappingFile; + PGOAction Action; + CSPGOAction CSAction; + bool DebugInfoForProfiling; + bool PseudoProbeForProfiling; +}; +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Parallel.h b/contrib/libs/llvm16/include/llvm/Support/Parallel.h new file mode 100644 index 00000000000..c3b729e55ca --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Parallel.h @@ -0,0 +1,303 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Parallel.h - Parallel algorithms ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PARALLEL_H +#define LLVM_SUPPORT_PARALLEL_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Threading.h" + +#include <algorithm> +#include <condition_variable> +#include <functional> +#include <mutex> + +namespace llvm { + +namespace parallel { + +// Strategy for the default executor used by the parallel routines provided by +// this file. It defaults to using all hardware threads and should be +// initialized before the first use of parallel routines. +extern ThreadPoolStrategy strategy; + +#if LLVM_ENABLE_THREADS +#ifdef _WIN32 +// Direct access to thread_local variables from a different DLL isn't +// possible with Windows Native TLS. +unsigned getThreadIndex(); +#else +// Don't access this directly, use the getThreadIndex wrapper. +extern thread_local unsigned threadIndex; + +inline unsigned getThreadIndex() { return threadIndex; } +#endif +#else +inline unsigned getThreadIndex() { return 0; } +#endif + +namespace detail { +class Latch { + uint32_t Count; + mutable std::mutex Mutex; + mutable std::condition_variable Cond; + +public: + explicit Latch(uint32_t Count = 0) : Count(Count) {} + ~Latch() { + // Ensure at least that sync() was called. + assert(Count == 0); + } + + void inc() { + std::lock_guard<std::mutex> lock(Mutex); + ++Count; + } + + void dec() { + std::lock_guard<std::mutex> lock(Mutex); + if (--Count == 0) + Cond.notify_all(); + } + + void sync() const { + std::unique_lock<std::mutex> lock(Mutex); + Cond.wait(lock, [&] { return Count == 0; }); + } +}; +} // namespace detail + +class TaskGroup { + detail::Latch L; + bool Parallel; + +public: + TaskGroup(); + ~TaskGroup(); + + // Spawn a task, but does not wait for it to finish. + void spawn(std::function<void()> f); + + // Similar to spawn, but execute the task immediately when ThreadsRequested == + // 1. The difference is to give the following pattern a more intuitive order + // when single threading is requested. + // + // for (size_t begin = 0, i = 0, taskSize = 0;;) { + // taskSize += ... + // bool done = ++i == end; + // if (done || taskSize >= taskSizeLimit) { + // tg.execute([=] { fn(begin, i); }); + // if (done) + // break; + // begin = i; + // taskSize = 0; + // } + // } + void execute(std::function<void()> f); + + void sync() const { L.sync(); } +}; + +namespace detail { + +#if LLVM_ENABLE_THREADS +const ptrdiff_t MinParallelSize = 1024; + +/// Inclusive median. +template <class RandomAccessIterator, class Comparator> +RandomAccessIterator medianOf3(RandomAccessIterator Start, + RandomAccessIterator End, + const Comparator &Comp) { + RandomAccessIterator Mid = Start + (std::distance(Start, End) / 2); + return Comp(*Start, *(End - 1)) + ? (Comp(*Mid, *(End - 1)) ? (Comp(*Start, *Mid) ? Mid : Start) + : End - 1) + : (Comp(*Mid, *Start) ? (Comp(*(End - 1), *Mid) ? Mid : End - 1) + : Start); +} + +template <class RandomAccessIterator, class Comparator> +void parallel_quick_sort(RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp, TaskGroup &TG, size_t Depth) { + // Do a sequential sort for small inputs. + if (std::distance(Start, End) < detail::MinParallelSize || Depth == 0) { + llvm::sort(Start, End, Comp); + return; + } + + // Partition. + auto Pivot = medianOf3(Start, End, Comp); + // Move Pivot to End. + std::swap(*(End - 1), *Pivot); + Pivot = std::partition(Start, End - 1, [&Comp, End](decltype(*Start) V) { + return Comp(V, *(End - 1)); + }); + // Move Pivot to middle of partition. + std::swap(*Pivot, *(End - 1)); + + // Recurse. + TG.spawn([=, &Comp, &TG] { + parallel_quick_sort(Start, Pivot, Comp, TG, Depth - 1); + }); + parallel_quick_sort(Pivot + 1, End, Comp, TG, Depth - 1); +} + +template <class RandomAccessIterator, class Comparator> +void parallel_sort(RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp) { + TaskGroup TG; + parallel_quick_sort(Start, End, Comp, TG, + llvm::Log2_64(std::distance(Start, End)) + 1); +} + +// TaskGroup has a relatively high overhead, so we want to reduce +// the number of spawn() calls. We'll create up to 1024 tasks here. +// (Note that 1024 is an arbitrary number. This code probably needs +// improving to take the number of available cores into account.) +enum { MaxTasksPerGroup = 1024 }; + +template <class IterTy, class ResultTy, class ReduceFuncTy, + class TransformFuncTy> +ResultTy parallel_transform_reduce(IterTy Begin, IterTy End, ResultTy Init, + ReduceFuncTy Reduce, + TransformFuncTy Transform) { + // Limit the number of tasks to MaxTasksPerGroup to limit job scheduling + // overhead on large inputs. + size_t NumInputs = std::distance(Begin, End); + if (NumInputs == 0) + return std::move(Init); + size_t NumTasks = std::min(static_cast<size_t>(MaxTasksPerGroup), NumInputs); + std::vector<ResultTy> Results(NumTasks, Init); + { + // Each task processes either TaskSize or TaskSize+1 inputs. Any inputs + // remaining after dividing them equally amongst tasks are distributed as + // one extra input over the first tasks. + TaskGroup TG; + size_t TaskSize = NumInputs / NumTasks; + size_t RemainingInputs = NumInputs % NumTasks; + IterTy TBegin = Begin; + for (size_t TaskId = 0; TaskId < NumTasks; ++TaskId) { + IterTy TEnd = TBegin + TaskSize + (TaskId < RemainingInputs ? 1 : 0); + TG.spawn([=, &Transform, &Reduce, &Results] { + // Reduce the result of transformation eagerly within each task. + ResultTy R = Init; + for (IterTy It = TBegin; It != TEnd; ++It) + R = Reduce(R, Transform(*It)); + Results[TaskId] = R; + }); + TBegin = TEnd; + } + assert(TBegin == End); + } + + // Do a final reduction. There are at most 1024 tasks, so this only adds + // constant single-threaded overhead for large inputs. Hopefully most + // reductions are cheaper than the transformation. + ResultTy FinalResult = std::move(Results.front()); + for (ResultTy &PartialResult : + MutableArrayRef(Results.data() + 1, Results.size() - 1)) + FinalResult = Reduce(FinalResult, std::move(PartialResult)); + return std::move(FinalResult); +} + +#endif + +} // namespace detail +} // namespace parallel + +template <class RandomAccessIterator, + class Comparator = std::less< + typename std::iterator_traits<RandomAccessIterator>::value_type>> +void parallelSort(RandomAccessIterator Start, RandomAccessIterator End, + const Comparator &Comp = Comparator()) { +#if LLVM_ENABLE_THREADS + if (parallel::strategy.ThreadsRequested != 1) { + parallel::detail::parallel_sort(Start, End, Comp); + return; + } +#endif + llvm::sort(Start, End, Comp); +} + +void parallelFor(size_t Begin, size_t End, function_ref<void(size_t)> Fn); + +template <class IterTy, class FuncTy> +void parallelForEach(IterTy Begin, IterTy End, FuncTy Fn) { + parallelFor(0, End - Begin, [&](size_t I) { Fn(Begin[I]); }); +} + +template <class IterTy, class ResultTy, class ReduceFuncTy, + class TransformFuncTy> +ResultTy parallelTransformReduce(IterTy Begin, IterTy End, ResultTy Init, + ReduceFuncTy Reduce, + TransformFuncTy Transform) { +#if LLVM_ENABLE_THREADS + if (parallel::strategy.ThreadsRequested != 1) { + return parallel::detail::parallel_transform_reduce(Begin, End, Init, Reduce, + Transform); + } +#endif + for (IterTy I = Begin; I != End; ++I) + Init = Reduce(std::move(Init), Transform(*I)); + return std::move(Init); +} + +// Range wrappers. +template <class RangeTy, + class Comparator = std::less<decltype(*std::begin(RangeTy()))>> +void parallelSort(RangeTy &&R, const Comparator &Comp = Comparator()) { + parallelSort(std::begin(R), std::end(R), Comp); +} + +template <class RangeTy, class FuncTy> +void parallelForEach(RangeTy &&R, FuncTy Fn) { + parallelForEach(std::begin(R), std::end(R), Fn); +} + +template <class RangeTy, class ResultTy, class ReduceFuncTy, + class TransformFuncTy> +ResultTy parallelTransformReduce(RangeTy &&R, ResultTy Init, + ReduceFuncTy Reduce, + TransformFuncTy Transform) { + return parallelTransformReduce(std::begin(R), std::end(R), Init, Reduce, + Transform); +} + +// Parallel for-each, but with error handling. +template <class RangeTy, class FuncTy> +Error parallelForEachError(RangeTy &&R, FuncTy Fn) { + // The transform_reduce algorithm requires that the initial value be copyable. + // Error objects are uncopyable. We only need to copy initial success values, + // so work around this mismatch via the C API. The C API represents success + // values with a null pointer. The joinErrors discards null values and joins + // multiple errors into an ErrorList. + return unwrap(parallelTransformReduce( + std::begin(R), std::end(R), wrap(Error::success()), + [](LLVMErrorRef Lhs, LLVMErrorRef Rhs) { + return wrap(joinErrors(unwrap(Lhs), unwrap(Rhs))); + }, + [&Fn](auto &&V) { return wrap(Fn(V)); })); +} + +} // namespace llvm + +#endif // LLVM_SUPPORT_PARALLEL_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Path.h b/contrib/libs/llvm16/include/llvm/Support/Path.h new file mode 100644 index 00000000000..0e2dd79c737 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Path.h @@ -0,0 +1,564 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Path.h - Path Operating System Concept ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::path namespace. It is designed after +// TR2/boost filesystem (v3), but modified to remove exception handling and the +// path class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PATH_H +#define LLVM_SUPPORT_PATH_H + +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/iterator.h" +#include "llvm/Support/DataTypes.h" +#include <iterator> + +namespace llvm { +namespace sys { +namespace path { + +enum class Style { + native, + posix, + windows_slash, + windows_backslash, + windows = windows_backslash, // deprecated +}; + +/// Check if \p S uses POSIX path rules. +constexpr bool is_style_posix(Style S) { + if (S == Style::posix) + return true; + if (S != Style::native) + return false; +#if defined(_WIN32) + return false; +#else + return true; +#endif +} + +/// Check if \p S uses Windows path rules. +constexpr bool is_style_windows(Style S) { return !is_style_posix(S); } + +/// @name Lexical Component Iterator +/// @{ + +/// Path iterator. +/// +/// This is an input iterator that iterates over the individual components in +/// \a path. The traversal order is as follows: +/// * The root-name element, if present. +/// * The root-directory element, if present. +/// * Each successive filename element, if present. +/// * Dot, if one or more trailing non-root slash characters are present. +/// Traversing backwards is possible with \a reverse_iterator +/// +/// Iteration examples. Each component is separated by ',': +/// @code +/// / => / +/// /foo => /,foo +/// foo/ => foo,. +/// /foo/bar => /,foo,bar +/// ../ => ..,. +/// C:\foo\bar => C:,\,foo,bar +/// @endcode +class const_iterator + : public iterator_facade_base<const_iterator, std::input_iterator_tag, + const StringRef> { + StringRef Path; ///< The entire path. + StringRef Component; ///< The current component. Not necessarily in Path. + size_t Position = 0; ///< The iterators current position within Path. + Style S = Style::native; ///< The path style to use. + + // An end iterator has Position = Path.size() + 1. + friend const_iterator begin(StringRef path, Style style); + friend const_iterator end(StringRef path); + +public: + reference operator*() const { return Component; } + const_iterator &operator++(); // preincrement + bool operator==(const const_iterator &RHS) const; + + /// Difference in bytes between this and RHS. + ptrdiff_t operator-(const const_iterator &RHS) const; +}; + +/// Reverse path iterator. +/// +/// This is an input iterator that iterates over the individual components in +/// \a path in reverse order. The traversal order is exactly reversed from that +/// of \a const_iterator +class reverse_iterator + : public iterator_facade_base<reverse_iterator, std::input_iterator_tag, + const StringRef> { + StringRef Path; ///< The entire path. + StringRef Component; ///< The current component. Not necessarily in Path. + size_t Position = 0; ///< The iterators current position within Path. + Style S = Style::native; ///< The path style to use. + + friend reverse_iterator rbegin(StringRef path, Style style); + friend reverse_iterator rend(StringRef path); + +public: + reference operator*() const { return Component; } + reverse_iterator &operator++(); // preincrement + bool operator==(const reverse_iterator &RHS) const; + + /// Difference in bytes between this and RHS. + ptrdiff_t operator-(const reverse_iterator &RHS) const; +}; + +/// Get begin iterator over \a path. +/// @param path Input path. +/// @returns Iterator initialized with the first component of \a path. +const_iterator begin(StringRef path, Style style = Style::native); + +/// Get end iterator over \a path. +/// @param path Input path. +/// @returns Iterator initialized to the end of \a path. +const_iterator end(StringRef path); + +/// Get reverse begin iterator over \a path. +/// @param path Input path. +/// @returns Iterator initialized with the first reverse component of \a path. +reverse_iterator rbegin(StringRef path, Style style = Style::native); + +/// Get reverse end iterator over \a path. +/// @param path Input path. +/// @returns Iterator initialized to the reverse end of \a path. +reverse_iterator rend(StringRef path); + +/// @} +/// @name Lexical Modifiers +/// @{ + +/// Remove the last component from \a path unless it is the root dir. +/// +/// Similar to the POSIX "dirname" utility. +/// +/// @code +/// directory/filename.cpp => directory/ +/// directory/ => directory +/// filename.cpp => <empty> +/// / => / +/// @endcode +/// +/// @param path A path that is modified to not have a file component. +void remove_filename(SmallVectorImpl<char> &path, Style style = Style::native); + +/// Replace the file extension of \a path with \a extension. +/// +/// @code +/// ./filename.cpp => ./filename.extension +/// ./filename => ./filename.extension +/// ./ => ./.extension +/// @endcode +/// +/// @param path A path that has its extension replaced with \a extension. +/// @param extension The extension to be added. It may be empty. It may also +/// optionally start with a '.', if it does not, one will be +/// prepended. +void replace_extension(SmallVectorImpl<char> &path, const Twine &extension, + Style style = Style::native); + +/// Replace matching path prefix with another path. +/// +/// @code +/// /foo, /old, /new => /foo +/// /old, /old, /new => /new +/// /old, /old/, /new => /old +/// /old/foo, /old, /new => /new/foo +/// /old/foo, /old/, /new => /new/foo +/// /old/foo, /old/, /new/ => /new/foo +/// /oldfoo, /old, /new => /oldfoo +/// /foo, <empty>, /new => /new/foo +/// /foo, <empty>, new => new/foo +/// /old/foo, /old, <empty> => /foo +/// @endcode +/// +/// @param Path If \a Path starts with \a OldPrefix modify to instead +/// start with \a NewPrefix. +/// @param OldPrefix The path prefix to strip from \a Path. +/// @param NewPrefix The path prefix to replace \a NewPrefix with. +/// @param style The style used to match the prefix. Exact match using +/// Posix style, case/separator insensitive match for Windows style. +/// @result true if \a Path begins with OldPrefix +bool replace_path_prefix(SmallVectorImpl<char> &Path, StringRef OldPrefix, + StringRef NewPrefix, + Style style = Style::native); + +/// Remove redundant leading "./" pieces and consecutive separators. +/// +/// @param path Input path. +/// @result The cleaned-up \a path. +StringRef remove_leading_dotslash(StringRef path, Style style = Style::native); + +/// In-place remove any './' and optionally '../' components from a path. +/// +/// @param path processed path +/// @param remove_dot_dot specify if '../' (except for leading "../") should be +/// removed +/// @result True if path was changed +bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot = false, + Style style = Style::native); + +/// Append to path. +/// +/// @code +/// /foo + bar/f => /foo/bar/f +/// /foo/ + bar/f => /foo/bar/f +/// foo + bar/f => foo/bar/f +/// @endcode +/// +/// @param path Set to \a path + \a component. +/// @param a The component to be appended to \a path. +void append(SmallVectorImpl<char> &path, const Twine &a, + const Twine &b = "", + const Twine &c = "", + const Twine &d = ""); + +void append(SmallVectorImpl<char> &path, Style style, const Twine &a, + const Twine &b = "", const Twine &c = "", const Twine &d = ""); + +/// Append to path. +/// +/// @code +/// /foo + [bar,f] => /foo/bar/f +/// /foo/ + [bar,f] => /foo/bar/f +/// foo + [bar,f] => foo/bar/f +/// @endcode +/// +/// @param path Set to \a path + [\a begin, \a end). +/// @param begin Start of components to append. +/// @param end One past the end of components to append. +void append(SmallVectorImpl<char> &path, const_iterator begin, + const_iterator end, Style style = Style::native); + +/// @} +/// @name Transforms (or some other better name) +/// @{ + +/// Convert path to the native form. This is used to give paths to users and +/// operating system calls in the platform's normal way. For example, on Windows +/// all '/' are converted to '\'. On Unix, it converts all '\' to '/'. +/// +/// @param path A path that is transformed to native format. +/// @param result Holds the result of the transformation. +void native(const Twine &path, SmallVectorImpl<char> &result, + Style style = Style::native); + +/// Convert path to the native form in place. This is used to give paths to +/// users and operating system calls in the platform's normal way. For example, +/// on Windows all '/' are converted to '\'. +/// +/// @param path A path that is transformed to native format. +void native(SmallVectorImpl<char> &path, Style style = Style::native); + +/// For Windows path styles, convert path to use the preferred path separators. +/// For other styles, do nothing. +/// +/// @param path A path that is transformed to preferred format. +inline void make_preferred(SmallVectorImpl<char> &path, + Style style = Style::native) { + if (!is_style_windows(style)) + return; + native(path, style); +} + +/// Replaces backslashes with slashes if Windows. +/// +/// @param path processed path +/// @result The result of replacing backslashes with forward slashes if Windows. +/// On Unix, this function is a no-op because backslashes are valid path +/// chracters. +std::string convert_to_slash(StringRef path, Style style = Style::native); + +/// @} +/// @name Lexical Observers +/// @{ + +/// Get root name. +/// +/// @code +/// //net/hello => //net +/// c:/hello => c: (on Windows, on other platforms nothing) +/// /hello => <empty> +/// @endcode +/// +/// @param path Input path. +/// @result The root name of \a path if it has one, otherwise "". +StringRef root_name(StringRef path, Style style = Style::native); + +/// Get root directory. +/// +/// @code +/// /goo/hello => / +/// c:/hello => / +/// d/file.txt => <empty> +/// @endcode +/// +/// @param path Input path. +/// @result The root directory of \a path if it has one, otherwise +/// "". +StringRef root_directory(StringRef path, Style style = Style::native); + +/// Get root path. +/// +/// Equivalent to root_name + root_directory. +/// +/// @param path Input path. +/// @result The root path of \a path if it has one, otherwise "". +StringRef root_path(StringRef path, Style style = Style::native); + +/// Get relative path. +/// +/// @code +/// C:\hello\world => hello\world +/// foo/bar => foo/bar +/// /foo/bar => foo/bar +/// @endcode +/// +/// @param path Input path. +/// @result The path starting after root_path if one exists, otherwise "". +StringRef relative_path(StringRef path, Style style = Style::native); + +/// Get parent path. +/// +/// @code +/// / => <empty> +/// /foo => / +/// foo/../bar => foo/.. +/// @endcode +/// +/// @param path Input path. +/// @result The parent path of \a path if one exists, otherwise "". +StringRef parent_path(StringRef path, Style style = Style::native); + +/// Get filename. +/// +/// @code +/// /foo.txt => foo.txt +/// . => . +/// .. => .. +/// / => / +/// @endcode +/// +/// @param path Input path. +/// @result The filename part of \a path. This is defined as the last component +/// of \a path. Similar to the POSIX "basename" utility. +StringRef filename(StringRef path, Style style = Style::native); + +/// Get stem. +/// +/// If filename contains a dot but not solely one or two dots, result is the +/// substring of filename ending at (but not including) the last dot. Otherwise +/// it is filename. +/// +/// @code +/// /foo/bar.txt => bar +/// /foo/bar => bar +/// /foo/.txt => <empty> +/// /foo/. => . +/// /foo/.. => .. +/// @endcode +/// +/// @param path Input path. +/// @result The stem of \a path. +StringRef stem(StringRef path, Style style = Style::native); + +/// Get extension. +/// +/// If filename contains a dot but not solely one or two dots, result is the +/// substring of filename starting at (and including) the last dot, and ending +/// at the end of \a path. Otherwise "". +/// +/// @code +/// /foo/bar.txt => .txt +/// /foo/bar => <empty> +/// /foo/.txt => .txt +/// @endcode +/// +/// @param path Input path. +/// @result The extension of \a path. +StringRef extension(StringRef path, Style style = Style::native); + +/// Check whether the given char is a path separator on the host OS. +/// +/// @param value a character +/// @result true if \a value is a path separator character on the host OS +bool is_separator(char value, Style style = Style::native); + +/// Return the preferred separator for this platform. +/// +/// @result StringRef of the preferred separator, null-terminated. +StringRef get_separator(Style style = Style::native); + +/// Get the typical temporary directory for the system, e.g., +/// "/var/tmp" or "C:/TEMP" +/// +/// @param erasedOnReboot Whether to favor a path that is erased on reboot +/// rather than one that potentially persists longer. This parameter will be +/// ignored if the user or system has set the typical environment variable +/// (e.g., TEMP on Windows, TMPDIR on *nix) to specify a temporary directory. +/// +/// @param result Holds the resulting path name. +void system_temp_directory(bool erasedOnReboot, SmallVectorImpl<char> &result); + +/// Get the user's home directory. +/// +/// @param result Holds the resulting path name. +/// @result True if a home directory is set, false otherwise. +bool home_directory(SmallVectorImpl<char> &result); + +/// Get the directory where packages should read user-specific configurations. +/// e.g. $XDG_CONFIG_HOME. +/// +/// @param result Holds the resulting path name. +/// @result True if the appropriate path was determined, it need not exist. +bool user_config_directory(SmallVectorImpl<char> &result); + +/// Get the directory where installed packages should put their +/// machine-local cache, e.g. $XDG_CACHE_HOME. +/// +/// @param result Holds the resulting path name. +/// @result True if the appropriate path was determined, it need not exist. +bool cache_directory(SmallVectorImpl<char> &result); + +/// Has root name? +/// +/// root_name != "" +/// +/// @param path Input path. +/// @result True if the path has a root name, false otherwise. +bool has_root_name(const Twine &path, Style style = Style::native); + +/// Has root directory? +/// +/// root_directory != "" +/// +/// @param path Input path. +/// @result True if the path has a root directory, false otherwise. +bool has_root_directory(const Twine &path, Style style = Style::native); + +/// Has root path? +/// +/// root_path != "" +/// +/// @param path Input path. +/// @result True if the path has a root path, false otherwise. +bool has_root_path(const Twine &path, Style style = Style::native); + +/// Has relative path? +/// +/// relative_path != "" +/// +/// @param path Input path. +/// @result True if the path has a relative path, false otherwise. +bool has_relative_path(const Twine &path, Style style = Style::native); + +/// Has parent path? +/// +/// parent_path != "" +/// +/// @param path Input path. +/// @result True if the path has a parent path, false otherwise. +bool has_parent_path(const Twine &path, Style style = Style::native); + +/// Has filename? +/// +/// filename != "" +/// +/// @param path Input path. +/// @result True if the path has a filename, false otherwise. +bool has_filename(const Twine &path, Style style = Style::native); + +/// Has stem? +/// +/// stem != "" +/// +/// @param path Input path. +/// @result True if the path has a stem, false otherwise. +bool has_stem(const Twine &path, Style style = Style::native); + +/// Has extension? +/// +/// extension != "" +/// +/// @param path Input path. +/// @result True if the path has a extension, false otherwise. +bool has_extension(const Twine &path, Style style = Style::native); + +/// Is path absolute? +/// +/// According to cppreference.com, C++17 states: "An absolute path is a path +/// that unambiguously identifies the location of a file without reference to +/// an additional starting location." +/// +/// In other words, the rules are: +/// 1) POSIX style paths with nonempty root directory are absolute. +/// 2) Windows style paths with nonempty root name and root directory are +/// absolute. +/// 3) No other paths are absolute. +/// +/// \see has_root_name +/// \see has_root_directory +/// +/// @param path Input path. +/// @result True if the path is absolute, false if it is not. +bool is_absolute(const Twine &path, Style style = Style::native); + +/// Is path absolute using GNU rules? +/// +/// GNU rules are: +/// 1) Paths starting with a path separator are absolute. +/// 2) Windows style paths are also absolute if they start with a character +/// followed by ':'. +/// 3) No other paths are absolute. +/// +/// On Windows style the path "C:\Users\Default" has "C:" as root name and "\" +/// as root directory. +/// +/// Hence "C:" on Windows is absolute under GNU rules and not absolute under +/// C++17 because it has no root directory. Likewise "/" and "\" on Windows are +/// absolute under GNU and are not absolute under C++17 due to empty root name. +/// +/// \see has_root_name +/// \see has_root_directory +/// +/// @param path Input path. +/// @param style The style of \p path (e.g. Windows or POSIX). "native" style +/// means to derive the style from the host. +/// @result True if the path is absolute following GNU rules, false if it is +/// not. +bool is_absolute_gnu(const Twine &path, Style style = Style::native); + +/// Is path relative? +/// +/// @param path Input path. +/// @result True if the path is relative, false if it is not. +bool is_relative(const Twine &path, Style style = Style::native); + +} // end namespace path +} // end namespace sys +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/PluginLoader.h b/contrib/libs/llvm16/include/llvm/Support/PluginLoader.h new file mode 100644 index 00000000000..2a502e99c4e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/PluginLoader.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/PluginLoader.h - Plugin Loader for Tools ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// A tool can #include this file to get a -load option that allows the user to +// load arbitrary shared objects into the tool's address space. Note that this +// header can only be included by a program ONCE, so it should never to used by +// library authors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PLUGINLOADER_H +#define LLVM_SUPPORT_PLUGINLOADER_H + +#ifndef DONT_GET_PLUGIN_LOADER_OPTION +#include "llvm/Support/CommandLine.h" +#endif + +#include <string> + +namespace llvm { + struct PluginLoader { + void operator=(const std::string &Filename); + static unsigned getNumPlugins(); + static std::string& getPlugin(unsigned num); + }; + +#ifndef DONT_GET_PLUGIN_LOADER_OPTION + // This causes operator= above to be invoked for every -load option. + static cl::opt<PluginLoader, false, cl::parser<std::string>> + LoadOpt("load", cl::value_desc("pluginfilename"), + cl::desc("Load the specified plugin")); +#endif +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/PointerLikeTypeTraits.h b/contrib/libs/llvm16/include/llvm/Support/PointerLikeTypeTraits.h new file mode 100644 index 00000000000..1766deb45c0 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/PointerLikeTypeTraits.h @@ -0,0 +1,163 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/PointerLikeTypeTraits.h - Pointer Traits ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the PointerLikeTypeTraits class. This allows data +// structures to reason about pointers and other things that are pointer sized. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_POINTERLIKETYPETRAITS_H +#define LLVM_SUPPORT_POINTERLIKETYPETRAITS_H + +#include "llvm/Support/DataTypes.h" +#include <cassert> +#include <type_traits> + +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> struct PointerLikeTypeTraits; + +namespace detail { +/// A tiny meta function to compute the log2 of a compile time constant. +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> struct PointerLikeTypeTraits<T *> { + static inline void *getAsVoidPointer(T *P) { return P; } + static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); } + + static constexpr int NumLowBitsAvailable = + detail::ConstantLog2<alignof(T)>::value; +}; + +template <> struct PointerLikeTypeTraits<void *> { + static inline void *getAsVoidPointer(void *P) { return P; } + static inline void *getFromVoidPointer(void *P) { return P; } + + /// Note, we assume here that void* is related to raw malloc'ed memory and + /// that malloc returns objects at least 4-byte aligned. However, this may be + /// wrong, or pointers may be from something other than malloc. In this case, + /// you should specify a real typed pointer or avoid this template. + /// + /// All clients should use assertions to do a run-time check to ensure that + /// this is actually true. + static constexpr int NumLowBitsAvailable = 2; +}; + +// Provide PointerLikeTypeTraits for const things. +template <typename T> struct PointerLikeTypeTraits<const T> { + typedef PointerLikeTypeTraits<T> NonConst; + + static inline const void *getAsVoidPointer(const T P) { + return NonConst::getAsVoidPointer(P); + } + static inline const T getFromVoidPointer(const void *P) { + return NonConst::getFromVoidPointer(const_cast<void *>(P)); + } + static constexpr int NumLowBitsAvailable = NonConst::NumLowBitsAvailable; +}; + +// Provide PointerLikeTypeTraits for const pointers. +template <typename T> struct PointerLikeTypeTraits<const T *> { + typedef PointerLikeTypeTraits<T *> NonConst; + + static inline const void *getAsVoidPointer(const T *P) { + return NonConst::getAsVoidPointer(const_cast<T *>(P)); + } + static inline const T *getFromVoidPointer(const void *P) { + return NonConst::getFromVoidPointer(const_cast<void *>(P)); + } + static constexpr int NumLowBitsAvailable = NonConst::NumLowBitsAvailable; +}; + +// Provide PointerLikeTypeTraits for uintptr_t. +template <> struct PointerLikeTypeTraits<uintptr_t> { + static inline void *getAsVoidPointer(uintptr_t P) { + return reinterpret_cast<void *>(P); + } + static inline uintptr_t getFromVoidPointer(void *P) { + return reinterpret_cast<uintptr_t>(P); + } + // No bits are available! + static constexpr int NumLowBitsAvailable = 0; +}; + +/// Provide suitable custom traits struct for function pointers. +/// +/// Function pointers can't be directly given these traits as functions can't +/// have their alignment computed with `alignof` and we need different casting. +/// +/// To rely on higher alignment for a specialized use, you can provide a +/// customized form of this template explicitly with higher alignment, and +/// potentially use alignment attributes on functions to satisfy that. +template <int Alignment, typename FunctionPointerT> +struct FunctionPointerLikeTypeTraits { + static constexpr int NumLowBitsAvailable = + detail::ConstantLog2<Alignment>::value; + static inline void *getAsVoidPointer(FunctionPointerT P) { + assert((reinterpret_cast<uintptr_t>(P) & + ~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 && + "Alignment not satisfied for an actual function pointer!"); + return reinterpret_cast<void *>(P); + } + static inline FunctionPointerT getFromVoidPointer(void *P) { + return reinterpret_cast<FunctionPointerT>(P); + } +}; + +/// Provide a default specialization for function pointers that assumes 4-byte +/// alignment. +/// +/// We assume here that functions used with this are always at least 4-byte +/// aligned. This means that, for example, thumb functions won't work or systems +/// with weird unaligned function pointers won't work. But all practical systems +/// we support satisfy this requirement. +template <typename ReturnT, typename... ParamTs> +struct PointerLikeTypeTraits<ReturnT (*)(ParamTs...)> + : FunctionPointerLikeTypeTraits<4, ReturnT (*)(ParamTs...)> {}; + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/PrettyStackTrace.h b/contrib/libs/llvm16/include/llvm/Support/PrettyStackTrace.h new file mode 100644 index 00000000000..eea84d187d2 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/PrettyStackTrace.h @@ -0,0 +1,127 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/PrettyStackTrace.h - Pretty Crash Handling --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the PrettyStackTraceEntry class, which is used to make +// crashes give more contextual information about what the program was doing +// when it crashed. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PRETTYSTACKTRACE_H +#define LLVM_SUPPORT_PRETTYSTACKTRACE_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + class raw_ostream; + + /// Enables dumping a "pretty" stack trace when the program crashes. + /// + /// \see PrettyStackTraceEntry + void EnablePrettyStackTrace(); + + /// Enables (or disables) dumping a "pretty" stack trace when the user sends + /// SIGINFO or SIGUSR1 to the current process. + /// + /// This is a per-thread decision so that a program can choose to print stack + /// traces only on a primary thread, or on all threads that use + /// PrettyStackTraceEntry. + /// + /// \see EnablePrettyStackTrace + /// \see PrettyStackTraceEntry + void EnablePrettyStackTraceOnSigInfoForThisThread(bool ShouldEnable = true); + + /// Replaces the generic bug report message that is output upon + /// a crash. + void setBugReportMsg(const char *Msg); + + /// Get the bug report message that will be output upon a crash. + const char *getBugReportMsg(); + + /// PrettyStackTraceEntry - This class is used to represent a frame of the + /// "pretty" stack trace that is dumped when a program crashes. You can define + /// subclasses of this and declare them on the program stack: when they are + /// constructed and destructed, they will add their symbolic frames to a + /// virtual stack trace. This gets dumped out if the program crashes. + class PrettyStackTraceEntry { + friend PrettyStackTraceEntry *ReverseStackTrace(PrettyStackTraceEntry *); + + PrettyStackTraceEntry *NextEntry; + PrettyStackTraceEntry(const PrettyStackTraceEntry &) = delete; + void operator=(const PrettyStackTraceEntry &) = delete; + public: + PrettyStackTraceEntry(); + virtual ~PrettyStackTraceEntry(); + + /// print - Emit information about this stack frame to OS. + virtual void print(raw_ostream &OS) const = 0; + + /// getNextEntry - Return the next entry in the list of frames. + const PrettyStackTraceEntry *getNextEntry() const { return NextEntry; } + }; + + /// PrettyStackTraceString - This object prints a specified string (which + /// should not contain newlines) to the stream as the stack trace when a crash + /// occurs. + class PrettyStackTraceString : public PrettyStackTraceEntry { + const char *Str; + public: + PrettyStackTraceString(const char *str) : Str(str) {} + void print(raw_ostream &OS) const override; + }; + + /// PrettyStackTraceFormat - This object prints a string (which may use + /// printf-style formatting but should not contain newlines) to the stream + /// as the stack trace when a crash occurs. + class PrettyStackTraceFormat : public PrettyStackTraceEntry { + llvm::SmallVector<char, 32> Str; + public: + PrettyStackTraceFormat(const char *Format, ...); + void print(raw_ostream &OS) const override; + }; + + /// PrettyStackTraceProgram - This object prints a specified program arguments + /// to the stream as the stack trace when a crash occurs. + class PrettyStackTraceProgram : public PrettyStackTraceEntry { + int ArgC; + const char *const *ArgV; + public: + PrettyStackTraceProgram(int argc, const char * const*argv) + : ArgC(argc), ArgV(argv) { + EnablePrettyStackTrace(); + } + void print(raw_ostream &OS) const override; + }; + + /// Returns the topmost element of the "pretty" stack state. + const void *SavePrettyStackState(); + + /// Restores the topmost element of the "pretty" stack state to State, which + /// should come from a previous call to SavePrettyStackState(). This is + /// useful when using a CrashRecoveryContext in code that also uses + /// PrettyStackTraceEntries, to make sure the stack that's printed if a crash + /// happens after a crash that's been recovered by CrashRecoveryContext + /// doesn't have frames on it that were added in code unwound by the + /// CrashRecoveryContext. + void RestorePrettyStackState(const void *State); + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Printable.h b/contrib/libs/llvm16/include/llvm/Support/Printable.h new file mode 100644 index 00000000000..33d0918a4c2 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Printable.h @@ -0,0 +1,63 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- Printable.h - Print function helpers -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the Printable struct. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PRINTABLE_H +#define LLVM_SUPPORT_PRINTABLE_H + +#include <functional> +#include <utility> + +namespace llvm { + +class raw_ostream; + +/// Simple wrapper around std::function<void(raw_ostream&)>. +/// This class is useful to construct print helpers for raw_ostream. +/// +/// Example: +/// Printable printRegister(unsigned Register) { +/// return Printable([Register](raw_ostream &OS) { +/// OS << getRegisterName(Register); +/// }); +/// } +/// ... OS << printRegister(Register); ... +/// +/// Implementation note: Ideally this would just be a typedef, but doing so +/// leads to operator << being ambiguous as function has matching constructors +/// in some STL versions. I have seen the problem on gcc 4.6 libstdc++ and +/// microsoft STL. +class Printable { +public: + std::function<void(raw_ostream &OS)> Print; + Printable(std::function<void(raw_ostream &OS)> Print) + : Print(std::move(Print)) {} +}; + +inline raw_ostream &operator<<(raw_ostream &OS, const Printable &P) { + P.Print(OS); + return OS; +} + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Process.h b/contrib/libs/llvm16/include/llvm/Support/Process.h new file mode 100644 index 00000000000..b562d59b4ec --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Process.h @@ -0,0 +1,236 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Process.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// Provides a library for accessing information about this process and other +/// processes on the operating system. Also provides means of spawning +/// subprocess for commands. The design of this library is modeled after the +/// proposed design of the Boost.Process library, and is design specifically to +/// follow the style of standard libraries and potentially become a proposal +/// for a standard library. +/// +/// This file declares the llvm::sys::Process class which contains a collection +/// of legacy static interfaces for extracting various information about the +/// current process. The goal is to migrate users of this API over to the new +/// interfaces. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PROCESS_H +#define LLVM_SUPPORT_PROCESS_H + +#include "llvm/Support/Chrono.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Program.h" +#include <optional> +#include <system_error> + +namespace llvm { +template <typename T> class ArrayRef; +class StringRef; + +namespace sys { + + +/// A collection of legacy interfaces for querying information about the +/// current executing process. +class Process { +public: + using Pid = int32_t; + + /// Get the process's identifier. + static Pid getProcessId(); + + /// Get the process's page size. + /// This may fail if the underlying syscall returns an error. In most cases, + /// page size information is used for optimization, and this error can be + /// safely discarded by calling consumeError, and an estimated page size + /// substituted instead. + static Expected<unsigned> getPageSize(); + + /// Get the process's estimated page size. + /// This function always succeeds, but if the underlying syscall to determine + /// the page size fails then this will silently return an estimated page size. + /// The estimated page size is guaranteed to be a power of 2. + static unsigned getPageSizeEstimate() { + if (auto PageSize = getPageSize()) + return *PageSize; + else { + consumeError(PageSize.takeError()); + return 4096; + } + } + + /// Return process memory usage. + /// This static function will return the total amount of memory allocated + /// by the process. This only counts the memory allocated via the malloc, + /// calloc and realloc functions and includes any "free" holes in the + /// allocated space. + static size_t GetMallocUsage(); + + /// This static function will set \p user_time to the amount of CPU time + /// spent in user (non-kernel) mode and \p sys_time to the amount of CPU + /// time spent in system (kernel) mode. If the operating system does not + /// support collection of these metrics, a zero duration will be for both + /// values. + /// \param elapsed Returns the system_clock::now() giving current time + /// \param user_time Returns the current amount of user time for the process + /// \param sys_time Returns the current amount of system time for the process + static void GetTimeUsage(TimePoint<> &elapsed, + std::chrono::nanoseconds &user_time, + std::chrono::nanoseconds &sys_time); + + /// This function makes the necessary calls to the operating system to + /// prevent core files or any other kind of large memory dumps that can + /// occur when a program fails. + /// Prevent core file generation. + static void PreventCoreFiles(); + + /// true if PreventCoreFiles has been called, false otherwise. + static bool AreCoreFilesPrevented(); + + // This function returns the environment variable \arg name's value as a UTF-8 + // string. \arg Name is assumed to be in UTF-8 encoding too. + static std::optional<std::string> GetEnv(StringRef name); + + /// 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. 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 std::optional<std::string> + FindInEnvPath(StringRef EnvName, StringRef FileName, + ArrayRef<std::string> IgnoreList, + char Separator = EnvPathSeparator); + + static std::optional<std::string> + FindInEnvPath(StringRef EnvName, StringRef FileName, + char Separator = EnvPathSeparator); + + // This functions ensures that the standard file descriptors (input, output, + // and error) are properly mapped to a file descriptor before we use any of + // them. This should only be called by standalone programs, library + // components should not call this. + static std::error_code FixupStandardFileDescriptors(); + + // This function safely closes a file descriptor. It is not safe to retry + // close(2) when it returns with errno equivalent to EINTR; this is because + // *nixen cannot agree if the file descriptor is, in fact, closed when this + // occurs. + // + // N.B. Some operating systems, due to thread cancellation, cannot properly + // guarantee that it will or will not be closed one way or the other! + static std::error_code SafelyCloseFileDescriptor(int FD); + + /// This function determines if the standard input is connected directly + /// to a user's input (keyboard probably), rather than coming from a file + /// or pipe. + static bool StandardInIsUserInput(); + + /// This function determines if the standard output is connected to a + /// "tty" or "console" window. That is, the output would be displayed to + /// the user rather than being put on a pipe or stored in a file. + static bool StandardOutIsDisplayed(); + + /// This function determines if the standard error is connected to a + /// "tty" or "console" window. That is, the output would be displayed to + /// the user rather than being put on a pipe or stored in a file. + static bool StandardErrIsDisplayed(); + + /// This function determines if the given file descriptor is connected to + /// a "tty" or "console" window. That is, the output would be displayed to + /// the user rather than being put on a pipe or stored in a file. + static bool FileDescriptorIsDisplayed(int fd); + + /// This function determines if the given file descriptor is displayd and + /// supports colors. + static bool FileDescriptorHasColors(int fd); + + /// This function determines the number of columns in the window + /// if standard output is connected to a "tty" or "console" + /// window. If standard output is not connected to a tty or + /// console, or if the number of columns cannot be determined, + /// this routine returns zero. + static unsigned StandardOutColumns(); + + /// This function determines the number of columns in the window + /// if standard error is connected to a "tty" or "console" + /// window. If standard error is not connected to a tty or + /// console, or if the number of columns cannot be determined, + /// this routine returns zero. + static unsigned StandardErrColumns(); + + /// This function determines whether the terminal connected to standard + /// output supports colors. If standard output is not connected to a + /// terminal, this function returns false. + static bool StandardOutHasColors(); + + /// This function determines whether the terminal connected to standard + /// error supports colors. If standard error is not connected to a + /// terminal, this function returns false. + static bool StandardErrHasColors(); + + /// Enables or disables whether ANSI escape sequences are used to output + /// colors. This only has an effect on Windows. + /// Note: Setting this option is not thread-safe and should only be done + /// during initialization. + static void UseANSIEscapeCodes(bool enable); + + /// Whether changing colors requires the output to be flushed. + /// This is needed on systems that don't support escape sequences for + /// changing colors. + static bool ColorNeedsFlush(); + + /// This function returns the colorcode escape sequences. + /// If ColorNeedsFlush() is true then this function will change the colors + /// and return an empty escape sequence. In that case it is the + /// responsibility of the client to flush the output stream prior to + /// calling this function. + static const char *OutputColor(char c, bool bold, bool bg); + + /// Same as OutputColor, but only enables the bold attribute. + static const char *OutputBold(bool bg); + + /// This function returns the escape sequence to reverse forground and + /// background colors. + static const char *OutputReverse(); + + /// Resets the terminals colors, or returns an escape sequence to do so. + static const char *ResetColor(); + + /// Get the result of a process wide random number generator. The + /// generator will be automatically seeded in non-deterministic fashion. + static unsigned GetRandomNumber(); + + /// Equivalent to ::exit(), except when running inside a CrashRecoveryContext. + /// In that case, the control flow will resume after RunSafely(), like for a + /// crash, rather than exiting the current process. + /// Use \arg NoCleanup for calling _exit() instead of exit(). + [[noreturn]] static void Exit(int RetCode, bool NoCleanup = false); + +private: + [[noreturn]] static void ExitNoCleanup(int RetCode); +}; + +} +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Program.h b/contrib/libs/llvm16/include/llvm/Support/Program.h new file mode 100644 index 00000000000..9a464bd73fa --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Program.h @@ -0,0 +1,255 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Program.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::Program class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_PROGRAM_H +#define LLVM_SUPPORT_PROGRAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include <chrono> +#include <optional> +#include <system_error> + +namespace llvm { +class BitVector; +namespace sys { + + /// This is the OS-specific separator for PATH like environment variables: + // a colon on Unix or a semicolon on Windows. +#if defined(LLVM_ON_UNIX) + const char EnvPathSeparator = ':'; +#elif defined (_WIN32) + const char EnvPathSeparator = ';'; +#endif + +#if defined(_WIN32) + typedef unsigned long procid_t; // Must match the type of DWORD on Windows. + typedef void *process_t; // Must match the type of HANDLE on Windows. +#else + typedef ::pid_t procid_t; + typedef procid_t process_t; +#endif + + /// This struct encapsulates information about a process. + struct ProcessInfo { + enum : procid_t { InvalidPid = 0 }; + + procid_t Pid; /// The process identifier. + process_t Process; /// Platform-dependent process object. + + /// The return code, set after execution. + int ReturnCode; + + ProcessInfo(); + }; + + /// This struct encapsulates information about a process execution. + struct ProcessStatistics { + std::chrono::microseconds TotalTime; + std::chrono::microseconds UserTime; + uint64_t PeakMemory = 0; ///< Maximum resident set size in KiB. + }; + + /// Find the first executable file \p Name in \p Paths. + /// + /// This does not perform hashing as a shell would but instead stats each PATH + /// entry individually so should generally be avoided. Core LLVM library + /// functions and options should instead require fully specified paths. + /// + /// \param Name name of the executable to find. If it contains any system + /// slashes, it will be returned as is. + /// \param Paths optional list of paths to search for \p Name. If empty it + /// will use the system PATH environment instead. + /// + /// \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 = {}); + + // These functions change the specified standard stream (stdin or stdout) mode + // based on the Flags. They return errc::success if the specified stream was + // changed. Otherwise, a platform dependent error is returned. + std::error_code ChangeStdinMode(fs::OpenFlags Flags); + std::error_code ChangeStdoutMode(fs::OpenFlags Flags); + + // These functions change the specified standard stream (stdin or stdout) to + // binary mode. They return errc::success if the specified stream + // was changed. Otherwise a platform dependent error is returned. + std::error_code ChangeStdinToBinary(); + std::error_code ChangeStdoutToBinary(); + + /// This function executes the program using the arguments provided. The + /// invoked program will inherit the stdin, stdout, and stderr file + /// descriptors, the environment and other configuration settings of the + /// invoking program. + /// 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. + /// 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. + ArrayRef<StringRef> Args, ///< An array of strings that are passed to the + ///< program. The first element should be the name of the program. + ///< The array should **not** be terminated by an empty StringRef. + std::optional<ArrayRef<StringRef>> Env = + std::nullopt, ///< An optional vector of + ///< strings to use for the program's environment. If not provided, the + ///< current program's environment will be used. If specified, the + ///< vector should **not** be terminated by an empty StringRef. + ArrayRef<std::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 std::nullopt). + ///< 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 + ///< 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. + std::string *ErrMsg = nullptr, ///< If non-zero, provides a pointer to a + ///< string instance in which error messages will be returned. If the + ///< string is non-empty upon return an error occurred while invoking the + ///< program. + bool *ExecutionFailed = nullptr, + std::optional<ProcessStatistics> *ProcStat = nullptr, ///< If non-zero, + /// provides a pointer to a structure in which process execution + /// statistics will be stored. + BitVector *AffinityMask = nullptr ///< CPUs or processors the new + /// program shall run on. + ); + + /// Similar to ExecuteAndWait, but returns immediately. + /// @returns The \see ProcessInfo of the newly launched 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, ArrayRef<StringRef> Args, + std::optional<ArrayRef<StringRef>> Env, + ArrayRef<std::optional<StringRef>> Redirects = {}, + unsigned MemoryLimit = 0, + std::string *ErrMsg = nullptr, + bool *ExecutionFailed = nullptr, + BitVector *AffinityMask = nullptr); + + /// Return true if the given arguments fit within system-specific + /// argument length limits. + bool commandLineFitsWithinSystemLimits(StringRef Program, + ArrayRef<StringRef> Args); + + /// Return true if the given arguments fit within system-specific + /// argument length limits. + 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. + enum WindowsEncodingMethod { + /// UTF-8 is the LLVM native encoding, being the same as "do not perform + /// encoding conversion". + WEM_UTF8, + WEM_CurrentCodePage, + WEM_UTF16 + }; + + /// Saves the UTF8-encoded \p contents string into the file \p FileName + /// using a specific encoding. + /// + /// This write file function adds the possibility to choose which encoding + /// to use when writing a text file. On Windows, this is important when + /// writing files with internationalization support with an encoding that is + /// different from the one used in LLVM (UTF-8). We use this when writing + /// response files, since GCC tools on MinGW only understand legacy code + /// pages, and VisualStudio tools only understand UTF-16. + /// For UNIX, using different encodings is silently ignored, since all tools + /// work well with UTF-8. + /// This function assumes that you only use UTF-8 *text* data and will convert + /// it to your desired encoding before writing to the file. + /// + /// FIXME: We use EM_CurrentCodePage to write response files for GNU tools in + /// a MinGW/MinGW-w64 environment, which has serious flaws but currently is + /// our best shot to make gcc/ld understand international characters. This + /// should be changed as soon as binutils fix this to support UTF16 on mingw. + /// + /// \returns non-zero error_code if failed + std::error_code + writeFileWithEncoding(StringRef FileName, StringRef Contents, + WindowsEncodingMethod Encoding = WEM_UTF8); + + /// This function waits for the process specified by \p PI to finish. + /// \returns A \see ProcessInfo struct with Pid set to: + /// \li The process id of the child process if the child process has changed + /// state. + /// \li 0 if the child process has not changed state. + /// \note Users of this function should always check the ReturnCode member of + /// the \see ProcessInfo returned from this function. + ProcessInfo + Wait(const ProcessInfo &PI, ///< The child process that should be waited on. + std::optional<unsigned> SecondsToWait, ///< If std::nullopt, waits until + ///< child has terminated. + ///< If a value, this specifies the amount of time to wait for the child + ///< process. If the time expires, and \p Polling is false, the child is + ///< killed and this < function returns. If the time expires and \p + ///< Polling is true, the child is resumed. + ///< + ///< If zero, this function will perform a non-blocking + ///< wait on the child process. + std::string *ErrMsg = nullptr, ///< If non-zero, provides a pointer to a + ///< string instance in which error messages will be returned. If the + ///< string is non-empty upon return an error occurred while invoking the + ///< program. + std::optional<ProcessStatistics> *ProcStat = + nullptr, ///< If non-zero, provides + /// a pointer to a structure in which process execution statistics will + /// be stored. + + bool Polling = false ///< If true, do not kill the process on timeout. + ); + + /// Print a command argument, and optionally quote it. + void printArg(llvm::raw_ostream &OS, StringRef Arg, bool Quote); + +#if defined(_WIN32) + /// Given a list of command line arguments, quote and escape them as necessary + /// to build a single flat command line appropriate for calling CreateProcess + /// on + /// Windows. + ErrorOr<std::wstring> flattenWindowsCommandLine(ArrayRef<StringRef> Args); +#endif + } +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/RISCVAttributeParser.h b/contrib/libs/llvm16/include/llvm/Support/RISCVAttributeParser.h new file mode 100644 index 00000000000..c7b935f3dec --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/RISCVAttributeParser.h @@ -0,0 +1,48 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- RISCVAttributeParser.h - RISCV Attribute Parser ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RISCVATTRIBUTEPARSER_H +#define LLVM_SUPPORT_RISCVATTRIBUTEPARSER_H + +#include "llvm/Support/ELFAttributeParser.h" +#include "llvm/Support/RISCVAttributes.h" + +namespace llvm { +class RISCVAttributeParser : public ELFAttributeParser { + struct DisplayHandler { + RISCVAttrs::AttrType attribute; + Error (RISCVAttributeParser::*routine)(unsigned); + }; + static const DisplayHandler displayRoutines[]; + + Error handler(uint64_t tag, bool &handled) override; + + Error unalignedAccess(unsigned tag); + Error stackAlign(unsigned tag); + +public: + RISCVAttributeParser(ScopedPrinter *sw) + : ELFAttributeParser(sw, RISCVAttrs::getRISCVAttributeTags(), "riscv") {} + RISCVAttributeParser() + : ELFAttributeParser(RISCVAttrs::getRISCVAttributeTags(), "riscv") {} +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/RISCVAttributes.h b/contrib/libs/llvm16/include/llvm/Support/RISCVAttributes.h new file mode 100644 index 00000000000..0fc2a0047d5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/RISCVAttributes.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- RISCVAttributes.h - RISCV Attributes --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains enumerations for RISCV attributes as defined in RISC-V +// ELF psABI specification. +// +// RISC-V ELF psABI specification +// +// https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_RISCVATTRIBUTES_H +#define LLVM_SUPPORT_RISCVATTRIBUTES_H + +#include "llvm/Support/ELFAttributes.h" + +namespace llvm { +namespace RISCVAttrs { + +const TagNameMap &getRISCVAttributeTags(); + +enum AttrType : unsigned { + // Attribute types in ELF/.riscv.attributes. + STACK_ALIGN = 4, + ARCH = 5, + UNALIGNED_ACCESS = 6, + PRIV_SPEC = 8, + PRIV_SPEC_MINOR = 10, + PRIV_SPEC_REVISION = 12, +}; + +enum StackAlign { ALIGN_4 = 4, ALIGN_16 = 16 }; + +enum { NOT_ALLOWED = 0, ALLOWED = 1 }; + +} // namespace RISCVAttrs +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/RISCVISAInfo.h b/contrib/libs/llvm16/include/llvm/Support/RISCVISAInfo.h new file mode 100644 index 00000000000..9a23501816d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/RISCVISAInfo.h @@ -0,0 +1,126 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- RISCVISAInfo.h - RISCV ISA Information ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RISCVISAINFO_H +#define LLVM_SUPPORT_RISCVISAINFO_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include <map> +#include <string> +#include <vector> + +namespace llvm { +struct RISCVExtensionInfo { + std::string ExtName; + unsigned MajorVersion; + unsigned MinorVersion; +}; + +class RISCVISAInfo { +public: + RISCVISAInfo(const RISCVISAInfo &) = delete; + RISCVISAInfo &operator=(const RISCVISAInfo &) = delete; + + static bool compareExtension(const std::string &LHS, const std::string &RHS); + + /// Helper class for OrderedExtensionMap. + struct ExtensionComparator { + bool operator()(const std::string &LHS, const std::string &RHS) const { + return compareExtension(LHS, RHS); + } + }; + + /// OrderedExtensionMap is std::map, it's specialized to keep entries + /// in canonical order of extension. + typedef std::map<std::string, RISCVExtensionInfo, ExtensionComparator> + OrderedExtensionMap; + + RISCVISAInfo(unsigned XLen, OrderedExtensionMap &Exts) + : XLen(XLen), FLen(0), MinVLen(0), MaxELen(0), MaxELenFp(0), Exts(Exts) {} + + /// Parse RISCV ISA info from arch string. + static llvm::Expected<std::unique_ptr<RISCVISAInfo>> + parseArchString(StringRef Arch, bool EnableExperimentalExtension, + bool ExperimentalExtensionVersionCheck = true, + bool IgnoreUnknown = false); + + /// Parse RISCV ISA info from an arch string that is already in normalized + /// form (as defined in the psABI). Unlike parseArchString, this function + /// will not error for unrecognized extension names or extension versions. + static llvm::Expected<std::unique_ptr<RISCVISAInfo>> + parseNormalizedArchString(StringRef Arch); + + /// Parse RISCV ISA info from feature vector. + static llvm::Expected<std::unique_ptr<RISCVISAInfo>> + parseFeatures(unsigned XLen, const std::vector<std::string> &Features); + + /// Convert RISCV ISA info to a feature vector. + void toFeatures(std::vector<StringRef> &Features, + llvm::function_ref<StringRef(const Twine &)> StrAlloc, + bool AddAllExtensions) const; + + const OrderedExtensionMap &getExtensions() const { return Exts; }; + + unsigned getXLen() const { return XLen; }; + unsigned getFLen() const { return FLen; }; + unsigned getMinVLen() const { return MinVLen; } + unsigned getMaxVLen() const { return 65536; } + unsigned getMaxELen() const { return MaxELen; } + unsigned getMaxELenFp() const { return MaxELenFp; } + + bool hasExtension(StringRef Ext) const; + std::string toString() const; + std::vector<std::string> toFeatureVector() const; + StringRef computeDefaultABI() const; + + static bool isSupportedExtensionFeature(StringRef Ext); + static bool isSupportedExtension(StringRef Ext); + static bool isSupportedExtension(StringRef Ext, unsigned MajorVersion, + unsigned MinorVersion); + static llvm::Expected<std::unique_ptr<RISCVISAInfo>> + postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo); + +private: + RISCVISAInfo(unsigned XLen) + : XLen(XLen), FLen(0), MinVLen(0), MaxELen(0), MaxELenFp(0) {} + + unsigned XLen; + unsigned FLen; + unsigned MinVLen; + unsigned MaxELen, MaxELenFp; + + OrderedExtensionMap Exts; + + void addExtension(StringRef ExtName, unsigned MajorVersion, + unsigned MinorVersion); + + Error checkDependency(); + + void updateImplication(); + void updateCombination(); + void updateFLen(); + void updateMinVLen(); + void updateMaxELen(); +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/RWMutex.h b/contrib/libs/llvm16/include/llvm/Support/RWMutex.h new file mode 100644 index 00000000000..8468e76b54b --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/RWMutex.h @@ -0,0 +1,206 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- RWMutex.h - Reader/Writer Mutual Exclusion Lock ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::RWMutex class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RWMUTEX_H +#define LLVM_SUPPORT_RWMUTEX_H + +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Threading.h" +#include <cassert> +#include <mutex> +#include <shared_mutex> + +// std::shared_timed_mutex is only availble on macOS 10.12 and later. +#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200 +#define LLVM_USE_RW_MUTEX_IMPL +#endif +#endif + +namespace llvm { +namespace sys { + +#if defined(LLVM_USE_RW_MUTEX_IMPL) +/// Platform agnostic RWMutex class. +class RWMutexImpl { + /// @name Constructors + /// @{ +public: + /// Initializes the lock but doesn't acquire it. + /// Default Constructor. + explicit RWMutexImpl(); + + /// @} + /// @name Do Not Implement + /// @{ + RWMutexImpl(const RWMutexImpl &original) = delete; + RWMutexImpl &operator=(const RWMutexImpl &) = delete; + /// @} + + /// Releases and removes the lock + /// Destructor + ~RWMutexImpl(); + + /// @} + /// @name Methods + /// @{ +public: + /// Attempts to unconditionally acquire the lock in reader mode. If the + /// lock is held by a writer, this method will wait until it can acquire + /// the lock. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally acquire the lock in reader mode. + bool lock_shared(); + + /// Attempts to release the lock in reader mode. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally release the lock in reader mode. + bool unlock_shared(); + + /// Attempts to unconditionally acquire the lock in reader mode. If the + /// lock is held by any readers, this method will wait until it can + /// acquire the lock. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally acquire the lock in writer mode. + bool lock(); + + /// Attempts to release the lock in writer mode. + /// @returns false if any kind of error occurs, true otherwise. + /// Unconditionally release the lock in write mode. + bool unlock(); + + //@} + /// @name Platform Dependent Data + /// @{ +private: +#if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0 + void *data_ = nullptr; ///< We don't know what the data will be +#endif +}; +#endif + +/// SmartMutex - An R/W mutex with a compile time constant parameter that +/// indicates whether this mutex should become a no-op when we're not +/// running in multithreaded mode. +template <bool mt_only> class SmartRWMutex { +#if !defined(LLVM_USE_RW_MUTEX_IMPL) + std::shared_mutex impl; +#else + RWMutexImpl impl; +#endif + unsigned readers = 0; + unsigned writers = 0; + +public: + bool lock_shared() { + if (!mt_only || llvm_is_multithreaded()) { + impl.lock_shared(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not basic checks in single threaded mode. + ++readers; + return true; + } + + bool unlock_shared() { + if (!mt_only || llvm_is_multithreaded()) { + impl.unlock_shared(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not basic checks in single threaded mode. + assert(readers > 0 && "Reader lock not acquired before release!"); + --readers; + return true; + } + + bool lock() { + if (!mt_only || llvm_is_multithreaded()) { + impl.lock(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not basic checks in single threaded mode. + assert(writers == 0 && "Writer lock already acquired!"); + ++writers; + return true; + } + + bool unlock() { + if (!mt_only || llvm_is_multithreaded()) { + impl.unlock(); + return true; + } + + // Single-threaded debugging code. This would be racy in multithreaded + // mode, but provides not basic checks in single threaded mode. + assert(writers == 1 && "Writer lock not acquired before release!"); + --writers; + return true; + } +}; + +typedef SmartRWMutex<false> RWMutex; + +/// ScopedReader - RAII acquisition of a reader lock +#if !defined(LLVM_USE_RW_MUTEX_IMPL) +template <bool mt_only> +using SmartScopedReader = const std::shared_lock<SmartRWMutex<mt_only>>; +#else +template <bool mt_only> struct SmartScopedReader { + SmartRWMutex<mt_only> &mutex; + + explicit SmartScopedReader(SmartRWMutex<mt_only> &m) : mutex(m) { + mutex.lock_shared(); + } + + ~SmartScopedReader() { mutex.unlock_shared(); } +}; +#endif +typedef SmartScopedReader<false> ScopedReader; + +/// ScopedWriter - RAII acquisition of a writer lock +#if !defined(LLVM_USE_RW_MUTEX_IMPL) +template <bool mt_only> +using SmartScopedWriter = std::lock_guard<SmartRWMutex<mt_only>>; +#else +template <bool mt_only> struct SmartScopedWriter { + SmartRWMutex<mt_only> &mutex; + + explicit SmartScopedWriter(SmartRWMutex<mt_only> &m) : mutex(m) { + mutex.lock(); + } + + ~SmartScopedWriter() { mutex.unlock(); } +}; +#endif +typedef SmartScopedWriter<false> ScopedWriter; + +} // end namespace sys +} // end namespace llvm + +#endif // LLVM_SUPPORT_RWMUTEX_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/RandomNumberGenerator.h b/contrib/libs/llvm16/include/llvm/Support/RandomNumberGenerator.h new file mode 100644 index 00000000000..28bab7133c4 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/RandomNumberGenerator.h @@ -0,0 +1,80 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==- llvm/Support/RandomNumberGenerator.h - RNG for diversity ---*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines an abstraction for deterministic random number +// generation (RNG). Note that the current implementation is not +// cryptographically secure as it uses the C++11 <random> facilities. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RANDOMNUMBERGENERATOR_H_ +#define LLVM_SUPPORT_RANDOMNUMBERGENERATOR_H_ + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataTypes.h" // Needed for uint64_t on Windows. +#include <random> +#include <system_error> + +namespace llvm { +class StringRef; + +/// A random number generator. +/// +/// Instances of this class should not be shared across threads. The +/// seed should be set by passing the -rng-seed=<uint64> option. Use +/// Module::createRNG to create a new RNG instance for use with that +/// module. +class RandomNumberGenerator { + + // 64-bit Mersenne Twister by Matsumoto and Nishimura, 2000 + // http://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine + // This RNG is deterministically portable across C++11 + // implementations. + using generator_type = std::mt19937_64; + +public: + using result_type = generator_type::result_type; + + /// Returns a random number in the range [0, Max). + result_type operator()(); + + static constexpr result_type min() { return generator_type::min(); } + static constexpr result_type max() { return generator_type::max(); } + +private: + /// Seeds and salts the underlying RNG engine. + /// + /// This constructor should not be used directly. Instead use + /// Module::createRNG to create a new RNG salted with the Module ID. + RandomNumberGenerator(StringRef Salt); + + generator_type Generator; + + // Noncopyable. + RandomNumberGenerator(const RandomNumberGenerator &other) = delete; + RandomNumberGenerator &operator=(const RandomNumberGenerator &other) = delete; + + friend class Module; +}; + +// Get random vector of specified size +std::error_code getRandomBytes(void *Buffer, size_t Size); +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Recycler.h b/contrib/libs/llvm16/include/llvm/Support/Recycler.h new file mode 100644 index 00000000000..7a9bcb29a8f --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Recycler.h @@ -0,0 +1,126 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==- llvm/Support/Recycler.h - Recycling Allocator --------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the Recycler class template. See the doxygen comment for +// Recycler for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RECYCLER_H +#define LLVM_SUPPORT_RECYCLER_H + +#include "llvm/ADT/ilist.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> + +namespace llvm { + +/// PrintRecyclingAllocatorStats - Helper for RecyclingAllocator for +/// printing statistics. +/// +void PrintRecyclerStats(size_t Size, size_t Align, size_t FreeListSize); + +/// Recycler - This class manages a linked-list of deallocated nodes +/// and facilitates reusing deallocated memory in place of allocating +/// new memory. +/// +template <class T, size_t Size = sizeof(T), size_t Align = alignof(T)> +class Recycler { + struct FreeNode { + FreeNode *Next; + }; + + /// List of nodes that have deleted contents and are not in active use. + FreeNode *FreeList = nullptr; + + FreeNode *pop_val() { + auto *Val = FreeList; + __asan_unpoison_memory_region(Val, Size); + FreeList = FreeList->Next; + __msan_allocated_memory(Val, Size); + return Val; + } + + void push(FreeNode *N) { + N->Next = FreeList; + FreeList = N; + __asan_poison_memory_region(N, Size); + } + +public: + ~Recycler() { + // If this fails, either the callee has lost track of some allocation, + // or the callee isn't tracking allocations and should just call + // clear() before deleting the Recycler. + assert(!FreeList && "Non-empty recycler deleted!"); + } + + /// clear - Release all the tracked allocations to the allocator. The + /// recycler must be free of any tracked allocations before being + /// deleted; calling clear is one way to ensure this. + template<class AllocatorType> + void clear(AllocatorType &Allocator) { + while (FreeList) { + T *t = reinterpret_cast<T *>(pop_val()); + Allocator.Deallocate(t); + } + } + + /// Special case for BumpPtrAllocator which has an empty Deallocate() + /// function. + /// + /// There is no need to traverse the free list, pulling all the objects into + /// cache. + void clear(BumpPtrAllocator &) { FreeList = nullptr; } + + template<class SubClass, class AllocatorType> + SubClass *Allocate(AllocatorType &Allocator) { + static_assert(alignof(SubClass) <= Align, + "Recycler allocation alignment is less than object align!"); + static_assert(sizeof(SubClass) <= Size, + "Recycler allocation size is less than object size!"); + return FreeList ? reinterpret_cast<SubClass *>(pop_val()) + : static_cast<SubClass *>(Allocator.Allocate(Size, Align)); + } + + template<class AllocatorType> + T *Allocate(AllocatorType &Allocator) { + return Allocate<T>(Allocator); + } + + template<class SubClass, class AllocatorType> + void Deallocate(AllocatorType & /*Allocator*/, SubClass* Element) { + push(reinterpret_cast<FreeNode *>(Element)); + } + + void PrintStats(); +}; + +template <class T, size_t Size, size_t Align> +void Recycler<T, Size, Align>::PrintStats() { + size_t S = 0; + for (auto *I = FreeList; I; I = I->Next) + ++S; + PrintRecyclerStats(Size, Align, S); +} + +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/RecyclingAllocator.h b/contrib/libs/llvm16/include/llvm/Support/RecyclingAllocator.h new file mode 100644 index 00000000000..c683de3ffab --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/RecyclingAllocator.h @@ -0,0 +1,87 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==- llvm/Support/RecyclingAllocator.h - Recycling Allocator ----*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the RecyclingAllocator class. See the doxygen comment for +// RecyclingAllocator for more details on the implementation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RECYCLINGALLOCATOR_H +#define LLVM_SUPPORT_RECYCLINGALLOCATOR_H + +#include "llvm/Support/Recycler.h" + +namespace llvm { + +/// RecyclingAllocator - This class wraps an Allocator, adding the +/// functionality of recycling deleted objects. +/// +template <class AllocatorType, class T, size_t Size = sizeof(T), + size_t Align = alignof(T)> +class RecyclingAllocator { +private: + /// Base - Implementation details. + /// + Recycler<T, Size, Align> Base; + + /// Allocator - The wrapped allocator. + /// + AllocatorType Allocator; + +public: + ~RecyclingAllocator() { Base.clear(Allocator); } + + /// Allocate - Return a pointer to storage for an object of type + /// SubClass. The storage may be either newly allocated or recycled. + /// + template<class SubClass> + SubClass *Allocate() { return Base.template Allocate<SubClass>(Allocator); } + + T *Allocate() { return Base.Allocate(Allocator); } + + /// Deallocate - Release storage for the pointed-to object. The + /// storage will be kept track of and may be recycled. + /// + template<class SubClass> + void Deallocate(SubClass* E) { return Base.Deallocate(Allocator, E); } + + void PrintStats() { + Allocator.PrintStats(); + Base.PrintStats(); + } +}; + +} + +template<class AllocatorType, class T, size_t Size, size_t Align> +inline void *operator new(size_t size, + llvm::RecyclingAllocator<AllocatorType, + T, Size, Align> &Allocator) { + assert(size <= Size && "allocation size exceeded"); + return Allocator.Allocate(); +} + +template<class AllocatorType, class T, size_t Size, size_t Align> +inline void operator delete(void *E, + llvm::RecyclingAllocator<AllocatorType, + T, Size, Align> &A) { + A.Deallocate(E); +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Regex.h b/contrib/libs/llvm16/include/llvm/Support/Regex.h new file mode 100644 index 00000000000..70387ec7f5e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Regex.h @@ -0,0 +1,124 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- Regex.h - Regular Expression matcher implementation -*- C++ -*-----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a POSIX regular expression matcher. Both Basic and +// Extended POSIX regular expressions (ERE) are supported. EREs were extended +// to support backreferences in matches. +// This implementation also supports matching strings with embedded NUL chars. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_REGEX_H +#define LLVM_SUPPORT_REGEX_H + +#include "llvm/ADT/BitmaskEnum.h" +#include <string> + +struct llvm_regex; + +namespace llvm { + class StringRef; + template<typename T> class SmallVectorImpl; + + class Regex { + public: + enum RegexFlags : unsigned { + NoFlags = 0, + /// Compile for matching that ignores upper/lower case distinctions. + IgnoreCase = 1, + /// Compile for newline-sensitive matching. With this flag '[^' bracket + /// expressions and '.' never match newline. A ^ anchor matches the + /// null string after any newline in the string in addition to its normal + /// function, and the $ anchor matches the null string before any + /// newline in the string in addition to its normal function. + Newline = 2, + /// By default, the POSIX extended regular expression (ERE) syntax is + /// assumed. Pass this flag to turn on basic regular expressions (BRE) + /// instead. + BasicRegex = 4, + + LLVM_MARK_AS_BITMASK_ENUM(BasicRegex) + }; + + Regex(); + /// Compiles the given regular expression \p Regex. + /// + /// \param Regex - referenced string is no longer needed after this + /// constructor does finish. Only its compiled form is kept stored. + Regex(StringRef Regex, RegexFlags Flags = NoFlags); + Regex(StringRef Regex, unsigned Flags); + Regex(const Regex &) = delete; + Regex &operator=(Regex regex) { + std::swap(preg, regex.preg); + std::swap(error, regex.error); + return *this; + } + Regex(Regex &®ex); + ~Regex(); + + /// isValid - returns the error encountered during regex compilation, if + /// any. + bool isValid(std::string &Error) const; + bool isValid() const { return !error; } + + /// getNumMatches - In a valid regex, return the number of parenthesized + /// matches it contains. The number filled in by match will include this + /// many entries plus one for the whole regex (as element 0). + unsigned getNumMatches() const; + + /// matches - Match the regex against a given \p String. + /// + /// \param Matches - If given, on a successful match this will be filled in + /// with references to the matched group expressions (inside \p String), + /// the first group is always the entire pattern. + /// + /// \param Error - If non-null, any errors in the matching will be recorded + /// as a non-empty string. If there is no error, it will be an empty string. + /// + /// This returns true on a successful match. + bool match(StringRef String, SmallVectorImpl<StringRef> *Matches = nullptr, + std::string *Error = nullptr) const; + + /// sub - Return the result of replacing the first match of the regex in + /// \p String with the \p Repl string. Backreferences like "\0" in the + /// replacement string are replaced with the appropriate match substring. + /// + /// Note that the replacement string has backslash escaping performed on + /// it. Invalid backreferences are ignored (replaced by empty strings). + /// + /// \param Error If non-null, any errors in the substitution (invalid + /// backreferences, trailing backslashes) will be recorded as a non-empty + /// string. If there is no error, it will be an empty string. + std::string sub(StringRef Repl, StringRef String, + std::string *Error = nullptr) const; + + /// If this function returns true, ^Str$ is an extended regular + /// expression that matches Str and only Str. + static bool isLiteralERE(StringRef Str); + + /// Turn String into a regex by escaping its special characters. + static std::string escape(StringRef String); + + private: + struct llvm_regex *preg; + int error; + }; +} + +#endif // LLVM_SUPPORT_REGEX_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Registry.h b/contrib/libs/llvm16/include/llvm/Support/Registry.h new file mode 100644 index 00000000000..720f3528566 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Registry.h @@ -0,0 +1,170 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//=== Registry.h - Linker-supported plugin registries -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines a registry template for discovering pluggable modules. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_REGISTRY_H +#define LLVM_SUPPORT_REGISTRY_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DynamicLibrary.h" +#include <memory> + +namespace llvm { + /// A simple registry entry which provides only a name, description, and + /// no-argument constructor. + template <typename T> + class SimpleRegistryEntry { + StringRef Name, Desc; + std::unique_ptr<T> (*Ctor)(); + + public: + SimpleRegistryEntry(StringRef N, StringRef D, std::unique_ptr<T> (*C)()) + : Name(N), Desc(D), Ctor(C) {} + + StringRef getName() const { return Name; } + StringRef getDesc() const { return Desc; } + std::unique_ptr<T> instantiate() const { return Ctor(); } + }; + + /// A global registry used in conjunction with static constructors to make + /// pluggable components (like targets or garbage collectors) "just work" when + /// linked with an executable. + template <typename T> + class Registry { + public: + typedef T type; + typedef SimpleRegistryEntry<T> entry; + + class node; + class iterator; + + private: + Registry() = delete; + + friend class node; + static node *Head, *Tail; + + public: + /// Node in linked list of entries. + /// + class node { + friend class iterator; + friend Registry<T>; + + node *Next; + const entry& Val; + + public: + node(const entry &V) : Next(nullptr), Val(V) {} + }; + + /// Add a node to the Registry: this is the interface between the plugin and + /// the executable. + /// + /// This function is exported by the executable and called by the plugin to + /// add a node to the executable's registry. Therefore it's not defined here + /// to avoid it being instantiated in the plugin and is instead defined in + /// the executable (see LLVM_INSTANTIATE_REGISTRY below). + static void add_node(node *N); + + /// Iterators for registry entries. + /// + class iterator + : public llvm::iterator_facade_base<iterator, std::forward_iterator_tag, + const entry> { + const node *Cur; + + public: + explicit iterator(const node *N) : Cur(N) {} + + bool operator==(const iterator &That) const { return Cur == That.Cur; } + iterator &operator++() { Cur = Cur->Next; return *this; } + const entry &operator*() const { return Cur->Val; } + }; + + // begin is not defined here in order to avoid usage of an undefined static + // data member, instead it's instantiated by LLVM_INSTANTIATE_REGISTRY. + static iterator begin(); + static iterator end() { return iterator(nullptr); } + + static iterator_range<iterator> entries() { + return make_range(begin(), end()); + } + + /// A static registration template. Use like such: + /// + /// Registry<Collector>::Add<FancyGC> + /// X("fancy-gc", "Newfangled garbage collector."); + /// + /// Use of this template requires that: + /// + /// 1. The registered subclass has a default constructor. + template <typename V> + class Add { + entry Entry; + node Node; + + static std::unique_ptr<T> CtorFn() { return std::make_unique<V>(); } + + public: + Add(StringRef Name, StringRef Desc) + : Entry(Name, Desc, CtorFn), Node(Entry) { + add_node(&Node); + } + }; + }; +} // end namespace llvm + +/// Instantiate a registry class. +/// +/// This provides template definitions of add_node, begin, and the Head and Tail +/// pointers, then explicitly instantiates them. We could explicitly specialize +/// them, instead of the two-step process of define then instantiate, but +/// strictly speaking that's not allowed by the C++ standard (we would need to +/// have explicit specialization declarations in all translation units where the +/// specialization is used) so we don't. +#define LLVM_INSTANTIATE_REGISTRY(REGISTRY_CLASS) \ + namespace llvm { \ + template<typename T> typename Registry<T>::node *Registry<T>::Head = nullptr;\ + template<typename T> typename Registry<T>::node *Registry<T>::Tail = nullptr;\ + template<typename T> \ + void Registry<T>::add_node(typename Registry<T>::node *N) { \ + if (Tail) \ + Tail->Next = N; \ + else \ + Head = N; \ + Tail = N; \ + } \ + template<typename T> typename Registry<T>::iterator Registry<T>::begin() { \ + return iterator(Head); \ + } \ + template REGISTRY_CLASS::node *Registry<REGISTRY_CLASS::type>::Head; \ + template REGISTRY_CLASS::node *Registry<REGISTRY_CLASS::type>::Tail; \ + template \ + void Registry<REGISTRY_CLASS::type>::add_node(REGISTRY_CLASS::node*); \ + template REGISTRY_CLASS::iterator Registry<REGISTRY_CLASS::type>::begin(); \ + } + +#endif // LLVM_SUPPORT_REGISTRY_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ReverseIteration.h b/contrib/libs/llvm16/include/llvm/Support/ReverseIteration.h new file mode 100644 index 00000000000..cac20c480c6 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ReverseIteration.h @@ -0,0 +1,30 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#ifndef LLVM_SUPPORT_REVERSEITERATION_H +#define LLVM_SUPPORT_REVERSEITERATION_H + +#include "llvm/Config/abi-breaking.h" +#include "llvm/Support/PointerLikeTypeTraits.h" + +namespace llvm { + +template<class T = void *> +bool shouldReverseIterate() { +#if LLVM_ENABLE_REVERSE_ITERATION + return detail::IsPointerLike<T>::value; +#else + return false; +#endif +} + +} +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SHA1.h b/contrib/libs/llvm16/include/llvm/Support/SHA1.h new file mode 100644 index 00000000000..5c90359e80e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SHA1.h @@ -0,0 +1,93 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//==- SHA1.h - SHA1 implementation for LLVM --*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by wrapping it in a C++ interface for LLVM, +// and removing unnecessary code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SHA1_H +#define LLVM_SUPPORT_SHA1_H + +#include <array> +#include <cstdint> + +namespace llvm { +template <typename T> class ArrayRef; +class StringRef; + +/// A class that wrap the SHA1 algorithm. +class SHA1 { +public: + SHA1() { init(); } + + /// Reinitialize the internal state + void init(); + + /// Digest more data. + void update(ArrayRef<uint8_t> Data); + + /// Digest more data. + void update(StringRef Str); + + /// Return the current raw 160-bits SHA1 for the digested data + /// since the last call to init(). This call will add data to the internal + /// state and as such is not suited for getting an intermediate result + /// (see result()). + std::array<uint8_t, 20> final(); + + /// Return the current raw 160-bits SHA1 for the digested data + /// since the last call to init(). This is suitable for getting the SHA1 at + /// any time without invalidating the internal state so that more calls can be + /// made into update. + std::array<uint8_t, 20> result(); + + /// Returns a raw 160-bit SHA1 hash for the given data. + static std::array<uint8_t, 20> hash(ArrayRef<uint8_t> Data); + +private: + /// Define some constants. + /// "static constexpr" would be cleaner but MSVC does not support it yet. + enum { BLOCK_LENGTH = 64 }; + enum { HASH_LENGTH = 20 }; + + // Internal State + struct { + union { + uint8_t C[BLOCK_LENGTH]; + uint32_t L[BLOCK_LENGTH / 4]; + } Buffer; + uint32_t State[HASH_LENGTH / 4]; + uint32_t ByteCount; + uint8_t BufferOffset; + } InternalState; + + // Helper + void writebyte(uint8_t data); + void hashBlock(); + void addUncounted(uint8_t data); + void pad(); + + void final(std::array<uint32_t, HASH_LENGTH / 4> &HashResult); +}; + +} // end llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SHA256.h b/contrib/libs/llvm16/include/llvm/Support/SHA256.h new file mode 100644 index 00000000000..25ff8212376 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SHA256.h @@ -0,0 +1,100 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//====- SHA256.cpp - SHA256 implementation ---*- C++ -* ======// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + * + * The implementation is based on nacl's sha256 implementation [0] and LLVM's + * pre-exsiting SHA1 code [1]. + * + * [0] https://hyperelliptic.org/nacl/nacl-20110221.tar.bz2 (public domain + * code) + * [1] llvm/lib/Support/SHA1.{h,cpp} + */ +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SHA256_H +#define LLVM_SUPPORT_SHA256_H + +#include <array> +#include <cstdint> + +namespace llvm { + +template <typename T> class ArrayRef; +class StringRef; + +class SHA256 { +public: + explicit SHA256() { init(); } + + /// Reinitialize the internal state + void init(); + + /// Digest more data. + void update(ArrayRef<uint8_t> Data); + + /// Digest more data. + void update(StringRef Str); + + /// Return the current raw 256-bits SHA256 for the digested + /// data since the last call to init(). This call will add data to the + /// internal state and as such is not suited for getting an intermediate + /// result (see result()). + std::array<uint8_t, 32> final(); + + /// Return the current raw 256-bits SHA256 for the digested + /// data since the last call to init(). This is suitable for getting the + /// SHA256 at any time without invalidating the internal state so that more + /// calls can be made into update. + std::array<uint8_t, 32> result(); + + /// Returns a raw 256-bit SHA256 hash for the given data. + static std::array<uint8_t, 32> hash(ArrayRef<uint8_t> Data); + +private: + /// Define some constants. + /// "static constexpr" would be cleaner but MSVC does not support it yet. + enum { BLOCK_LENGTH = 64 }; + enum { HASH_LENGTH = 32 }; + + // Internal State + struct { + union { + uint8_t C[BLOCK_LENGTH]; + uint32_t L[BLOCK_LENGTH / 4]; + } Buffer; + uint32_t State[HASH_LENGTH / 4]; + uint32_t ByteCount; + uint8_t BufferOffset; + } InternalState; + + // Helper + void writebyte(uint8_t data); + void hashBlock(); + void addUncounted(uint8_t data); + void pad(); + + void final(std::array<uint32_t, HASH_LENGTH / 4> &HashResult); +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_SHA256_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SMLoc.h b/contrib/libs/llvm16/include/llvm/Support/SMLoc.h new file mode 100644 index 00000000000..d91f25afee5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SMLoc.h @@ -0,0 +1,75 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SMLoc.h - Source location for use with diagnostics -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the SMLoc class. This class encapsulates a location in +// source code for use in diagnostics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SMLOC_H +#define LLVM_SUPPORT_SMLOC_H + +#include <cassert> +#include <optional> + +namespace llvm { + +/// Represents a location in source code. +class SMLoc { + const char *Ptr = nullptr; + +public: + SMLoc() = default; + + bool isValid() const { return Ptr != nullptr; } + + bool operator==(const SMLoc &RHS) const { return RHS.Ptr == Ptr; } + bool operator!=(const SMLoc &RHS) const { return RHS.Ptr != Ptr; } + + const char *getPointer() const { return Ptr; } + + static SMLoc getFromPointer(const char *Ptr) { + SMLoc L; + L.Ptr = Ptr; + return L; + } +}; + +/// Represents a range in source code. +/// +/// SMRange is implemented using a half-open range, as is the convention in C++. +/// In the string "abc", the range [1,3) represents the substring "bc", and the +/// range [2,2) represents an empty range between the characters "b" and "c". +class SMRange { +public: + SMLoc Start, End; + + SMRange() = default; + SMRange(std::nullopt_t) {} + SMRange(SMLoc St, SMLoc En) : Start(St), End(En) { + assert(Start.isValid() == End.isValid() && + "Start and End should either both be valid or both be invalid!"); + } + + bool isValid() const { return Start.isValid(); } +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_SMLOC_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SMTAPI.h b/contrib/libs/llvm16/include/llvm/Support/SMTAPI.h new file mode 100644 index 00000000000..55699d26442 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SMTAPI.h @@ -0,0 +1,458 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SMTAPI.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a SMT generic Solver API, which will be the base class +// for every SMT solver specific class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SMTAPI_H +#define LLVM_SUPPORT_SMTAPI_H + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +namespace llvm { + +/// Generic base class for SMT sorts +class SMTSort { +public: + SMTSort() = default; + virtual ~SMTSort() = default; + + /// Returns true if the sort is a bitvector, calls isBitvectorSortImpl(). + virtual bool isBitvectorSort() const { return isBitvectorSortImpl(); } + + /// Returns true if the sort is a floating-point, calls isFloatSortImpl(). + virtual bool isFloatSort() const { return isFloatSortImpl(); } + + /// Returns true if the sort is a boolean, calls isBooleanSortImpl(). + virtual bool isBooleanSort() const { return isBooleanSortImpl(); } + + /// Returns the bitvector size, fails if the sort is not a bitvector + /// Calls getBitvectorSortSizeImpl(). + virtual unsigned getBitvectorSortSize() const { + assert(isBitvectorSort() && "Not a bitvector sort!"); + unsigned Size = getBitvectorSortSizeImpl(); + assert(Size && "Size is zero!"); + return Size; + }; + + /// Returns the floating-point size, fails if the sort is not a floating-point + /// Calls getFloatSortSizeImpl(). + virtual unsigned getFloatSortSize() const { + assert(isFloatSort() && "Not a floating-point sort!"); + unsigned Size = getFloatSortSizeImpl(); + assert(Size && "Size is zero!"); + return Size; + }; + + virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; + + bool operator<(const SMTSort &Other) const { + llvm::FoldingSetNodeID ID1, ID2; + Profile(ID1); + Other.Profile(ID2); + return ID1 < ID2; + } + + friend bool operator==(SMTSort const &LHS, SMTSort const &RHS) { + return LHS.equal_to(RHS); + } + + virtual void print(raw_ostream &OS) const = 0; + + LLVM_DUMP_METHOD void dump() const; + +protected: + /// Query the SMT solver and returns true if two sorts are equal (same kind + /// and bit width). This does not check if the two sorts are the same objects. + virtual bool equal_to(SMTSort const &other) const = 0; + + /// Query the SMT solver and checks if a sort is bitvector. + virtual bool isBitvectorSortImpl() const = 0; + + /// Query the SMT solver and checks if a sort is floating-point. + virtual bool isFloatSortImpl() const = 0; + + /// Query the SMT solver and checks if a sort is boolean. + virtual bool isBooleanSortImpl() const = 0; + + /// Query the SMT solver and returns the sort bit width. + virtual unsigned getBitvectorSortSizeImpl() const = 0; + + /// Query the SMT solver and returns the sort bit width. + virtual unsigned getFloatSortSizeImpl() const = 0; +}; + +/// Shared pointer for SMTSorts, used by SMTSolver API. +using SMTSortRef = const SMTSort *; + +/// Generic base class for SMT exprs +class SMTExpr { +public: + SMTExpr() = default; + virtual ~SMTExpr() = default; + + bool operator<(const SMTExpr &Other) const { + llvm::FoldingSetNodeID ID1, ID2; + Profile(ID1); + Other.Profile(ID2); + return ID1 < ID2; + } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; + + friend bool operator==(SMTExpr const &LHS, SMTExpr const &RHS) { + return LHS.equal_to(RHS); + } + + virtual void print(raw_ostream &OS) const = 0; + + LLVM_DUMP_METHOD void dump() const; + +protected: + /// Query the SMT solver and returns true if two sorts are equal (same kind + /// and bit width). This does not check if the two sorts are the same objects. + virtual bool equal_to(SMTExpr const &other) const = 0; +}; + +/// Shared pointer for SMTExprs, used by SMTSolver API. +using SMTExprRef = const SMTExpr *; + +/// Generic base class for SMT Solvers +/// +/// This class is responsible for wrapping all sorts and expression generation, +/// through the mk* methods. It also provides methods to create SMT expressions +/// straight from clang's AST, through the from* methods. +class SMTSolver { +public: + SMTSolver() = default; + virtual ~SMTSolver() = default; + + LLVM_DUMP_METHOD void dump() const; + + // Returns an appropriate floating-point sort for the given bitwidth. + SMTSortRef getFloatSort(unsigned BitWidth) { + switch (BitWidth) { + case 16: + return getFloat16Sort(); + case 32: + return getFloat32Sort(); + case 64: + return getFloat64Sort(); + case 128: + return getFloat128Sort(); + default:; + } + llvm_unreachable("Unsupported floating-point bitwidth!"); + } + + // Returns a boolean sort. + virtual SMTSortRef getBoolSort() = 0; + + // Returns an appropriate bitvector sort for the given bitwidth. + virtual SMTSortRef getBitvectorSort(const unsigned BitWidth) = 0; + + // Returns a floating-point sort of width 16 + virtual SMTSortRef getFloat16Sort() = 0; + + // Returns a floating-point sort of width 32 + virtual SMTSortRef getFloat32Sort() = 0; + + // Returns a floating-point sort of width 64 + virtual SMTSortRef getFloat64Sort() = 0; + + // Returns a floating-point sort of width 128 + virtual SMTSortRef getFloat128Sort() = 0; + + // Returns an appropriate sort for the given AST. + virtual SMTSortRef getSort(const SMTExprRef &AST) = 0; + + /// Given a constraint, adds it to the solver + virtual void addConstraint(const SMTExprRef &Exp) const = 0; + + /// Creates a bitvector addition operation + virtual SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector subtraction operation + virtual SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector multiplication operation + virtual SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed modulus operation + virtual SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned modulus operation + virtual SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed division operation + virtual SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned division operation + virtual SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector logical shift left operation + virtual SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector arithmetic shift right operation + virtual SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector logical shift right operation + virtual SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector negation operation + virtual SMTExprRef mkBVNeg(const SMTExprRef &Exp) = 0; + + /// Creates a bitvector not operation + virtual SMTExprRef mkBVNot(const SMTExprRef &Exp) = 0; + + /// Creates a bitvector xor operation + virtual SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector or operation + virtual SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector and operation + virtual SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned less-than operation + virtual SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed less-than operation + virtual SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned greater-than operation + virtual SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed greater-than operation + virtual SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned less-equal-than operation + virtual SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed less-equal-than operation + virtual SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector unsigned greater-equal-than operation + virtual SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a bitvector signed greater-equal-than operation + virtual SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean not operation + virtual SMTExprRef mkNot(const SMTExprRef &Exp) = 0; + + /// Creates a boolean equality operation + virtual SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean and operation + virtual SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean or operation + virtual SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a boolean ite operation + virtual SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T, + const SMTExprRef &F) = 0; + + /// Creates a bitvector sign extension operation + virtual SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) = 0; + + /// Creates a bitvector zero extension operation + virtual SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) = 0; + + /// Creates a bitvector extract operation + virtual SMTExprRef mkBVExtract(unsigned High, unsigned Low, + const SMTExprRef &Exp) = 0; + + /// Creates a bitvector concat operation + virtual SMTExprRef mkBVConcat(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a predicate that checks for overflow in a bitvector addition + /// operation + virtual SMTExprRef mkBVAddNoOverflow(const SMTExprRef &LHS, + const SMTExprRef &RHS, + bool isSigned) = 0; + + /// Creates a predicate that checks for underflow in a signed bitvector + /// addition operation + virtual SMTExprRef mkBVAddNoUnderflow(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a predicate that checks for overflow in a signed bitvector + /// subtraction operation + virtual SMTExprRef mkBVSubNoOverflow(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a predicate that checks for underflow in a bitvector subtraction + /// operation + virtual SMTExprRef mkBVSubNoUnderflow(const SMTExprRef &LHS, + const SMTExprRef &RHS, + bool isSigned) = 0; + + /// Creates a predicate that checks for overflow in a signed bitvector + /// division/modulus operation + virtual SMTExprRef mkBVSDivNoOverflow(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a predicate that checks for overflow in a bitvector negation + /// operation + virtual SMTExprRef mkBVNegNoOverflow(const SMTExprRef &Exp) = 0; + + /// Creates a predicate that checks for overflow in a bitvector multiplication + /// operation + virtual SMTExprRef mkBVMulNoOverflow(const SMTExprRef &LHS, + const SMTExprRef &RHS, + bool isSigned) = 0; + + /// Creates a predicate that checks for underflow in a signed bitvector + /// multiplication operation + virtual SMTExprRef mkBVMulNoUnderflow(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a floating-point negation operation + virtual SMTExprRef mkFPNeg(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isInfinite operation + virtual SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isNaN operation + virtual SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isNormal operation + virtual SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point isZero operation + virtual SMTExprRef mkFPIsZero(const SMTExprRef &Exp) = 0; + + /// Creates a floating-point multiplication operation + virtual SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point division operation + virtual SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point remainder operation + virtual SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point addition operation + virtual SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point subtraction operation + virtual SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point less-than operation + virtual SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point greater-than operation + virtual SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point less-than-or-equal operation + virtual SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point greater-than-or-equal operation + virtual SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) = 0; + + /// Creates a floating-point equality operation + virtual SMTExprRef mkFPEqual(const SMTExprRef &LHS, + const SMTExprRef &RHS) = 0; + + /// Creates a floating-point conversion from floatint-point to floating-point + /// operation + virtual SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from signed bitvector to + /// floatint-point operation + virtual SMTExprRef mkSBVtoFP(const SMTExprRef &From, + const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from unsigned bitvector to + /// floatint-point operation + virtual SMTExprRef mkUBVtoFP(const SMTExprRef &From, + const SMTSortRef &To) = 0; + + /// Creates a floating-point conversion from floatint-point to signed + /// bitvector operation + virtual SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) = 0; + + /// Creates a floating-point conversion from floatint-point to unsigned + /// bitvector operation + virtual SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) = 0; + + /// Creates a new symbol, given a name and a sort + virtual SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) = 0; + + // Returns an appropriate floating-point rounding mode. + virtual SMTExprRef getFloatRoundingMode() = 0; + + // If the a model is available, returns the value of a given bitvector symbol + virtual llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, + bool isUnsigned) = 0; + + // If the a model is available, returns the value of a given boolean symbol + virtual bool getBoolean(const SMTExprRef &Exp) = 0; + + /// Constructs an SMTExprRef from a boolean. + virtual SMTExprRef mkBoolean(const bool b) = 0; + + /// Constructs an SMTExprRef from a finite APFloat. + virtual SMTExprRef mkFloat(const llvm::APFloat Float) = 0; + + /// Constructs an SMTExprRef from an APSInt and its bit width + virtual SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) = 0; + + /// Given an expression, extract the value of this operand in the model. + virtual bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) = 0; + + /// Given an expression extract the value of this operand in the model. + virtual bool getInterpretation(const SMTExprRef &Exp, + llvm::APFloat &Float) = 0; + + /// Check if the constraints are satisfiable + virtual std::optional<bool> check() const = 0; + + /// Push the current solver state + virtual void push() = 0; + + /// Pop the previous solver state + virtual void pop(unsigned NumStates = 1) = 0; + + /// Reset the solver and remove all constraints. + virtual void reset() = 0; + + /// Checks if the solver supports floating-points. + virtual bool isFPSupported() = 0; + + virtual void print(raw_ostream &OS) const = 0; +}; + +/// Shared pointer for SMTSolvers. +using SMTSolverRef = std::shared_ptr<SMTSolver>; + +/// Convenience method to create and Z3Solver object +SMTSolverRef CreateZ3Solver(); + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SaveAndRestore.h b/contrib/libs/llvm16/include/llvm/Support/SaveAndRestore.h new file mode 100644 index 00000000000..8cc5e4e19b0 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SaveAndRestore.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- SaveAndRestore.h - Utility -------------------------------*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides utility classes that use RAII to save and restore +/// values. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SAVEANDRESTORE_H +#define LLVM_SUPPORT_SAVEANDRESTORE_H + +#include <utility> + +namespace llvm { + +/// A utility class that uses RAII to save and restore the value of a variable. +template <typename T> struct SaveAndRestore { + SaveAndRestore(T &X) : X(X), OldValue(X) {} + SaveAndRestore(T &X, const T &NewValue) : X(X), OldValue(X) { X = NewValue; } + SaveAndRestore(T &X, T &&NewValue) : X(X), OldValue(std::move(X)) { + X = std::move(NewValue); + } + ~SaveAndRestore() { X = std::move(OldValue); } + const T &get() { return OldValue; } + +private: + T &X; + T OldValue; +}; + +// User-defined CTAD guides. +template <typename T> SaveAndRestore(T &) -> SaveAndRestore<T>; +template <typename T> SaveAndRestore(T &, const T &) -> SaveAndRestore<T>; +template <typename T> SaveAndRestore(T &, T &&) -> SaveAndRestore<T>; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ScaledNumber.h b/contrib/libs/llvm16/include/llvm/Support/ScaledNumber.h new file mode 100644 index 00000000000..8880a26c692 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ScaledNumber.h @@ -0,0 +1,903 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/ScaledNumber.h - Support for scaled numbers -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains functions (and a class) useful for working with scaled +// numbers -- in particular, pairs of integers where one represents digits and +// another represents a scale. The functions are helpers and live in the +// namespace ScaledNumbers. The class ScaledNumber is useful for modelling +// certain cost metrics that need simple, integer-like semantics that are easy +// to reason about. +// +// These might remind you of soft-floats. If you want one of those, you're in +// the wrong place. Look at include/llvm/ADT/APFloat.h instead. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SCALEDNUMBER_H +#define LLVM_SUPPORT_SCALEDNUMBER_H + +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cstdint> +#include <limits> +#include <string> +#include <tuple> +#include <utility> + +namespace llvm { +namespace ScaledNumbers { + +/// Maximum scale; same as APFloat for easy debug printing. +const int32_t MaxScale = 16383; + +/// Maximum scale; same as APFloat for easy debug printing. +const int32_t MinScale = -16382; + +/// Get the width of a number. +template <class DigitsT> inline int getWidth() { return sizeof(DigitsT) * 8; } + +/// Conditionally round up a scaled number. +/// +/// Given \c Digits and \c Scale, round up iff \c ShouldRound is \c true. +/// Always returns \c Scale unless there's an overflow, in which case it +/// returns \c 1+Scale. +/// +/// \pre adding 1 to \c Scale will not overflow INT16_MAX. +template <class DigitsT> +inline std::pair<DigitsT, int16_t> getRounded(DigitsT Digits, int16_t Scale, + bool ShouldRound) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + if (ShouldRound) + if (!++Digits) + // Overflow. + return std::make_pair(DigitsT(1) << (getWidth<DigitsT>() - 1), Scale + 1); + return std::make_pair(Digits, Scale); +} + +/// Convenience helper for 32-bit rounding. +inline std::pair<uint32_t, int16_t> getRounded32(uint32_t Digits, int16_t Scale, + bool ShouldRound) { + return getRounded(Digits, Scale, ShouldRound); +} + +/// Convenience helper for 64-bit rounding. +inline std::pair<uint64_t, int16_t> getRounded64(uint64_t Digits, int16_t Scale, + bool ShouldRound) { + return getRounded(Digits, Scale, ShouldRound); +} + +/// Adjust a 64-bit scaled number down to the appropriate width. +/// +/// \pre Adding 64 to \c Scale will not overflow INT16_MAX. +template <class DigitsT> +inline std::pair<DigitsT, int16_t> getAdjusted(uint64_t Digits, + int16_t Scale = 0) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + const int Width = getWidth<DigitsT>(); + if (Width == 64 || Digits <= std::numeric_limits<DigitsT>::max()) + return std::make_pair(Digits, Scale); + + // Shift right and round. + int Shift = 64 - Width - countLeadingZeros(Digits); + return getRounded<DigitsT>(Digits >> Shift, Scale + Shift, + Digits & (UINT64_C(1) << (Shift - 1))); +} + +/// Convenience helper for adjusting to 32 bits. +inline std::pair<uint32_t, int16_t> getAdjusted32(uint64_t Digits, + int16_t Scale = 0) { + return getAdjusted<uint32_t>(Digits, Scale); +} + +/// Convenience helper for adjusting to 64 bits. +inline std::pair<uint64_t, int16_t> getAdjusted64(uint64_t Digits, + int16_t Scale = 0) { + return getAdjusted<uint64_t>(Digits, Scale); +} + +/// Multiply two 64-bit integers to create a 64-bit scaled number. +/// +/// Implemented with four 64-bit integer multiplies. +std::pair<uint64_t, int16_t> multiply64(uint64_t LHS, uint64_t RHS); + +/// Multiply two 32-bit integers to create a 32-bit scaled number. +/// +/// Implemented with one 64-bit integer multiply. +template <class DigitsT> +inline std::pair<DigitsT, int16_t> getProduct(DigitsT LHS, DigitsT RHS) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + if (getWidth<DigitsT>() <= 32 || (LHS <= UINT32_MAX && RHS <= UINT32_MAX)) + return getAdjusted<DigitsT>(uint64_t(LHS) * RHS); + + return multiply64(LHS, RHS); +} + +/// Convenience helper for 32-bit product. +inline std::pair<uint32_t, int16_t> getProduct32(uint32_t LHS, uint32_t RHS) { + return getProduct(LHS, RHS); +} + +/// Convenience helper for 64-bit product. +inline std::pair<uint64_t, int16_t> getProduct64(uint64_t LHS, uint64_t RHS) { + return getProduct(LHS, RHS); +} + +/// Divide two 64-bit integers to create a 64-bit scaled number. +/// +/// Implemented with long division. +/// +/// \pre \c Dividend and \c Divisor are non-zero. +std::pair<uint64_t, int16_t> divide64(uint64_t Dividend, uint64_t Divisor); + +/// Divide two 32-bit integers to create a 32-bit scaled number. +/// +/// Implemented with one 64-bit integer divide/remainder pair. +/// +/// \pre \c Dividend and \c Divisor are non-zero. +std::pair<uint32_t, int16_t> divide32(uint32_t Dividend, uint32_t Divisor); + +/// Divide two 32-bit numbers to create a 32-bit scaled number. +/// +/// Implemented with one 64-bit integer divide/remainder pair. +/// +/// Returns \c (DigitsT_MAX, MaxScale) for divide-by-zero (0 for 0/0). +template <class DigitsT> +std::pair<DigitsT, int16_t> getQuotient(DigitsT Dividend, DigitsT Divisor) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + static_assert(sizeof(DigitsT) == 4 || sizeof(DigitsT) == 8, + "expected 32-bit or 64-bit digits"); + + // Check for zero. + if (!Dividend) + return std::make_pair(0, 0); + if (!Divisor) + return std::make_pair(std::numeric_limits<DigitsT>::max(), MaxScale); + + if (getWidth<DigitsT>() == 64) + return divide64(Dividend, Divisor); + return divide32(Dividend, Divisor); +} + +/// Convenience helper for 32-bit quotient. +inline std::pair<uint32_t, int16_t> getQuotient32(uint32_t Dividend, + uint32_t Divisor) { + return getQuotient(Dividend, Divisor); +} + +/// Convenience helper for 64-bit quotient. +inline std::pair<uint64_t, int16_t> getQuotient64(uint64_t Dividend, + uint64_t Divisor) { + return getQuotient(Dividend, Divisor); +} + +/// Implementation of getLg() and friends. +/// +/// Returns the rounded lg of \c Digits*2^Scale and an int specifying whether +/// this was rounded up (1), down (-1), or exact (0). +/// +/// Returns \c INT32_MIN when \c Digits is zero. +template <class DigitsT> +inline std::pair<int32_t, int> getLgImpl(DigitsT Digits, int16_t Scale) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + if (!Digits) + return std::make_pair(INT32_MIN, 0); + + // Get the floor of the lg of Digits. + int32_t LocalFloor = sizeof(Digits) * 8 - countLeadingZeros(Digits) - 1; + + // Get the actual floor. + int32_t Floor = Scale + LocalFloor; + if (Digits == UINT64_C(1) << LocalFloor) + return std::make_pair(Floor, 0); + + // Round based on the next digit. + assert(LocalFloor >= 1); + bool Round = Digits & UINT64_C(1) << (LocalFloor - 1); + return std::make_pair(Floor + Round, Round ? 1 : -1); +} + +/// Get the lg (rounded) of a scaled number. +/// +/// Get the lg of \c Digits*2^Scale. +/// +/// Returns \c INT32_MIN when \c Digits is zero. +template <class DigitsT> int32_t getLg(DigitsT Digits, int16_t Scale) { + return getLgImpl(Digits, Scale).first; +} + +/// Get the lg floor of a scaled number. +/// +/// Get the floor of the lg of \c Digits*2^Scale. +/// +/// Returns \c INT32_MIN when \c Digits is zero. +template <class DigitsT> int32_t getLgFloor(DigitsT Digits, int16_t Scale) { + auto Lg = getLgImpl(Digits, Scale); + return Lg.first - (Lg.second > 0); +} + +/// Get the lg ceiling of a scaled number. +/// +/// Get the ceiling of the lg of \c Digits*2^Scale. +/// +/// Returns \c INT32_MIN when \c Digits is zero. +template <class DigitsT> int32_t getLgCeiling(DigitsT Digits, int16_t Scale) { + auto Lg = getLgImpl(Digits, Scale); + return Lg.first + (Lg.second < 0); +} + +/// Implementation for comparing scaled numbers. +/// +/// Compare two 64-bit numbers with different scales. Given that the scale of +/// \c L is higher than that of \c R by \c ScaleDiff, compare them. Return -1, +/// 1, and 0 for less than, greater than, and equal, respectively. +/// +/// \pre 0 <= ScaleDiff < 64. +int compareImpl(uint64_t L, uint64_t R, int ScaleDiff); + +/// Compare two scaled numbers. +/// +/// Compare two scaled numbers. Returns 0 for equal, -1 for less than, and 1 +/// for greater than. +template <class DigitsT> +int compare(DigitsT LDigits, int16_t LScale, DigitsT RDigits, int16_t RScale) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + // Check for zero. + if (!LDigits) + return RDigits ? -1 : 0; + if (!RDigits) + return 1; + + // Check for the scale. Use getLgFloor to be sure that the scale difference + // is always lower than 64. + int32_t lgL = getLgFloor(LDigits, LScale), lgR = getLgFloor(RDigits, RScale); + if (lgL != lgR) + return lgL < lgR ? -1 : 1; + + // Compare digits. + if (LScale < RScale) + return compareImpl(LDigits, RDigits, RScale - LScale); + + return -compareImpl(RDigits, LDigits, LScale - RScale); +} + +/// Match scales of two numbers. +/// +/// Given two scaled numbers, match up their scales. Change the digits and +/// scales in place. Shift the digits as necessary to form equivalent numbers, +/// losing precision only when necessary. +/// +/// If the output value of \c LDigits (\c RDigits) is \c 0, the output value of +/// \c LScale (\c RScale) is unspecified. +/// +/// As a convenience, returns the matching scale. If the output value of one +/// number is zero, returns the scale of the other. If both are zero, which +/// scale is returned is unspecified. +template <class DigitsT> +int16_t matchScales(DigitsT &LDigits, int16_t &LScale, DigitsT &RDigits, + int16_t &RScale) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + if (LScale < RScale) + // Swap arguments. + return matchScales(RDigits, RScale, LDigits, LScale); + if (!LDigits) + return RScale; + if (!RDigits || LScale == RScale) + return LScale; + + // Now LScale > RScale. Get the difference. + int32_t ScaleDiff = int32_t(LScale) - RScale; + if (ScaleDiff >= 2 * getWidth<DigitsT>()) { + // Don't bother shifting. RDigits will get zero-ed out anyway. + RDigits = 0; + return LScale; + } + + // Shift LDigits left as much as possible, then shift RDigits right. + int32_t ShiftL = std::min<int32_t>(countLeadingZeros(LDigits), ScaleDiff); + assert(ShiftL < getWidth<DigitsT>() && "can't shift more than width"); + + int32_t ShiftR = ScaleDiff - ShiftL; + if (ShiftR >= getWidth<DigitsT>()) { + // Don't bother shifting. RDigits will get zero-ed out anyway. + RDigits = 0; + return LScale; + } + + LDigits <<= ShiftL; + RDigits >>= ShiftR; + + LScale -= ShiftL; + RScale += ShiftR; + assert(LScale == RScale && "scales should match"); + return LScale; +} + +/// Get the sum of two scaled numbers. +/// +/// Get the sum of two scaled numbers with as much precision as possible. +/// +/// \pre Adding 1 to \c LScale (or \c RScale) will not overflow INT16_MAX. +template <class DigitsT> +std::pair<DigitsT, int16_t> getSum(DigitsT LDigits, int16_t LScale, + DigitsT RDigits, int16_t RScale) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + // Check inputs up front. This is only relevant if addition overflows, but + // testing here should catch more bugs. + assert(LScale < INT16_MAX && "scale too large"); + assert(RScale < INT16_MAX && "scale too large"); + + // Normalize digits to match scales. + int16_t Scale = matchScales(LDigits, LScale, RDigits, RScale); + + // Compute sum. + DigitsT Sum = LDigits + RDigits; + if (Sum >= RDigits) + return std::make_pair(Sum, Scale); + + // Adjust sum after arithmetic overflow. + DigitsT HighBit = DigitsT(1) << (getWidth<DigitsT>() - 1); + return std::make_pair(HighBit | Sum >> 1, Scale + 1); +} + +/// Convenience helper for 32-bit sum. +inline std::pair<uint32_t, int16_t> getSum32(uint32_t LDigits, int16_t LScale, + uint32_t RDigits, int16_t RScale) { + return getSum(LDigits, LScale, RDigits, RScale); +} + +/// Convenience helper for 64-bit sum. +inline std::pair<uint64_t, int16_t> getSum64(uint64_t LDigits, int16_t LScale, + uint64_t RDigits, int16_t RScale) { + return getSum(LDigits, LScale, RDigits, RScale); +} + +/// Get the difference of two scaled numbers. +/// +/// Get LHS minus RHS with as much precision as possible. +/// +/// Returns \c (0, 0) if the RHS is larger than the LHS. +template <class DigitsT> +std::pair<DigitsT, int16_t> getDifference(DigitsT LDigits, int16_t LScale, + DigitsT RDigits, int16_t RScale) { + static_assert(!std::numeric_limits<DigitsT>::is_signed, "expected unsigned"); + + // Normalize digits to match scales. + const DigitsT SavedRDigits = RDigits; + const int16_t SavedRScale = RScale; + matchScales(LDigits, LScale, RDigits, RScale); + + // Compute difference. + if (LDigits <= RDigits) + return std::make_pair(0, 0); + if (RDigits || !SavedRDigits) + return std::make_pair(LDigits - RDigits, LScale); + + // Check if RDigits just barely lost its last bit. E.g., for 32-bit: + // + // 1*2^32 - 1*2^0 == 0xffffffff != 1*2^32 + const auto RLgFloor = getLgFloor(SavedRDigits, SavedRScale); + if (!compare(LDigits, LScale, DigitsT(1), RLgFloor + getWidth<DigitsT>())) + return std::make_pair(std::numeric_limits<DigitsT>::max(), RLgFloor); + + return std::make_pair(LDigits, LScale); +} + +/// Convenience helper for 32-bit difference. +inline std::pair<uint32_t, int16_t> getDifference32(uint32_t LDigits, + int16_t LScale, + uint32_t RDigits, + int16_t RScale) { + return getDifference(LDigits, LScale, RDigits, RScale); +} + +/// Convenience helper for 64-bit difference. +inline std::pair<uint64_t, int16_t> getDifference64(uint64_t LDigits, + int16_t LScale, + uint64_t RDigits, + int16_t RScale) { + return getDifference(LDigits, LScale, RDigits, RScale); +} + +} // end namespace ScaledNumbers +} // end namespace llvm + +namespace llvm { + +class raw_ostream; +class ScaledNumberBase { +public: + static constexpr int DefaultPrecision = 10; + + static void dump(uint64_t D, int16_t E, int Width); + static raw_ostream &print(raw_ostream &OS, uint64_t D, int16_t E, int Width, + unsigned Precision); + static std::string toString(uint64_t D, int16_t E, int Width, + unsigned Precision); + static int countLeadingZeros32(uint32_t N) { return countLeadingZeros(N); } + static int countLeadingZeros64(uint64_t N) { return countLeadingZeros(N); } + static uint64_t getHalf(uint64_t N) { return (N >> 1) + (N & 1); } + + static std::pair<uint64_t, bool> splitSigned(int64_t N) { + if (N >= 0) + return std::make_pair(N, false); + uint64_t Unsigned = N == INT64_MIN ? UINT64_C(1) << 63 : uint64_t(-N); + return std::make_pair(Unsigned, true); + } + static int64_t joinSigned(uint64_t U, bool IsNeg) { + if (U > uint64_t(INT64_MAX)) + return IsNeg ? INT64_MIN : INT64_MAX; + return IsNeg ? -int64_t(U) : int64_t(U); + } +}; + +/// Simple representation of a scaled number. +/// +/// ScaledNumber is a number represented by digits and a scale. It uses simple +/// saturation arithmetic and every operation is well-defined for every value. +/// It's somewhat similar in behaviour to a soft-float, but is *not* a +/// replacement for one. If you're doing numerics, look at \a APFloat instead. +/// Nevertheless, we've found these semantics useful for modelling certain cost +/// metrics. +/// +/// The number is split into a signed scale and unsigned digits. The number +/// represented is \c getDigits()*2^getScale(). In this way, the digits are +/// much like the mantissa in the x87 long double, but there is no canonical +/// form so the same number can be represented by many bit representations. +/// +/// ScaledNumber is templated on the underlying integer type for digits, which +/// is expected to be unsigned. +/// +/// Unlike APFloat, ScaledNumber does not model architecture floating point +/// behaviour -- while this might make it a little faster and easier to reason +/// about, it certainly makes it more dangerous for general numerics. +/// +/// ScaledNumber is totally ordered. However, there is no canonical form, so +/// there are multiple representations of most scalars. E.g.: +/// +/// ScaledNumber(8u, 0) == ScaledNumber(4u, 1) +/// ScaledNumber(4u, 1) == ScaledNumber(2u, 2) +/// ScaledNumber(2u, 2) == ScaledNumber(1u, 3) +/// +/// ScaledNumber implements most arithmetic operations. Precision is kept +/// where possible. Uses simple saturation arithmetic, so that operations +/// saturate to 0.0 or getLargest() rather than under or overflowing. It has +/// some extra arithmetic for unit inversion. 0.0/0.0 is defined to be 0.0. +/// Any other division by 0.0 is defined to be getLargest(). +/// +/// As a convenience for modifying the exponent, left and right shifting are +/// both implemented, and both interpret negative shifts as positive shifts in +/// the opposite direction. +/// +/// Scales are limited to the range accepted by x87 long double. This makes +/// it trivial to add functionality to convert to APFloat (this is already +/// relied on for the implementation of printing). +/// +/// Possible (and conflicting) future directions: +/// +/// 1. Turn this into a wrapper around \a APFloat. +/// 2. Share the algorithm implementations with \a APFloat. +/// 3. Allow \a ScaledNumber to represent a signed number. +template <class DigitsT> class ScaledNumber : ScaledNumberBase { +public: + static_assert(!std::numeric_limits<DigitsT>::is_signed, + "only unsigned floats supported"); + + typedef DigitsT DigitsType; + +private: + typedef std::numeric_limits<DigitsType> DigitsLimits; + + static constexpr int Width = sizeof(DigitsType) * 8; + static_assert(Width <= 64, "invalid integer width for digits"); + +private: + DigitsType Digits = 0; + int16_t Scale = 0; + +public: + ScaledNumber() = default; + + constexpr ScaledNumber(DigitsType Digits, int16_t Scale) + : Digits(Digits), Scale(Scale) {} + +private: + ScaledNumber(const std::pair<DigitsT, int16_t> &X) + : Digits(X.first), Scale(X.second) {} + +public: + static ScaledNumber getZero() { return ScaledNumber(0, 0); } + static ScaledNumber getOne() { return ScaledNumber(1, 0); } + static ScaledNumber getLargest() { + return ScaledNumber(DigitsLimits::max(), ScaledNumbers::MaxScale); + } + static ScaledNumber get(uint64_t N) { return adjustToWidth(N, 0); } + static ScaledNumber getInverse(uint64_t N) { + return get(N).invert(); + } + static ScaledNumber getFraction(DigitsType N, DigitsType D) { + return getQuotient(N, D); + } + + int16_t getScale() const { return Scale; } + DigitsType getDigits() const { return Digits; } + + /// Convert to the given integer type. + /// + /// Convert to \c IntT using simple saturating arithmetic, truncating if + /// necessary. + template <class IntT> IntT toInt() const; + + bool isZero() const { return !Digits; } + bool isLargest() const { return *this == getLargest(); } + bool isOne() const { + if (Scale > 0 || Scale <= -Width) + return false; + return Digits == DigitsType(1) << -Scale; + } + + /// The log base 2, rounded. + /// + /// Get the lg of the scalar. lg 0 is defined to be INT32_MIN. + int32_t lg() const { return ScaledNumbers::getLg(Digits, Scale); } + + /// The log base 2, rounded towards INT32_MIN. + /// + /// Get the lg floor. lg 0 is defined to be INT32_MIN. + int32_t lgFloor() const { return ScaledNumbers::getLgFloor(Digits, Scale); } + + /// The log base 2, rounded towards INT32_MAX. + /// + /// Get the lg ceiling. lg 0 is defined to be INT32_MIN. + int32_t lgCeiling() const { + return ScaledNumbers::getLgCeiling(Digits, Scale); + } + + bool operator==(const ScaledNumber &X) const { return compare(X) == 0; } + bool operator<(const ScaledNumber &X) const { return compare(X) < 0; } + bool operator!=(const ScaledNumber &X) const { return compare(X) != 0; } + bool operator>(const ScaledNumber &X) const { return compare(X) > 0; } + bool operator<=(const ScaledNumber &X) const { return compare(X) <= 0; } + bool operator>=(const ScaledNumber &X) const { return compare(X) >= 0; } + + bool operator!() const { return isZero(); } + + /// Convert to a decimal representation in a string. + /// + /// Convert to a string. Uses scientific notation for very large/small + /// numbers. Scientific notation is used roughly for numbers outside of the + /// range 2^-64 through 2^64. + /// + /// \c Precision indicates the number of decimal digits of precision to use; + /// 0 requests the maximum available. + /// + /// As a special case to make debugging easier, if the number is small enough + /// to convert without scientific notation and has more than \c Precision + /// digits before the decimal place, it's printed accurately to the first + /// digit past zero. E.g., assuming 10 digits of precision: + /// + /// 98765432198.7654... => 98765432198.8 + /// 8765432198.7654... => 8765432198.8 + /// 765432198.7654... => 765432198.8 + /// 65432198.7654... => 65432198.77 + /// 5432198.7654... => 5432198.765 + std::string toString(unsigned Precision = DefaultPrecision) { + return ScaledNumberBase::toString(Digits, Scale, Width, Precision); + } + + /// Print a decimal representation. + /// + /// Print a string. See toString for documentation. + raw_ostream &print(raw_ostream &OS, + unsigned Precision = DefaultPrecision) const { + return ScaledNumberBase::print(OS, Digits, Scale, Width, Precision); + } + void dump() const { return ScaledNumberBase::dump(Digits, Scale, Width); } + + ScaledNumber &operator+=(const ScaledNumber &X) { + std::tie(Digits, Scale) = + ScaledNumbers::getSum(Digits, Scale, X.Digits, X.Scale); + // Check for exponent past MaxScale. + if (Scale > ScaledNumbers::MaxScale) + *this = getLargest(); + return *this; + } + ScaledNumber &operator-=(const ScaledNumber &X) { + std::tie(Digits, Scale) = + ScaledNumbers::getDifference(Digits, Scale, X.Digits, X.Scale); + return *this; + } + ScaledNumber &operator*=(const ScaledNumber &X); + ScaledNumber &operator/=(const ScaledNumber &X); + ScaledNumber &operator<<=(int16_t Shift) { + shiftLeft(Shift); + return *this; + } + ScaledNumber &operator>>=(int16_t Shift) { + shiftRight(Shift); + return *this; + } + +private: + void shiftLeft(int32_t Shift); + void shiftRight(int32_t Shift); + + /// Adjust two floats to have matching exponents. + /// + /// Adjust \c this and \c X to have matching exponents. Returns the new \c X + /// by value. Does nothing if \a isZero() for either. + /// + /// The value that compares smaller will lose precision, and possibly become + /// \a isZero(). + ScaledNumber matchScales(ScaledNumber X) { + ScaledNumbers::matchScales(Digits, Scale, X.Digits, X.Scale); + return X; + } + +public: + /// Scale a large number accurately. + /// + /// Scale N (multiply it by this). Uses full precision multiplication, even + /// if Width is smaller than 64, so information is not lost. + uint64_t scale(uint64_t N) const; + uint64_t scaleByInverse(uint64_t N) const { + // TODO: implement directly, rather than relying on inverse. Inverse is + // expensive. + return inverse().scale(N); + } + int64_t scale(int64_t N) const { + std::pair<uint64_t, bool> Unsigned = splitSigned(N); + return joinSigned(scale(Unsigned.first), Unsigned.second); + } + int64_t scaleByInverse(int64_t N) const { + std::pair<uint64_t, bool> Unsigned = splitSigned(N); + return joinSigned(scaleByInverse(Unsigned.first), Unsigned.second); + } + + int compare(const ScaledNumber &X) const { + return ScaledNumbers::compare(Digits, Scale, X.Digits, X.Scale); + } + int compareTo(uint64_t N) const { + return ScaledNumbers::compare<uint64_t>(Digits, Scale, N, 0); + } + int compareTo(int64_t N) const { return N < 0 ? 1 : compareTo(uint64_t(N)); } + + ScaledNumber &invert() { return *this = ScaledNumber::get(1) / *this; } + ScaledNumber inverse() const { return ScaledNumber(*this).invert(); } + +private: + static ScaledNumber getProduct(DigitsType LHS, DigitsType RHS) { + return ScaledNumbers::getProduct(LHS, RHS); + } + static ScaledNumber getQuotient(DigitsType Dividend, DigitsType Divisor) { + return ScaledNumbers::getQuotient(Dividend, Divisor); + } + + static int countLeadingZerosWidth(DigitsType Digits) { + if (Width == 64) + return countLeadingZeros64(Digits); + if (Width == 32) + return countLeadingZeros32(Digits); + return countLeadingZeros32(Digits) + Width - 32; + } + + /// Adjust a number to width, rounding up if necessary. + /// + /// Should only be called for \c Shift close to zero. + /// + /// \pre Shift >= MinScale && Shift + 64 <= MaxScale. + static ScaledNumber adjustToWidth(uint64_t N, int32_t Shift) { + assert(Shift >= ScaledNumbers::MinScale && "Shift should be close to 0"); + assert(Shift <= ScaledNumbers::MaxScale - 64 && + "Shift should be close to 0"); + auto Adjusted = ScaledNumbers::getAdjusted<DigitsT>(N, Shift); + return Adjusted; + } + + static ScaledNumber getRounded(ScaledNumber P, bool Round) { + // Saturate. + if (P.isLargest()) + return P; + + return ScaledNumbers::getRounded(P.Digits, P.Scale, Round); + } +}; + +#define SCALED_NUMBER_BOP(op, base) \ + template <class DigitsT> \ + ScaledNumber<DigitsT> operator op(const ScaledNumber<DigitsT> &L, \ + const ScaledNumber<DigitsT> &R) { \ + return ScaledNumber<DigitsT>(L) base R; \ + } +SCALED_NUMBER_BOP(+, += ) +SCALED_NUMBER_BOP(-, -= ) +SCALED_NUMBER_BOP(*, *= ) +SCALED_NUMBER_BOP(/, /= ) +#undef SCALED_NUMBER_BOP + +template <class DigitsT> +ScaledNumber<DigitsT> operator<<(const ScaledNumber<DigitsT> &L, + int16_t Shift) { + return ScaledNumber<DigitsT>(L) <<= Shift; +} + +template <class DigitsT> +ScaledNumber<DigitsT> operator>>(const ScaledNumber<DigitsT> &L, + int16_t Shift) { + return ScaledNumber<DigitsT>(L) >>= Shift; +} + +template <class DigitsT> +raw_ostream &operator<<(raw_ostream &OS, const ScaledNumber<DigitsT> &X) { + return X.print(OS, 10); +} + +#define SCALED_NUMBER_COMPARE_TO_TYPE(op, T1, T2) \ + template <class DigitsT> \ + bool operator op(const ScaledNumber<DigitsT> &L, T1 R) { \ + return L.compareTo(T2(R)) op 0; \ + } \ + template <class DigitsT> \ + bool operator op(T1 L, const ScaledNumber<DigitsT> &R) { \ + return 0 op R.compareTo(T2(L)); \ + } +#define SCALED_NUMBER_COMPARE_TO(op) \ + SCALED_NUMBER_COMPARE_TO_TYPE(op, uint64_t, uint64_t) \ + SCALED_NUMBER_COMPARE_TO_TYPE(op, uint32_t, uint64_t) \ + SCALED_NUMBER_COMPARE_TO_TYPE(op, int64_t, int64_t) \ + SCALED_NUMBER_COMPARE_TO_TYPE(op, int32_t, int64_t) +SCALED_NUMBER_COMPARE_TO(< ) +SCALED_NUMBER_COMPARE_TO(> ) +SCALED_NUMBER_COMPARE_TO(== ) +SCALED_NUMBER_COMPARE_TO(!= ) +SCALED_NUMBER_COMPARE_TO(<= ) +SCALED_NUMBER_COMPARE_TO(>= ) +#undef SCALED_NUMBER_COMPARE_TO +#undef SCALED_NUMBER_COMPARE_TO_TYPE + +template <class DigitsT> +uint64_t ScaledNumber<DigitsT>::scale(uint64_t N) const { + if (Width == 64 || N <= DigitsLimits::max()) + return (get(N) * *this).template toInt<uint64_t>(); + + // Defer to the 64-bit version. + return ScaledNumber<uint64_t>(Digits, Scale).scale(N); +} + +template <class DigitsT> +template <class IntT> +IntT ScaledNumber<DigitsT>::toInt() const { + typedef std::numeric_limits<IntT> Limits; + if (*this < 1) + return 0; + if (*this >= Limits::max()) + return Limits::max(); + + IntT N = Digits; + if (Scale > 0) { + assert(size_t(Scale) < sizeof(IntT) * 8); + return N << Scale; + } + if (Scale < 0) { + assert(size_t(-Scale) < sizeof(IntT) * 8); + return N >> -Scale; + } + return N; +} + +template <class DigitsT> +ScaledNumber<DigitsT> &ScaledNumber<DigitsT>:: +operator*=(const ScaledNumber &X) { + if (isZero()) + return *this; + if (X.isZero()) + return *this = X; + + // Save the exponents. + int32_t Scales = int32_t(Scale) + int32_t(X.Scale); + + // Get the raw product. + *this = getProduct(Digits, X.Digits); + + // Combine with exponents. + return *this <<= Scales; +} +template <class DigitsT> +ScaledNumber<DigitsT> &ScaledNumber<DigitsT>:: +operator/=(const ScaledNumber &X) { + if (isZero()) + return *this; + if (X.isZero()) + return *this = getLargest(); + + // Save the exponents. + int32_t Scales = int32_t(Scale) - int32_t(X.Scale); + + // Get the raw quotient. + *this = getQuotient(Digits, X.Digits); + + // Combine with exponents. + return *this <<= Scales; +} +template <class DigitsT> void ScaledNumber<DigitsT>::shiftLeft(int32_t Shift) { + if (!Shift || isZero()) + return; + assert(Shift != INT32_MIN); + if (Shift < 0) { + shiftRight(-Shift); + return; + } + + // Shift as much as we can in the exponent. + int32_t ScaleShift = std::min(Shift, ScaledNumbers::MaxScale - Scale); + Scale += ScaleShift; + if (ScaleShift == Shift) + return; + + // Check this late, since it's rare. + if (isLargest()) + return; + + // Shift the digits themselves. + Shift -= ScaleShift; + if (Shift > countLeadingZerosWidth(Digits)) { + // Saturate. + *this = getLargest(); + return; + } + + Digits <<= Shift; +} + +template <class DigitsT> void ScaledNumber<DigitsT>::shiftRight(int32_t Shift) { + if (!Shift || isZero()) + return; + assert(Shift != INT32_MIN); + if (Shift < 0) { + shiftLeft(-Shift); + return; + } + + // Shift as much as we can in the exponent. + int32_t ScaleShift = std::min(Shift, Scale - ScaledNumbers::MinScale); + Scale -= ScaleShift; + if (ScaleShift == Shift) + return; + + // Shift the digits themselves. + Shift -= ScaleShift; + if (Shift >= Width) { + // Saturate. + *this = getZero(); + return; + } + + Digits >>= Shift; +} + + +} // end namespace llvm + +#endif // LLVM_SUPPORT_SCALEDNUMBER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ScopedPrinter.h b/contrib/libs/llvm16/include/llvm/Support/ScopedPrinter.h new file mode 100644 index 00000000000..1e8b8db2cce --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ScopedPrinter.h @@ -0,0 +1,860 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- ScopedPrinter.h ----------------------------------------*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SCOPEDPRINTER_H +#define LLVM_SUPPORT_SCOPEDPRINTER_H + +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +template <typename T> struct EnumEntry { + StringRef Name; + // While Name suffices in most of the cases, in certain cases + // GNU style and LLVM style of ELFDumper do not + // display same string for same enum. The AltName if initialized appropriately + // will hold the string that GNU style emits. + // Example: + // "EM_X86_64" string on LLVM style for Elf_Ehdr->e_machine corresponds to + // "Advanced Micro Devices X86-64" on GNU style + StringRef AltName; + T Value; + constexpr EnumEntry(StringRef N, StringRef A, T V) + : Name(N), AltName(A), Value(V) {} + constexpr EnumEntry(StringRef N, T V) : Name(N), AltName(N), Value(V) {} +}; + +struct HexNumber { + // To avoid sign-extension we have to explicitly cast to the appropriate + // unsigned type. The overloads are here so that every type that is implicitly + // convertible to an integer (including enums and endian helpers) can be used + // without requiring type traits or call-site changes. + HexNumber(char Value) : Value(static_cast<unsigned char>(Value)) {} + HexNumber(signed char Value) : Value(static_cast<unsigned char>(Value)) {} + HexNumber(signed short Value) : Value(static_cast<unsigned short>(Value)) {} + HexNumber(signed int Value) : Value(static_cast<unsigned int>(Value)) {} + HexNumber(signed long Value) : Value(static_cast<unsigned long>(Value)) {} + HexNumber(signed long long Value) + : Value(static_cast<unsigned long long>(Value)) {} + HexNumber(unsigned char Value) : Value(Value) {} + HexNumber(unsigned short Value) : Value(Value) {} + HexNumber(unsigned int Value) : Value(Value) {} + HexNumber(unsigned long Value) : Value(Value) {} + HexNumber(unsigned long long Value) : Value(Value) {} + uint64_t Value; +}; + +struct FlagEntry { + FlagEntry(StringRef Name, char Value) + : Name(Name), Value(static_cast<unsigned char>(Value)) {} + FlagEntry(StringRef Name, signed char Value) + : Name(Name), Value(static_cast<unsigned char>(Value)) {} + FlagEntry(StringRef Name, signed short Value) + : Name(Name), Value(static_cast<unsigned short>(Value)) {} + FlagEntry(StringRef Name, signed int Value) + : Name(Name), Value(static_cast<unsigned int>(Value)) {} + FlagEntry(StringRef Name, signed long Value) + : Name(Name), Value(static_cast<unsigned long>(Value)) {} + FlagEntry(StringRef Name, signed long long Value) + : Name(Name), Value(static_cast<unsigned long long>(Value)) {} + FlagEntry(StringRef Name, unsigned char Value) : Name(Name), Value(Value) {} + FlagEntry(StringRef Name, unsigned short Value) : Name(Name), Value(Value) {} + FlagEntry(StringRef Name, unsigned int Value) : Name(Name), Value(Value) {} + FlagEntry(StringRef Name, unsigned long Value) : Name(Name), Value(Value) {} + FlagEntry(StringRef Name, unsigned long long Value) + : Name(Name), Value(Value) {} + StringRef Name; + uint64_t Value; +}; + +raw_ostream &operator<<(raw_ostream &OS, const HexNumber &Value); + +template <class T> std::string to_string(const T &Value) { + std::string number; + raw_string_ostream stream(number); + stream << Value; + return stream.str(); +} + +template <typename T, typename TEnum> +std::string enumToString(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) { + for (const EnumEntry<TEnum> &EnumItem : EnumValues) + if (EnumItem.Value == Value) + return std::string(EnumItem.AltName); + return utohexstr(Value, true); +} + +class ScopedPrinter { +public: + enum class ScopedPrinterKind { + Base, + JSON, + }; + + ScopedPrinter(raw_ostream &OS, + ScopedPrinterKind Kind = ScopedPrinterKind::Base) + : OS(OS), Kind(Kind) {} + + ScopedPrinterKind getKind() const { return Kind; } + + static bool classof(const ScopedPrinter *SP) { + return SP->getKind() == ScopedPrinterKind::Base; + } + + virtual ~ScopedPrinter() = default; + + void flush() { OS.flush(); } + + void indent(int Levels = 1) { IndentLevel += Levels; } + + void unindent(int Levels = 1) { + IndentLevel = IndentLevel > Levels ? IndentLevel - Levels : 0; + } + + void resetIndent() { IndentLevel = 0; } + + int getIndentLevel() { return IndentLevel; } + + void setPrefix(StringRef P) { Prefix = P; } + + void printIndent() { + OS << Prefix; + for (int i = 0; i < IndentLevel; ++i) + OS << " "; + } + + template <typename T> HexNumber hex(T Value) { return HexNumber(Value); } + + template <typename T, typename TEnum> + void printEnum(StringRef Label, T Value, + ArrayRef<EnumEntry<TEnum>> EnumValues) { + StringRef Name; + bool Found = false; + for (const auto &EnumItem : EnumValues) { + if (EnumItem.Value == Value) { + Name = EnumItem.Name; + Found = true; + break; + } + } + + if (Found) + printHex(Label, Name, Value); + else + printHex(Label, Value); + } + + template <typename T, typename TFlag> + void printFlags(StringRef Label, T Value, ArrayRef<EnumEntry<TFlag>> Flags, + TFlag EnumMask1 = {}, TFlag EnumMask2 = {}, + TFlag EnumMask3 = {}) { + SmallVector<FlagEntry, 10> SetFlags; + + for (const auto &Flag : Flags) { + if (Flag.Value == 0) + continue; + + TFlag EnumMask{}; + if (Flag.Value & EnumMask1) + EnumMask = EnumMask1; + else if (Flag.Value & EnumMask2) + EnumMask = EnumMask2; + else if (Flag.Value & EnumMask3) + EnumMask = EnumMask3; + bool IsEnum = (Flag.Value & EnumMask) != 0; + if ((!IsEnum && (Value & Flag.Value) == Flag.Value) || + (IsEnum && (Value & EnumMask) == Flag.Value)) { + SetFlags.emplace_back(Flag.Name, Flag.Value); + } + } + + llvm::sort(SetFlags, &flagName); + printFlagsImpl(Label, hex(Value), SetFlags); + } + + template <typename T> void printFlags(StringRef Label, T Value) { + SmallVector<HexNumber, 10> SetFlags; + uint64_t Flag = 1; + uint64_t Curr = Value; + while (Curr > 0) { + if (Curr & 1) + SetFlags.emplace_back(Flag); + Curr >>= 1; + Flag <<= 1; + } + printFlagsImpl(Label, hex(Value), SetFlags); + } + + virtual void printNumber(StringRef Label, uint64_t Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printNumber(StringRef Label, uint32_t Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printNumber(StringRef Label, uint16_t Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printNumber(StringRef Label, uint8_t Value) { + startLine() << Label << ": " << unsigned(Value) << "\n"; + } + + virtual void printNumber(StringRef Label, int64_t Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printNumber(StringRef Label, int32_t Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printNumber(StringRef Label, int16_t Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printNumber(StringRef Label, int8_t Value) { + startLine() << Label << ": " << int(Value) << "\n"; + } + + virtual void printNumber(StringRef Label, const APSInt &Value) { + startLine() << Label << ": " << Value << "\n"; + } + + template <typename T> + void printNumber(StringRef Label, StringRef Str, T Value) { + printNumberImpl(Label, Str, to_string(Value)); + } + + virtual void printBoolean(StringRef Label, bool Value) { + startLine() << Label << ": " << (Value ? "Yes" : "No") << '\n'; + } + + template <typename... T> void printVersion(StringRef Label, T... Version) { + startLine() << Label << ": "; + printVersionInternal(Version...); + getOStream() << "\n"; + } + + template <typename T> + void printList(StringRef Label, const ArrayRef<T> List) { + SmallVector<std::string, 10> StringList; + for (const auto &Item : List) + StringList.emplace_back(to_string(Item)); + printList(Label, StringList); + } + + virtual void printList(StringRef Label, const ArrayRef<bool> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<std::string> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<uint64_t> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<uint32_t> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<uint16_t> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<uint8_t> List) { + SmallVector<unsigned> NumberList; + for (const uint8_t &Item : List) + NumberList.emplace_back(Item); + printListImpl(Label, NumberList); + } + + virtual void printList(StringRef Label, const ArrayRef<int64_t> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<int32_t> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<int16_t> List) { + printListImpl(Label, List); + } + + virtual void printList(StringRef Label, const ArrayRef<int8_t> List) { + SmallVector<int> NumberList; + for (const int8_t &Item : List) + NumberList.emplace_back(Item); + printListImpl(Label, NumberList); + } + + virtual void printList(StringRef Label, const ArrayRef<APSInt> List) { + printListImpl(Label, List); + } + + template <typename T, typename U> + void printList(StringRef Label, const T &List, const U &Printer) { + startLine() << Label << ": ["; + ListSeparator LS; + for (const auto &Item : List) { + OS << LS; + Printer(OS, Item); + } + OS << "]\n"; + } + + template <typename T> void printHexList(StringRef Label, const T &List) { + SmallVector<HexNumber> HexList; + for (const auto &Item : List) + HexList.emplace_back(Item); + printHexListImpl(Label, HexList); + } + + template <typename T> void printHex(StringRef Label, T Value) { + printHexImpl(Label, hex(Value)); + } + + template <typename T> void printHex(StringRef Label, StringRef Str, T Value) { + printHexImpl(Label, Str, hex(Value)); + } + + template <typename T> + void printSymbolOffset(StringRef Label, StringRef Symbol, T Value) { + printSymbolOffsetImpl(Label, Symbol, hex(Value)); + } + + virtual void printString(StringRef Value) { startLine() << Value << "\n"; } + + virtual void printString(StringRef Label, StringRef Value) { + startLine() << Label << ": " << Value << "\n"; + } + + void printStringEscaped(StringRef Label, StringRef Value) { + printStringEscapedImpl(Label, Value); + } + + void printBinary(StringRef Label, StringRef Str, ArrayRef<uint8_t> Value) { + printBinaryImpl(Label, Str, Value, false); + } + + void printBinary(StringRef Label, StringRef Str, ArrayRef<char> Value) { + auto V = + ArrayRef(reinterpret_cast<const uint8_t *>(Value.data()), Value.size()); + printBinaryImpl(Label, Str, V, false); + } + + void printBinary(StringRef Label, ArrayRef<uint8_t> Value) { + printBinaryImpl(Label, StringRef(), Value, false); + } + + void printBinary(StringRef Label, ArrayRef<char> Value) { + auto V = + ArrayRef(reinterpret_cast<const uint8_t *>(Value.data()), Value.size()); + printBinaryImpl(Label, StringRef(), V, false); + } + + void printBinary(StringRef Label, StringRef Value) { + auto V = + ArrayRef(reinterpret_cast<const uint8_t *>(Value.data()), Value.size()); + printBinaryImpl(Label, StringRef(), V, false); + } + + void printBinaryBlock(StringRef Label, ArrayRef<uint8_t> Value, + uint32_t StartOffset) { + printBinaryImpl(Label, StringRef(), Value, true, StartOffset); + } + + void printBinaryBlock(StringRef Label, ArrayRef<uint8_t> Value) { + printBinaryImpl(Label, StringRef(), Value, true); + } + + void printBinaryBlock(StringRef Label, StringRef Value) { + auto V = + ArrayRef(reinterpret_cast<const uint8_t *>(Value.data()), Value.size()); + printBinaryImpl(Label, StringRef(), V, true); + } + + template <typename T> void printObject(StringRef Label, const T &Value) { + printString(Label, to_string(Value)); + } + + virtual void objectBegin() { scopedBegin('{'); } + + virtual void objectBegin(StringRef Label) { scopedBegin(Label, '{'); } + + virtual void objectEnd() { scopedEnd('}'); } + + virtual void arrayBegin() { scopedBegin('['); } + + virtual void arrayBegin(StringRef Label) { scopedBegin(Label, '['); } + + virtual void arrayEnd() { scopedEnd(']'); } + + virtual raw_ostream &startLine() { + printIndent(); + return OS; + } + + virtual raw_ostream &getOStream() { return OS; } + +private: + template <typename T> void printVersionInternal(T Value) { + getOStream() << Value; + } + + template <typename S, typename T, typename... TArgs> + void printVersionInternal(S Value, T Value2, TArgs... Args) { + getOStream() << Value << "."; + printVersionInternal(Value2, Args...); + } + + static bool flagName(const FlagEntry &LHS, const FlagEntry &RHS) { + return LHS.Name < RHS.Name; + } + + virtual void printBinaryImpl(StringRef Label, StringRef Str, + ArrayRef<uint8_t> Value, bool Block, + uint32_t StartOffset = 0); + + virtual void printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef<FlagEntry> Flags) { + startLine() << Label << " [ (" << Value << ")\n"; + for (const auto &Flag : Flags) + startLine() << " " << Flag.Name << " (" << hex(Flag.Value) << ")\n"; + startLine() << "]\n"; + } + + virtual void printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef<HexNumber> Flags) { + startLine() << Label << " [ (" << Value << ")\n"; + for (const auto &Flag : Flags) + startLine() << " " << Flag << '\n'; + startLine() << "]\n"; + } + + template <typename T> void printListImpl(StringRef Label, const T List) { + startLine() << Label << ": ["; + ListSeparator LS; + for (const auto &Item : List) + OS << LS << Item; + OS << "]\n"; + } + + virtual void printHexListImpl(StringRef Label, + const ArrayRef<HexNumber> List) { + startLine() << Label << ": ["; + ListSeparator LS; + for (const auto &Item : List) + OS << LS << hex(Item); + OS << "]\n"; + } + + virtual void printHexImpl(StringRef Label, HexNumber Value) { + startLine() << Label << ": " << Value << "\n"; + } + + virtual void printHexImpl(StringRef Label, StringRef Str, HexNumber Value) { + startLine() << Label << ": " << Str << " (" << Value << ")\n"; + } + + virtual void printSymbolOffsetImpl(StringRef Label, StringRef Symbol, + HexNumber Value) { + startLine() << Label << ": " << Symbol << '+' << Value << '\n'; + } + + virtual void printNumberImpl(StringRef Label, StringRef Str, + StringRef Value) { + startLine() << Label << ": " << Str << " (" << Value << ")\n"; + } + + virtual void printStringEscapedImpl(StringRef Label, StringRef Value) { + startLine() << Label << ": "; + OS.write_escaped(Value); + OS << '\n'; + } + + void scopedBegin(char Symbol) { + startLine() << Symbol << '\n'; + indent(); + } + + void scopedBegin(StringRef Label, char Symbol) { + startLine() << Label; + if (!Label.empty()) + OS << ' '; + OS << Symbol << '\n'; + indent(); + } + + void scopedEnd(char Symbol) { + unindent(); + startLine() << Symbol << '\n'; + } + + raw_ostream &OS; + int IndentLevel = 0; + StringRef Prefix; + ScopedPrinterKind Kind; +}; + +template <> +inline void +ScopedPrinter::printHex<support::ulittle16_t>(StringRef Label, + support::ulittle16_t Value) { + startLine() << Label << ": " << hex(Value) << "\n"; +} + +struct DelimitedScope; + +class JSONScopedPrinter : public ScopedPrinter { +private: + enum class Scope { + Array, + Object, + }; + + enum class ScopeKind { + NoAttribute, + Attribute, + NestedAttribute, + }; + + struct ScopeContext { + Scope Context; + ScopeKind Kind; + ScopeContext(Scope Context, ScopeKind Kind = ScopeKind::NoAttribute) + : Context(Context), Kind(Kind) {} + }; + + SmallVector<ScopeContext, 8> ScopeHistory; + json::OStream JOS; + std::unique_ptr<DelimitedScope> OuterScope; + +public: + JSONScopedPrinter(raw_ostream &OS, bool PrettyPrint = false, + std::unique_ptr<DelimitedScope> &&OuterScope = + std::unique_ptr<DelimitedScope>{}); + + static bool classof(const ScopedPrinter *SP) { + return SP->getKind() == ScopedPrinter::ScopedPrinterKind::JSON; + } + + void printNumber(StringRef Label, uint64_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, uint32_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, uint16_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, uint8_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int64_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int32_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int16_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, int8_t Value) override { + JOS.attribute(Label, Value); + } + + void printNumber(StringRef Label, const APSInt &Value) override { + JOS.attributeBegin(Label); + printAPSInt(Value); + JOS.attributeEnd(); + } + + void printBoolean(StringRef Label, bool Value) override { + JOS.attribute(Label, Value); + } + + void printList(StringRef Label, const ArrayRef<bool> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<std::string> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<uint64_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<uint32_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<uint16_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<uint8_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<int64_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<int32_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<int16_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<int8_t> List) override { + printListImpl(Label, List); + } + + void printList(StringRef Label, const ArrayRef<APSInt> List) override { + JOS.attributeArray(Label, [&]() { + for (const APSInt &Item : List) { + printAPSInt(Item); + } + }); + } + + void printString(StringRef Value) override { JOS.value(Value); } + + void printString(StringRef Label, StringRef Value) override { + JOS.attribute(Label, Value); + } + + void objectBegin() override { + scopedBegin({Scope::Object, ScopeKind::NoAttribute}); + } + + void objectBegin(StringRef Label) override { + scopedBegin(Label, Scope::Object); + } + + void objectEnd() override { scopedEnd(); } + + void arrayBegin() override { + scopedBegin({Scope::Array, ScopeKind::NoAttribute}); + } + + void arrayBegin(StringRef Label) override { + scopedBegin(Label, Scope::Array); + } + + void arrayEnd() override { scopedEnd(); } + +private: + // Output HexNumbers as decimals so that they're easier to parse. + uint64_t hexNumberToInt(HexNumber Hex) { return Hex.Value; } + + void printAPSInt(const APSInt &Value) { + JOS.rawValueBegin() << Value; + JOS.rawValueEnd(); + } + + void printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef<FlagEntry> Flags) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("RawFlags", hexNumberToInt(Value)); + JOS.attributeArray("Flags", [&]() { + for (const FlagEntry &Flag : Flags) { + JOS.objectBegin(); + JOS.attribute("Name", Flag.Name); + JOS.attribute("Value", Flag.Value); + JOS.objectEnd(); + } + }); + }); + } + + void printFlagsImpl(StringRef Label, HexNumber Value, + ArrayRef<HexNumber> Flags) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("RawFlags", hexNumberToInt(Value)); + JOS.attributeArray("Flags", [&]() { + for (const HexNumber &Flag : Flags) { + JOS.value(Flag.Value); + } + }); + }); + } + + template <typename T> void printListImpl(StringRef Label, const T &List) { + JOS.attributeArray(Label, [&]() { + for (const auto &Item : List) + JOS.value(Item); + }); + } + + void printHexListImpl(StringRef Label, + const ArrayRef<HexNumber> List) override { + JOS.attributeArray(Label, [&]() { + for (const HexNumber &Item : List) { + JOS.value(hexNumberToInt(Item)); + } + }); + } + + void printHexImpl(StringRef Label, HexNumber Value) override { + JOS.attribute(Label, hexNumberToInt(Value)); + } + + void printHexImpl(StringRef Label, StringRef Str, HexNumber Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("Value", Str); + JOS.attribute("RawValue", hexNumberToInt(Value)); + }); + } + + void printSymbolOffsetImpl(StringRef Label, StringRef Symbol, + HexNumber Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("SymName", Symbol); + JOS.attribute("Offset", hexNumberToInt(Value)); + }); + } + + void printNumberImpl(StringRef Label, StringRef Str, + StringRef Value) override { + JOS.attributeObject(Label, [&]() { + JOS.attribute("Value", Str); + JOS.attributeBegin("RawValue"); + JOS.rawValueBegin() << Value; + JOS.rawValueEnd(); + JOS.attributeEnd(); + }); + } + + void printBinaryImpl(StringRef Label, StringRef Str, ArrayRef<uint8_t> Value, + bool Block, uint32_t StartOffset = 0) override { + JOS.attributeObject(Label, [&]() { + if (!Str.empty()) + JOS.attribute("Value", Str); + JOS.attribute("Offset", StartOffset); + JOS.attributeArray("Bytes", [&]() { + for (uint8_t Val : Value) + JOS.value(Val); + }); + }); + } + + void scopedBegin(ScopeContext ScopeCtx) { + if (ScopeCtx.Context == Scope::Object) + JOS.objectBegin(); + else if (ScopeCtx.Context == Scope::Array) + JOS.arrayBegin(); + ScopeHistory.push_back(ScopeCtx); + } + + void scopedBegin(StringRef Label, Scope Ctx) { + ScopeKind Kind = ScopeKind::Attribute; + if (ScopeHistory.empty() || ScopeHistory.back().Context != Scope::Object) { + JOS.objectBegin(); + Kind = ScopeKind::NestedAttribute; + } + JOS.attributeBegin(Label); + scopedBegin({Ctx, Kind}); + } + + void scopedEnd() { + ScopeContext ScopeCtx = ScopeHistory.back(); + if (ScopeCtx.Context == Scope::Object) + JOS.objectEnd(); + else if (ScopeCtx.Context == Scope::Array) + JOS.arrayEnd(); + if (ScopeCtx.Kind == ScopeKind::Attribute || + ScopeCtx.Kind == ScopeKind::NestedAttribute) + JOS.attributeEnd(); + if (ScopeCtx.Kind == ScopeKind::NestedAttribute) + JOS.objectEnd(); + ScopeHistory.pop_back(); + } +}; + +struct DelimitedScope { + DelimitedScope(ScopedPrinter &W) : W(&W) {} + DelimitedScope() : W(nullptr) {} + virtual ~DelimitedScope() = default; + virtual void setPrinter(ScopedPrinter &W) = 0; + ScopedPrinter *W; +}; + +struct DictScope : DelimitedScope { + explicit DictScope() = default; + explicit DictScope(ScopedPrinter &W) : DelimitedScope(W) { W.objectBegin(); } + + DictScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) { + W.objectBegin(N); + } + + void setPrinter(ScopedPrinter &W) override { + this->W = &W; + W.objectBegin(); + } + + ~DictScope() { + if (W) + W->objectEnd(); + } +}; + +struct ListScope : DelimitedScope { + explicit ListScope() = default; + explicit ListScope(ScopedPrinter &W) : DelimitedScope(W) { W.arrayBegin(); } + + ListScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) { + W.arrayBegin(N); + } + + void setPrinter(ScopedPrinter &W) override { + this->W = &W; + W.arrayBegin(); + } + + ~ListScope() { + if (W) + W->arrayEnd(); + } +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Signals.h b/contrib/libs/llvm16/include/llvm/Support/Signals.h new file mode 100644 index 00000000000..66f3329c756 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Signals.h @@ -0,0 +1,140 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Signals.h - Signal Handling support ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines some helpful functions for dealing with the possibility of +// unix signals occurring while your program is running. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SIGNALS_H +#define LLVM_SUPPORT_SIGNALS_H + +#include <cstdint> +#include <string> + +namespace llvm { +class StringRef; +class raw_ostream; + +namespace sys { + + /// This function runs all the registered interrupt handlers, including the + /// removal of files registered by RemoveFileOnSignal. + void RunInterruptHandlers(); + + /// This function registers signal handlers to ensure that if a signal gets + /// delivered that the named file is removed. + /// Remove a file if a fatal signal occurs. + bool RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg = nullptr); + + /// This function removes a file from the list of files to be removed on + /// signal delivery. + void DontRemoveFileOnSignal(StringRef Filename); + + /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the + /// process, print a stack trace and then exit. + /// Print a stack trace if a fatal signal occurs. + /// \param Argv0 the current binary name, used to find the symbolizer + /// relative to the current binary before searching $PATH; can be + /// StringRef(), in which case we will only search $PATH. + /// \param DisableCrashReporting if \c true, disable the normal crash + /// reporting mechanisms on the underlying operating system. + void PrintStackTraceOnErrorSignal(StringRef Argv0, + bool DisableCrashReporting = false); + + /// Disable all system dialog boxes that appear when the process crashes. + void DisableSystemDialogsOnCrash(); + + /// Print the stack trace using the given \c raw_ostream object. + /// \param Depth refers to the number of stackframes to print. If not + /// specified, the entire frame is printed. + void PrintStackTrace(raw_ostream &OS, int Depth = 0); + + // Run all registered signal handlers. + void RunSignalHandlers(); + + using SignalHandlerCallback = void (*)(void *); + + /// Add a function to be called when an abort/kill signal is delivered to the + /// process. The handler can have a cookie passed to it to identify what + /// instance of the handler it is. + void AddSignalHandler(SignalHandlerCallback FnPtr, void *Cookie); + + /// This function registers a function to be called when the user "interrupts" + /// the program (typically by pressing ctrl-c). When the user interrupts the + /// program, the specified interrupt function is called instead of the program + /// being killed, and the interrupt function automatically disabled. + /// + /// Note that interrupt functions are not allowed to call any non-reentrant + /// functions. An null interrupt function pointer disables the current + /// installed function. Note also that the handler may be executed on a + /// different thread on some platforms. + void SetInterruptFunction(void (*IF)()); + + /// Registers a function to be called when an "info" signal is delivered to + /// the process. + /// + /// On POSIX systems, this will be SIGUSR1; on systems that have it, SIGINFO + /// will also be used (typically ctrl-t). + /// + /// Note that signal handlers are not allowed to call any non-reentrant + /// functions. An null function pointer disables the current installed + /// function. Note also that the handler may be executed on a different + /// thread on some platforms. + void SetInfoSignalFunction(void (*Handler)()); + + /// Registers a function to be called in a "one-shot" manner when a pipe + /// signal is delivered to the process (i.e., on a failed write to a pipe). + /// After the pipe signal is handled once, the handler is unregistered. + /// + /// The LLVM signal handling code will not install any handler for the pipe + /// signal unless one is provided with this API (see \ref + /// DefaultOneShotPipeSignalHandler). This handler must be provided before + /// any other LLVM signal handlers are installed: the \ref InitLLVM + /// constructor has a flag that can simplify this setup. + /// + /// Note that the handler is not allowed to call any non-reentrant + /// functions. A null handler pointer disables the current installed + /// function. Note also that the handler may be executed on a + /// different thread on some platforms. + void SetOneShotPipeSignalFunction(void (*Handler)()); + + /// On Unix systems and Windows, this function exits with an "IO error" exit + /// code. + void DefaultOneShotPipeSignalHandler(); + +#ifdef _WIN32 + /// Windows does not support signals and this handler must be called manually. + void CallOneShotPipeSignalHandler(); +#endif + + /// This function does the following: + /// - clean up any temporary files registered with RemoveFileOnSignal() + /// - dump the callstack from the exception context + /// - call any relevant interrupt/signal handlers + /// - create a core/mini dump of the exception context whenever possible + /// Context is a system-specific failure context: it is the signal type on + /// Unix; the ExceptionContext on Windows. + void CleanupOnSignal(uintptr_t Context); + + void unregisterHandlers(); +} // End sys namespace +} // End llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Signposts.h b/contrib/libs/llvm16/include/llvm/Support/Signposts.h new file mode 100644 index 00000000000..ce4c271caa7 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Signposts.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Signposts.h - Interval debug annotations ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file Some OS's provide profilers that allow applications to provide custom +/// annotations to the profiler. For example, on Xcode 10 and later 'signposts' +/// can be emitted by the application and these will be rendered to the Points +/// of Interest track on the instruments timeline. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SIGNPOSTS_H +#define LLVM_SUPPORT_SIGNPOSTS_H + +#include <memory> + +namespace llvm { +class SignpostEmitterImpl; +class StringRef; + +/// Manages the emission of signposts into the recording method supported by +/// the OS. +class SignpostEmitter { + std::unique_ptr<SignpostEmitterImpl> Impl; + +public: + SignpostEmitter(); + ~SignpostEmitter(); + + bool isEnabled() const; + + /// Begin a signposted interval for a given object. + void startInterval(const void *O, StringRef Name); + /// End a signposted interval for a given object. + void endInterval(const void *O, StringRef Name); +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_SIGNPOSTS_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SmallVectorMemoryBuffer.h b/contrib/libs/llvm16/include/llvm/Support/SmallVectorMemoryBuffer.h new file mode 100644 index 00000000000..18d3033ee58 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SmallVectorMemoryBuffer.h @@ -0,0 +1,74 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SmallVectorMemoryBuffer.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares a wrapper class to hold the memory into which an +// object will be generated. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SMALLVECTORMEMORYBUFFER_H +#define LLVM_SUPPORT_SMALLVECTORMEMORYBUFFER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// SmallVector-backed MemoryBuffer instance. +/// +/// This class enables efficient construction of MemoryBuffers from SmallVector +/// instances. This is useful for MCJIT and Orc, where object files are streamed +/// into SmallVectors, then inspected using ObjectFile (which takes a +/// MemoryBuffer). +class SmallVectorMemoryBuffer : public MemoryBuffer { +public: + /// Construct a SmallVectorMemoryBuffer from the given SmallVector r-value. + SmallVectorMemoryBuffer(SmallVectorImpl<char> &&SV, + bool RequiresNullTerminator = true) + : SmallVectorMemoryBuffer(std::move(SV), "<in-memory object>", + RequiresNullTerminator) {} + + /// Construct a named SmallVectorMemoryBuffer from the given SmallVector + /// r-value and StringRef. + SmallVectorMemoryBuffer(SmallVectorImpl<char> &&SV, StringRef Name, + bool RequiresNullTerminator = true) + : SV(std::move(SV)), BufferName(std::string(Name)) { + if (RequiresNullTerminator) { + this->SV.push_back('\0'); + this->SV.pop_back(); + } + init(this->SV.begin(), this->SV.end(), false); + } + + // Key function. + ~SmallVectorMemoryBuffer() override; + + StringRef getBufferIdentifier() const override { return BufferName; } + + BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; } + +private: + SmallVector<char, 0> SV; + std::string BufferName; +}; + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SourceMgr.h b/contrib/libs/llvm16/include/llvm/Support/SourceMgr.h new file mode 100644 index 00000000000..02371ce7225 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SourceMgr.h @@ -0,0 +1,336 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SourceMgr.h - Manager for Source Buffers & Diagnostics ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the SMDiagnostic and SourceMgr classes. This +// provides a simple substrate for diagnostics, #include handling, and other low +// level things for simple parsers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SOURCEMGR_H +#define LLVM_SUPPORT_SOURCEMGR_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SMLoc.h" +#include <vector> + +namespace llvm { + +class raw_ostream; +class SMDiagnostic; +class SMFixIt; + +/// This owns the files read by a parser, handles include stacks, +/// and handles diagnostic wrangling. +class SourceMgr { +public: + enum DiagKind { + DK_Error, + DK_Warning, + DK_Remark, + DK_Note, + }; + + /// Clients that want to handle their own diagnostics in a custom way can + /// register a function pointer+context as a diagnostic handler. + /// It gets called each time PrintMessage is invoked. + using DiagHandlerTy = void (*)(const SMDiagnostic &, void *Context); + +private: + struct SrcBuffer { + /// The memory buffer for the file. + std::unique_ptr<MemoryBuffer> Buffer; + + /// Vector of offsets into Buffer at which there are line-endings + /// (lazily populated). Once populated, the '\n' that marks the end of + /// line number N from [1..] is at Buffer[OffsetCache[N-1]]. Since + /// these offsets are in sorted (ascending) order, they can be + /// binary-searched for the first one after any given offset (eg. an + /// offset corresponding to a particular SMLoc). + /// + /// Since we're storing offsets into relatively small files (often smaller + /// than 2^8 or 2^16 bytes), we select the offset vector element type + /// dynamically based on the size of Buffer. + mutable void *OffsetCache = nullptr; + + /// Look up a given \p Ptr in in the buffer, determining which line it came + /// from. + unsigned getLineNumber(const char *Ptr) const; + template <typename T> + unsigned getLineNumberSpecialized(const char *Ptr) const; + + /// Return a pointer to the first character of the specified line number or + /// null if the line number is invalid. + const char *getPointerForLineNumber(unsigned LineNo) const; + template <typename T> + const char *getPointerForLineNumberSpecialized(unsigned LineNo) const; + + /// This is the location of the parent include, or null if at the top level. + SMLoc IncludeLoc; + + SrcBuffer() = default; + SrcBuffer(SrcBuffer &&); + SrcBuffer(const SrcBuffer &) = delete; + SrcBuffer &operator=(const SrcBuffer &) = delete; + ~SrcBuffer(); + }; + + /// This is all of the buffers that we are reading from. + std::vector<SrcBuffer> Buffers; + + // This is the list of directories we should search for include files in. + std::vector<std::string> IncludeDirectories; + + DiagHandlerTy DiagHandler = nullptr; + void *DiagContext = nullptr; + + bool isValidBufferID(unsigned i) const { return i && i <= Buffers.size(); } + +public: + SourceMgr() = default; + SourceMgr(const SourceMgr &) = delete; + SourceMgr &operator=(const SourceMgr &) = delete; + SourceMgr(SourceMgr &&) = default; + SourceMgr &operator=(SourceMgr &&) = default; + ~SourceMgr() = default; + + /// Return the include directories of this source manager. + ArrayRef<std::string> getIncludeDirs() const { return IncludeDirectories; } + + void setIncludeDirs(const std::vector<std::string> &Dirs) { + IncludeDirectories = Dirs; + } + + /// Specify a diagnostic handler to be invoked every time PrintMessage is + /// called. \p Ctx is passed into the handler when it is invoked. + void setDiagHandler(DiagHandlerTy DH, void *Ctx = nullptr) { + DiagHandler = DH; + DiagContext = Ctx; + } + + DiagHandlerTy getDiagHandler() const { return DiagHandler; } + void *getDiagContext() const { return DiagContext; } + + const SrcBuffer &getBufferInfo(unsigned i) const { + assert(isValidBufferID(i)); + return Buffers[i - 1]; + } + + const MemoryBuffer *getMemoryBuffer(unsigned i) const { + assert(isValidBufferID(i)); + return Buffers[i - 1].Buffer.get(); + } + + unsigned getNumBuffers() const { return Buffers.size(); } + + unsigned getMainFileID() const { + assert(getNumBuffers()); + return 1; + } + + SMLoc getParentIncludeLoc(unsigned i) const { + assert(isValidBufferID(i)); + return Buffers[i - 1].IncludeLoc; + } + + /// Add a new source buffer to this source manager. This takes ownership of + /// the memory buffer. + unsigned AddNewSourceBuffer(std::unique_ptr<MemoryBuffer> F, + SMLoc IncludeLoc) { + SrcBuffer NB; + NB.Buffer = std::move(F); + NB.IncludeLoc = IncludeLoc; + Buffers.push_back(std::move(NB)); + return Buffers.size(); + } + + /// Takes the source buffers from the given source manager and append them to + /// the current manager. `MainBufferIncludeLoc` is an optional include + /// location to attach to the main buffer of `SrcMgr` after it gets moved to + /// the current manager. + void takeSourceBuffersFrom(SourceMgr &SrcMgr, + SMLoc MainBufferIncludeLoc = SMLoc()) { + if (SrcMgr.Buffers.empty()) + return; + + size_t OldNumBuffers = getNumBuffers(); + std::move(SrcMgr.Buffers.begin(), SrcMgr.Buffers.end(), + std::back_inserter(Buffers)); + SrcMgr.Buffers.clear(); + Buffers[OldNumBuffers].IncludeLoc = MainBufferIncludeLoc; + } + + /// Search for a file with the specified name in the current directory or in + /// one of the IncludeDirs. + /// + /// If no file is found, this returns 0, otherwise it returns the buffer ID + /// of the stacked file. The full path to the included file can be found in + /// \p IncludedFile. + unsigned AddIncludeFile(const std::string &Filename, SMLoc IncludeLoc, + std::string &IncludedFile); + + /// Search for a file with the specified name in the current directory or in + /// one of the IncludeDirs, and try to open it **without** adding to the + /// SourceMgr. If the opened file is intended to be added to the source + /// manager, prefer `AddIncludeFile` instead. + /// + /// If no file is found, this returns an Error, otherwise it returns the + /// buffer of the stacked file. The full path to the included file can be + /// found in \p IncludedFile. + ErrorOr<std::unique_ptr<MemoryBuffer>> + OpenIncludeFile(const std::string &Filename, std::string &IncludedFile); + + /// Return the ID of the buffer containing the specified location. + /// + /// 0 is returned if the buffer is not found. + unsigned FindBufferContainingLoc(SMLoc Loc) const; + + /// Find the line number for the specified location in the specified file. + /// This is not a fast method. + unsigned FindLineNumber(SMLoc Loc, unsigned BufferID = 0) const { + return getLineAndColumn(Loc, BufferID).first; + } + + /// Find the line and column number for the specified location in the + /// specified file. This is not a fast method. + std::pair<unsigned, unsigned> getLineAndColumn(SMLoc Loc, + unsigned BufferID = 0) const; + + /// Get a string with the \p SMLoc filename and line number + /// formatted in the standard style. + std::string getFormattedLocationNoOffset(SMLoc Loc, + bool IncludePath = false) const; + + /// Given a line and column number in a mapped buffer, turn it into an SMLoc. + /// This will return a null SMLoc if the line/column location is invalid. + SMLoc FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo, + unsigned ColNo); + + /// Emit a message about the specified location with the specified string. + /// + /// \param ShowColors Display colored messages if output is a terminal and + /// the default error handler is used. + void PrintMessage(raw_ostream &OS, SMLoc Loc, DiagKind Kind, const Twine &Msg, + ArrayRef<SMRange> Ranges = {}, + ArrayRef<SMFixIt> FixIts = {}, + bool ShowColors = true) const; + + /// Emits a diagnostic to llvm::errs(). + void PrintMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, + ArrayRef<SMRange> Ranges = {}, + ArrayRef<SMFixIt> FixIts = {}, + bool ShowColors = true) const; + + /// Emits a manually-constructed diagnostic to the given output stream. + /// + /// \param ShowColors Display colored messages if output is a terminal and + /// the default error handler is used. + void PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, + bool ShowColors = true) const; + + /// Return an SMDiagnostic at the specified location with the specified + /// string. + /// + /// \param Msg If non-null, the kind of message (e.g., "error") which is + /// prefixed to the message. + SMDiagnostic GetMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, + ArrayRef<SMRange> Ranges = {}, + ArrayRef<SMFixIt> FixIts = {}) const; + + /// Prints the names of included files and the line of the file they were + /// included from. A diagnostic handler can use this before printing its + /// custom formatted message. + /// + /// \param IncludeLoc The location of the include. + /// \param OS the raw_ostream to print on. + void PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const; +}; + +/// Represents a single fixit, a replacement of one range of text with another. +class SMFixIt { + SMRange Range; + + std::string Text; + +public: + SMFixIt(SMRange R, const Twine &Replacement); + + SMFixIt(SMLoc Loc, const Twine &Replacement) + : SMFixIt(SMRange(Loc, Loc), Replacement) {} + + StringRef getText() const { return Text; } + SMRange getRange() const { return Range; } + + bool operator<(const SMFixIt &Other) const { + if (Range.Start.getPointer() != Other.Range.Start.getPointer()) + return Range.Start.getPointer() < Other.Range.Start.getPointer(); + if (Range.End.getPointer() != Other.Range.End.getPointer()) + return Range.End.getPointer() < Other.Range.End.getPointer(); + return Text < Other.Text; + } +}; + +/// Instances of this class encapsulate one diagnostic report, allowing +/// printing to a raw_ostream as a caret diagnostic. +class SMDiagnostic { + const SourceMgr *SM = nullptr; + SMLoc Loc; + std::string Filename; + int LineNo = 0; + int ColumnNo = 0; + SourceMgr::DiagKind Kind = SourceMgr::DK_Error; + std::string Message, LineContents; + std::vector<std::pair<unsigned, unsigned>> Ranges; + SmallVector<SMFixIt, 4> FixIts; + +public: + // Null diagnostic. + SMDiagnostic() = default; + // Diagnostic with no location (e.g. file not found, command line arg error). + SMDiagnostic(StringRef filename, SourceMgr::DiagKind Knd, StringRef Msg) + : Filename(filename), LineNo(-1), ColumnNo(-1), Kind(Knd), Message(Msg) {} + + // Diagnostic with a location. + SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, int Line, int Col, + SourceMgr::DiagKind Kind, StringRef Msg, StringRef LineStr, + ArrayRef<std::pair<unsigned, unsigned>> Ranges, + ArrayRef<SMFixIt> FixIts = {}); + + const SourceMgr *getSourceMgr() const { return SM; } + SMLoc getLoc() const { return Loc; } + StringRef getFilename() const { return Filename; } + int getLineNo() const { return LineNo; } + int getColumnNo() const { return ColumnNo; } + SourceMgr::DiagKind getKind() const { return Kind; } + StringRef getMessage() const { return Message; } + StringRef getLineContents() const { return LineContents; } + ArrayRef<std::pair<unsigned, unsigned>> getRanges() const { return Ranges; } + + void addFixIt(const SMFixIt &Hint) { FixIts.push_back(Hint); } + + ArrayRef<SMFixIt> getFixIts() const { return FixIts; } + + void print(const char *ProgName, raw_ostream &S, bool ShowColors = true, + bool ShowKindLabel = true) const; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_SOURCEMGR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SpecialCaseList.h b/contrib/libs/llvm16/include/llvm/Support/SpecialCaseList.h new file mode 100644 index 00000000000..515f7ba0f2a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SpecialCaseList.h @@ -0,0 +1,170 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- SpecialCaseList.h - special case list for sanitizers ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// +// +// 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 an "ABI list" for DataFlowSanitizer and allow/exclusion lists for +// sanitizers like AddressSanitizer or UndefinedBehaviorSanitizer. +// +// 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 "prefix" and "category" are sanitizer-specific. For example, +// sanitizer exclusion support prefixes "src", "mainfile", "fun" and "global". +// Wildcard expressions define, respectively, source files, main files, +// functions or globals which shouldn't be instrumented. +// Examples of categories: +// "functional": used in DFSan to list functions with pure functional +// semantics. +// "init": used in ASan exclusion list to disable initialization-order bugs +// detection for certain globals or source files. +// Full special case list file example: +// --- +// [address] +// # Excluded items: +// fun:*_ZN4base6subtle* +// global:*global_with_bad_access_or_initialization* +// global:*global_with_initialization_issues*=init +// type:*Namespace::ClassName*=init +// src:file_with_tricky_code.cc +// src:ignore-global-initializers-issues.cc=init +// mainfile:main_file.cc +// +// [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 .* +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SPECIALCASELIST_H +#define LLVM_SUPPORT_SPECIALCASELIST_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/TrigramIndex.h" +#include <memory> +#include <string> +#include <vector> + +namespace llvm { +class MemoryBuffer; +class StringRef; + +namespace vfs { +class FileSystem; +} + +class SpecialCaseList { +public: + /// Parses the special case list entries from files. On failure, returns + /// 0 and writes an error message to string. + static std::unique_ptr<SpecialCaseList> + create(const std::vector<std::string> &Paths, llvm::vfs::FileSystem &FS, + std::string &Error); + /// Parses the special case list from a memory buffer. On failure, returns + /// 0 and writes an error message to string. + static std::unique_ptr<SpecialCaseList> create(const MemoryBuffer *MB, + std::string &Error); + /// Parses the special case list entries from files. On failure, reports a + /// fatal error. + static std::unique_ptr<SpecialCaseList> + createOrDie(const std::vector<std::string> &Paths, llvm::vfs::FileSystem &FS); + + ~SpecialCaseList(); + + /// Returns true, if special case list contains a line + /// \code + /// @Prefix:<E>=@Category + /// \endcode + /// where @Query satisfies wildcard expression <E> in a given @Section. + bool inSection(StringRef Section, StringRef Prefix, StringRef Query, + StringRef Category = StringRef()) const; + + /// 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 exclusion 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, + vfs::FileSystem &VFS, std::string &Error); + bool createInternal(const MemoryBuffer *MB, std::string &Error); + + SpecialCaseList() = default; + SpecialCaseList(SpecialCaseList const &) = delete; + SpecialCaseList &operator=(SpecialCaseList const &) = delete; + + /// 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; + + /// Parses just-constructed SpecialCaseList entries from a memory buffer. + 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 + +#endif // LLVM_SUPPORT_SPECIALCASELIST_H + + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/StringSaver.h b/contrib/libs/llvm16/include/llvm/Support/StringSaver.h new file mode 100644 index 00000000000..3f0b26aab41 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/StringSaver.h @@ -0,0 +1,70 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/StringSaver.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_STRINGSAVER_H +#define LLVM_SUPPORT_STRINGSAVER_H + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Allocator.h" + +namespace llvm { + +/// Saves strings in the provided stable storage and returns a +/// StringRef with a stable character pointer. +class StringSaver final { + BumpPtrAllocator &Alloc; + +public: + StringSaver(BumpPtrAllocator &Alloc) : Alloc(Alloc) {} + + BumpPtrAllocator &getAllocator() const { return Alloc; } + + // All returned strings are null-terminated: *save(S).end() == 0. + StringRef save(const char *S) { return save(StringRef(S)); } + StringRef save(StringRef S); + StringRef save(const Twine &S) { return save(StringRef(S.str())); } + StringRef save(const std::string &S) { return save(StringRef(S)); } +}; + +/// Saves strings in the provided stable storage and returns a StringRef with a +/// stable character pointer. Saving the same string yields the same StringRef. +/// +/// Compared to StringSaver, it does more work but avoids saving the same string +/// multiple times. +/// +/// Compared to StringPool, it performs fewer allocations but doesn't support +/// refcounting/deletion. +class UniqueStringSaver final { + StringSaver Strings; + llvm::DenseSet<llvm::StringRef> Unique; + +public: + UniqueStringSaver(BumpPtrAllocator &Alloc) : Strings(Alloc) {} + + // All returned strings are null-terminated: *save(S).end() == 0. + StringRef save(const char *S) { return save(StringRef(S)); } + StringRef save(StringRef S); + StringRef save(const Twine &S) { return save(StringRef(S.str())); } + StringRef save(const std::string &S) { return save(StringRef(S)); } +}; + +} +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SuffixTree.h b/contrib/libs/llvm16/include/llvm/Support/SuffixTree.h new file mode 100644 index 00000000000..1b0da216bfc --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SuffixTree.h @@ -0,0 +1,361 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/ADT/SuffixTree.h - Tree for substrings --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the Suffix Tree class and Suffix Tree Node struct. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_SUFFIXTREE_H +#define LLVM_SUPPORT_SUFFIXTREE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Allocator.h" +#include <vector> + +namespace llvm { + +/// Represents an undefined index in the suffix tree. +const unsigned EmptyIdx = -1; + +/// A node in a suffix tree which represents a substring or suffix. +/// +/// Each node has either no children or at least two children, with the root +/// being a exception in the empty tree. +/// +/// Children are represented as a map between unsigned integers and nodes. If +/// a node N has a child M on unsigned integer k, then the mapping represented +/// by N is a proper prefix of the mapping represented by M. Note that this, +/// although similar to a trie is somewhat different: each node stores a full +/// substring of the full mapping rather than a single character state. +/// +/// Each internal node contains a pointer to the internal node representing +/// the same string, but with the first character chopped off. This is stored +/// in \p Link. Each leaf node stores the start index of its respective +/// suffix in \p SuffixIdx. +struct SuffixTreeNode { + + /// The children of this node. + /// + /// A child existing on an unsigned integer implies that from the mapping + /// represented by the current node, there is a way to reach another + /// mapping by tacking that character on the end of the current string. + llvm::DenseMap<unsigned, SuffixTreeNode *> Children; + + /// The start index of this node's substring in the main string. + unsigned StartIdx = EmptyIdx; + + /// The end index of this node's substring in the main string. + /// + /// Every leaf node must have its \p EndIdx incremented at the end of every + /// step in the construction algorithm. To avoid having to update O(N) + /// nodes individually at the end of every step, the end index is stored + /// as a pointer. + unsigned *EndIdx = nullptr; + + /// For leaves, the start index of the suffix represented by this node. + /// + /// For all other nodes, this is ignored. + unsigned SuffixIdx = EmptyIdx; + + /// For internal nodes, a pointer to the internal node representing + /// the same sequence with the first character chopped off. + /// + /// This acts as a shortcut in Ukkonen's algorithm. One of the things that + /// Ukkonen's algorithm does to achieve linear-time construction is + /// keep track of which node the next insert should be at. This makes each + /// insert O(1), and there are a total of O(N) inserts. The suffix link + /// helps with inserting children of internal nodes. + /// + /// Say we add a child to an internal node with associated mapping S. The + /// next insertion must be at the node representing S - its first character. + /// This is given by the way that we iteratively build the tree in Ukkonen's + /// algorithm. The main idea is to look at the suffixes of each prefix in the + /// string, starting with the longest suffix of the prefix, and ending with + /// the shortest. Therefore, if we keep pointers between such nodes, we can + /// move to the next insertion point in O(1) time. If we don't, then we'd + /// have to query from the root, which takes O(N) time. This would make the + /// construction algorithm O(N^2) rather than O(N). + SuffixTreeNode *Link = nullptr; + + /// The length of the string formed by concatenating the edge labels from the + /// root to this node. + unsigned ConcatLen = 0; + + /// Returns true if this node is a leaf. + bool isLeaf() const { return SuffixIdx != EmptyIdx; } + + /// Returns true if this node is the root of its owning \p SuffixTree. + bool isRoot() const { return StartIdx == EmptyIdx; } + + /// Return the number of elements in the substring associated with this node. + size_t size() const { + + // Is it the root? If so, it's the empty string so return 0. + if (isRoot()) + return 0; + + assert(*EndIdx != EmptyIdx && "EndIdx is undefined!"); + + // Size = the number of elements in the string. + // For example, [0 1 2 3] has length 4, not 3. 3-0 = 3, so we have 3-0+1. + return *EndIdx - StartIdx + 1; + } + + SuffixTreeNode(unsigned StartIdx, unsigned *EndIdx, SuffixTreeNode *Link) + : StartIdx(StartIdx), EndIdx(EndIdx), Link(Link) {} + + SuffixTreeNode() = default; +}; + +/// A data structure for fast substring queries. +/// +/// Suffix trees represent the suffixes of their input strings in their leaves. +/// A suffix tree is a type of compressed trie structure where each node +/// represents an entire substring rather than a single character. Each leaf +/// of the tree is a suffix. +/// +/// A suffix tree can be seen as a type of state machine where each state is a +/// substring of the full string. The tree is structured so that, for a string +/// of length N, there are exactly N leaves in the tree. This structure allows +/// us to quickly find repeated substrings of the input string. +/// +/// In this implementation, a "string" is a vector of unsigned integers. +/// These integers may result from hashing some data type. A suffix tree can +/// contain 1 or many strings, which can then be queried as one large string. +/// +/// The suffix tree is implemented using Ukkonen's algorithm for linear-time +/// suffix tree construction. Ukkonen's algorithm is explained in more detail +/// in the paper by Esko Ukkonen "On-line construction of suffix trees. The +/// paper is available at +/// +/// https://www.cs.helsinki.fi/u/ukkonen/SuffixT1withFigs.pdf +class SuffixTree { +public: + /// Each element is an integer representing an instruction in the module. + llvm::ArrayRef<unsigned> Str; + + /// A repeated substring in the tree. + struct RepeatedSubstring { + /// The length of the string. + unsigned Length; + + /// The start indices of each occurrence. + std::vector<unsigned> StartIndices; + }; + +private: + /// Maintains each node in the tree. + llvm::SpecificBumpPtrAllocator<SuffixTreeNode> NodeAllocator; + + /// The root of the suffix tree. + /// + /// The root represents the empty string. It is maintained by the + /// \p NodeAllocator like every other node in the tree. + SuffixTreeNode *Root = nullptr; + + /// Maintains the end indices of the internal nodes in the tree. + /// + /// Each internal node is guaranteed to never have its end index change + /// during the construction algorithm; however, leaves must be updated at + /// every step. Therefore, we need to store leaf end indices by reference + /// to avoid updating O(N) leaves at every step of construction. Thus, + /// every internal node must be allocated its own end index. + llvm::BumpPtrAllocator InternalEndIdxAllocator; + + /// The end index of each leaf in the tree. + unsigned LeafEndIdx = -1; + + /// Helper struct which keeps track of the next insertion point in + /// Ukkonen's algorithm. + struct ActiveState { + /// The next node to insert at. + SuffixTreeNode *Node = nullptr; + + /// The index of the first character in the substring currently being added. + unsigned Idx = EmptyIdx; + + /// The length of the substring we have to add at the current step. + unsigned Len = 0; + }; + + /// The point the next insertion will take place at in the + /// construction algorithm. + ActiveState Active; + + /// Allocate a leaf node and add it to the tree. + /// + /// \param Parent The parent of this node. + /// \param StartIdx The start index of this node's associated string. + /// \param Edge The label on the edge leaving \p Parent to this node. + /// + /// \returns A pointer to the allocated leaf node. + SuffixTreeNode *insertLeaf(SuffixTreeNode &Parent, unsigned StartIdx, + unsigned Edge); + + /// Allocate an internal node and add it to the tree. + /// + /// \param Parent The parent of this node. Only null when allocating the root. + /// \param StartIdx The start index of this node's associated string. + /// \param EndIdx The end index of this node's associated string. + /// \param Edge The label on the edge leaving \p Parent to this node. + /// + /// \returns A pointer to the allocated internal node. + SuffixTreeNode *insertInternalNode(SuffixTreeNode *Parent, unsigned StartIdx, + unsigned EndIdx, unsigned Edge); + + /// Set the suffix indices of the leaves to the start indices of their + /// respective suffixes. + void setSuffixIndices(); + + /// Construct the suffix tree for the prefix of the input ending at + /// \p EndIdx. + /// + /// Used to construct the full suffix tree iteratively. At the end of each + /// step, the constructed suffix tree is either a valid suffix tree, or a + /// suffix tree with implicit suffixes. At the end of the final step, the + /// suffix tree is a valid tree. + /// + /// \param EndIdx The end index of the current prefix in the main string. + /// \param SuffixesToAdd The number of suffixes that must be added + /// to complete the suffix tree at the current phase. + /// + /// \returns The number of suffixes that have not been added at the end of + /// this step. + unsigned extend(unsigned EndIdx, unsigned SuffixesToAdd); + +public: + /// Construct a suffix tree from a sequence of unsigned integers. + /// + /// \param Str The string to construct the suffix tree for. + SuffixTree(const std::vector<unsigned> &Str); + + /// Iterator for finding all repeated substrings in the suffix tree. + struct RepeatedSubstringIterator { + private: + /// The current node we're visiting. + SuffixTreeNode *N = nullptr; + + /// The repeated substring associated with this node. + RepeatedSubstring RS; + + /// The nodes left to visit. + std::vector<SuffixTreeNode *> ToVisit; + + /// The minimum length of a repeated substring to find. + /// Since we're outlining, we want at least two instructions in the range. + /// FIXME: This may not be true for targets like X86 which support many + /// instruction lengths. + const unsigned MinLength = 2; + + /// Move the iterator to the next repeated substring. + void advance() { + // Clear the current state. If we're at the end of the range, then this + // is the state we want to be in. + RS = RepeatedSubstring(); + N = nullptr; + + // Each leaf node represents a repeat of a string. + std::vector<SuffixTreeNode *> LeafChildren; + + // Continue visiting nodes until we find one which repeats more than once. + while (!ToVisit.empty()) { + SuffixTreeNode *Curr = ToVisit.back(); + ToVisit.pop_back(); + LeafChildren.clear(); + + // Keep track of the length of the string associated with the node. If + // it's too short, we'll quit. + unsigned Length = Curr->ConcatLen; + + // Iterate over each child, saving internal nodes for visiting, and + // leaf nodes in LeafChildren. Internal nodes represent individual + // strings, which may repeat. + for (auto &ChildPair : Curr->Children) { + // Save all of this node's children for processing. + if (!ChildPair.second->isLeaf()) + ToVisit.push_back(ChildPair.second); + + // It's not an internal node, so it must be a leaf. If we have a + // long enough string, then save the leaf children. + else if (Length >= MinLength) + LeafChildren.push_back(ChildPair.second); + } + + // The root never represents a repeated substring. If we're looking at + // that, then skip it. + if (Curr->isRoot()) + continue; + + // Do we have any repeated substrings? + if (LeafChildren.size() >= 2) { + // Yes. Update the state to reflect this, and then bail out. + N = Curr; + RS.Length = Length; + for (SuffixTreeNode *Leaf : LeafChildren) + RS.StartIndices.push_back(Leaf->SuffixIdx); + break; + } + } + + // At this point, either NewRS is an empty RepeatedSubstring, or it was + // set in the above loop. Similarly, N is either nullptr, or the node + // associated with NewRS. + } + + public: + /// Return the current repeated substring. + RepeatedSubstring &operator*() { return RS; } + + RepeatedSubstringIterator &operator++() { + advance(); + return *this; + } + + RepeatedSubstringIterator operator++(int I) { + RepeatedSubstringIterator It(*this); + advance(); + return It; + } + + bool operator==(const RepeatedSubstringIterator &Other) const { + return N == Other.N; + } + bool operator!=(const RepeatedSubstringIterator &Other) const { + return !(*this == Other); + } + + RepeatedSubstringIterator(SuffixTreeNode *N) : N(N) { + // Do we have a non-null node? + if (N) { + // Yes. At the first step, we need to visit all of N's children. + // Note: This means that we visit N last. + ToVisit.push_back(N); + advance(); + } + } + }; + + typedef RepeatedSubstringIterator iterator; + iterator begin() { return iterator(Root); } + iterator end() { return iterator(nullptr); } +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_SUFFIXTREE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SwapByteOrder.h b/contrib/libs/llvm16/include/llvm/Support/SwapByteOrder.h new file mode 100644 index 00000000000..085df5b4168 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SwapByteOrder.h @@ -0,0 +1,130 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SwapByteOrder.h - Generic and optimized byte swaps -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares generic and optimized functions to swap the byte order of +// an integral type. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SWAPBYTEORDER_H +#define LLVM_SUPPORT_SWAPBYTEORDER_H + +#include "llvm/ADT/bit.h" +#include <cstddef> +#include <cstdint> +#include <type_traits> + +#if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) || \ + defined(__Fuchsia__) || defined(__EMSCRIPTEN__) +#include <endian.h> +#elif defined(_AIX) +#include <sys/machine.h> +#elif defined(__sun) +/* Solaris provides _BIG_ENDIAN/_LITTLE_ENDIAN selector in sys/types.h */ +#include <sys/types.h> +#define BIG_ENDIAN 4321 +#define LITTLE_ENDIAN 1234 +#if defined(_BIG_ENDIAN) +#define BYTE_ORDER BIG_ENDIAN +#else +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#elif defined(__MVS__) +#define BIG_ENDIAN 4321 +#define LITTLE_ENDIAN 1234 +#define BYTE_ORDER BIG_ENDIAN +#else +#if !defined(BYTE_ORDER) && !defined(_WIN32) +#include <machine/endian.h> +#endif +#endif + +namespace llvm { + +/// ByteSwap_16 - This function returns a byte-swapped representation of +/// the 16-bit argument. +inline uint16_t ByteSwap_16(uint16_t value) { return llvm::byteswap(value); } + +/// This function returns a byte-swapped representation of the 32-bit argument. +inline uint32_t ByteSwap_32(uint32_t value) { return llvm::byteswap(value); } + +/// This function returns a byte-swapped representation of the 64-bit argument. +inline uint64_t ByteSwap_64(uint64_t value) { return llvm::byteswap(value); } + +namespace sys { + +#if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN +constexpr bool IsBigEndianHost = true; +#else +constexpr bool IsBigEndianHost = false; +#endif + +static const bool IsLittleEndianHost = !IsBigEndianHost; + +inline unsigned char getSwappedBytes(unsigned char C) { return llvm::byteswap(C); } +inline signed char getSwappedBytes( signed char C) { return llvm::byteswap(C); } +inline char getSwappedBytes( char C) { return llvm::byteswap(C); } + +inline unsigned short getSwappedBytes(unsigned short C) { return llvm::byteswap(C); } +inline signed short getSwappedBytes( signed short C) { return llvm::byteswap(C); } + +inline unsigned int getSwappedBytes(unsigned int C) { return llvm::byteswap(C); } +inline signed int getSwappedBytes( signed int C) { return llvm::byteswap(C); } + +inline unsigned long getSwappedBytes(unsigned long C) { return llvm::byteswap(C); } +inline signed long getSwappedBytes( signed long C) { return llvm::byteswap(C); } + +inline unsigned long long getSwappedBytes(unsigned long long C) { return llvm::byteswap(C); } +inline signed long long getSwappedBytes( signed long long C) { return llvm::byteswap(C); } + +inline float getSwappedBytes(float C) { + union { + uint32_t i; + float f; + } in, out; + in.f = C; + out.i = llvm::byteswap(in.i); + return out.f; +} + +inline double getSwappedBytes(double C) { + union { + uint64_t i; + double d; + } in, out; + in.d = C; + out.i = llvm::byteswap(in.i); + return out.d; +} + +template <typename T> +inline std::enable_if_t<std::is_enum<T>::value, T> getSwappedBytes(T C) { + return static_cast<T>( + llvm::byteswap(static_cast<std::underlying_type_t<T>>(C))); +} + +template<typename T> +inline void swapByteOrder(T &Value) { + Value = getSwappedBytes(Value); +} + +} // end namespace sys +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SymbolRemappingReader.h b/contrib/libs/llvm16/include/llvm/Support/SymbolRemappingReader.h new file mode 100644 index 00000000000..8d87e6e6abc --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SymbolRemappingReader.h @@ -0,0 +1,144 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SymbolRemappingReader.h - Read symbol remapping file -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains definitions needed for reading and applying symbol +// remapping files. +// +// Support is provided only for the Itanium C++ name mangling scheme for now. +// +// NOTE: If you are making changes to this file format, please remember +// to document them in the Clang documentation at +// tools/clang/docs/UsersManual.rst. +// +// File format +// ----------- +// +// The symbol remappings are written as an ASCII text file. Blank lines and +// lines starting with a # are ignored. All other lines specify a kind of +// mangled name fragment, along with two fragments of that kind that should +// be treated as equivalent, separated by spaces. +// +// See http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling for a +// description of the Itanium name mangling scheme. +// +// The accepted fragment kinds are: +// +// * name A <name>, such as 6foobar or St3__1 +// * type A <type>, such as Ss or N4llvm9StringRefE +// * encoding An <encoding> (a complete mangling without the leading _Z) +// +// For example: +// +// # Ignore int / long differences to treat symbols from 32-bit and 64-bit +// # builds with differing size_t / ptrdiff_t / intptr_t as equivalent. +// type i l +// type j m +// +// # Ignore differences between libc++ and libstdc++, and between libstdc++'s +// # C++98 and C++11 ABIs. +// name 3std St3__1 +// name 3std St7__cxx11 +// +// # Remap a function overload to a specialization of a template (including +// # any local symbols declared within it). +// encoding N2NS1fEi N2NS1fIiEEvT_ +// +// # Substitutions must be remapped separately from namespace 'std' for now. +// name Sa NSt3__19allocatorE +// name Sb NSt3__112basic_stringE +// type Ss NSt3__112basic_stringIcSt11char_traitsIcESaE +// # ... +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SYMBOLREMAPPINGREADER_H +#define LLVM_SUPPORT_SYMBOLREMAPPINGREADER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ItaniumManglingCanonicalizer.h" + +namespace llvm { + +class MemoryBuffer; + +class SymbolRemappingParseError : public ErrorInfo<SymbolRemappingParseError> { +public: + SymbolRemappingParseError(StringRef File, int64_t Line, const Twine &Message) + : File(File), Line(Line), Message(Message.str()) {} + + void log(llvm::raw_ostream &OS) const override { + OS << File << ':' << Line << ": " << Message; + } + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + StringRef getFileName() const { return File; } + int64_t getLineNum() const { return Line; } + StringRef getMessage() const { return Message; } + + static char ID; + +private: + std::string File; + int64_t Line; + std::string Message; +}; + +/// Reader for symbol remapping files. +/// +/// Remaps the symbol names in profile data to match those in the program +/// according to a set of rules specified in a given file. +class SymbolRemappingReader { +public: + /// Read remappings from the given buffer, which must live as long as + /// the remapper. + Error read(MemoryBuffer &B); + + /// A Key represents an equivalence class of symbol names. + using Key = uintptr_t; + + /// Construct a key for the given symbol, or return an existing one if an + /// equivalent name has already been inserted. The symbol name must live + /// as long as the remapper. + /// + /// The result will be Key() if the name cannot be remapped (typically + /// because it is not a valid mangled name). + Key insert(StringRef FunctionName) { + return Canonicalizer.canonicalize(FunctionName); + } + + /// Map the given symbol name into the key for the corresponding equivalence + /// class. + /// + /// The result will typically be Key() if no equivalent symbol has been + /// inserted, but this is not guaranteed: a Key different from all keys ever + /// returned by \c insert may be returned instead. + Key lookup(StringRef FunctionName) { + return Canonicalizer.lookup(FunctionName); + } + +private: + ItaniumManglingCanonicalizer Canonicalizer; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_SYMBOLREMAPPINGREADER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/SystemUtils.h b/contrib/libs/llvm16/include/llvm/Support/SystemUtils.h new file mode 100644 index 00000000000..7b5e35b30f0 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/SystemUtils.h @@ -0,0 +1,41 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- SystemUtils.h - Utilities to do low-level system stuff ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains functions used to do a variety of low-level, often +// system-specific, tasks. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SYSTEMUTILS_H +#define LLVM_SUPPORT_SYSTEMUTILS_H + +namespace llvm { +class raw_ostream; + +/// Determine if the raw_ostream provided is connected to a terminal. If so, +/// generate a warning message to errs() advising against display of bitcode +/// and return true. Otherwise just return false. +/// Check for output written to a console +bool CheckBitcodeOutputToConsole( + raw_ostream &stream_to_check ///< The stream to be checked +); + +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TarWriter.h b/contrib/libs/llvm16/include/llvm/Support/TarWriter.h new file mode 100644 index 00000000000..ace31bef832 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TarWriter.h @@ -0,0 +1,44 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/TarWriter.h - Tar archive file creator -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TARWRITER_H +#define LLVM_SUPPORT_TARWRITER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +class TarWriter { +public: + static Expected<std::unique_ptr<TarWriter>> create(StringRef OutputPath, + StringRef BaseDir); + + void append(StringRef Path, StringRef Data); + +private: + TarWriter(int FD, StringRef BaseDir); + raw_fd_ostream OS; + std::string BaseDir; + StringSet<> Files; +}; +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TargetOpcodes.def b/contrib/libs/llvm16/include/llvm/Support/TargetOpcodes.def new file mode 100644 index 00000000000..d3fe1eec38d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TargetOpcodes.def @@ -0,0 +1,808 @@ +//===-- llvm/Support/TargetOpcodes.def - Target Indep Opcodes ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the target independent instruction opcodes. +// +//===----------------------------------------------------------------------===// + +// NOTE: NO INCLUDE GUARD DESIRED! + +/// HANDLE_TARGET_OPCODE defines an opcode and its associated enum value. +/// +#ifndef HANDLE_TARGET_OPCODE +#define HANDLE_TARGET_OPCODE(OPC, NUM) +#endif + +/// HANDLE_TARGET_OPCODE_MARKER defines an alternative identifier for an opcode. +/// +#ifndef HANDLE_TARGET_OPCODE_MARKER +#define HANDLE_TARGET_OPCODE_MARKER(IDENT, OPC) +#endif + +/// Every instruction defined here must also appear in Target.td. +/// +HANDLE_TARGET_OPCODE(PHI) +HANDLE_TARGET_OPCODE(INLINEASM) +HANDLE_TARGET_OPCODE(INLINEASM_BR) +HANDLE_TARGET_OPCODE(CFI_INSTRUCTION) +HANDLE_TARGET_OPCODE(EH_LABEL) +HANDLE_TARGET_OPCODE(GC_LABEL) +HANDLE_TARGET_OPCODE(ANNOTATION_LABEL) + +/// KILL - This instruction is a noop that is used only to adjust the +/// liveness of registers. This can be useful when dealing with +/// sub-registers. +HANDLE_TARGET_OPCODE(KILL) + +/// EXTRACT_SUBREG - This instruction takes two operands: a register +/// that has subregisters, and a subregister index. It returns the +/// extracted subregister value. This is commonly used to implement +/// truncation operations on target architectures which support it. +HANDLE_TARGET_OPCODE(EXTRACT_SUBREG) + +/// INSERT_SUBREG - This instruction takes three operands: a register that +/// has subregisters, a register providing an insert value, and a +/// subregister index. It returns the value of the first register with the +/// value of the second register inserted. The first register is often +/// defined by an IMPLICIT_DEF, because it is commonly used to implement +/// anyext operations on target architectures which support it. +HANDLE_TARGET_OPCODE(INSERT_SUBREG) + +/// IMPLICIT_DEF - This is the MachineInstr-level equivalent of undef. +HANDLE_TARGET_OPCODE(IMPLICIT_DEF) + +/// SUBREG_TO_REG - Assert the value of bits in a super register. +/// The result of this instruction is the value of the second operand inserted +/// into the subregister specified by the third operand. All other bits are +/// assumed to be equal to the bits in the immediate integer constant in the +/// first operand. This instruction just communicates information; No code +/// should be generated. +/// This is typically used after an instruction where the write to a subregister +/// implicitly cleared the bits in the super registers. +HANDLE_TARGET_OPCODE(SUBREG_TO_REG) + +/// COPY_TO_REGCLASS - This instruction is a placeholder for a plain +/// register-to-register copy into a specific register class. This is only +/// used between instruction selection and MachineInstr creation, before +/// virtual registers have been created for all the instructions, and it's +/// only needed in cases where the register classes implied by the +/// instructions are insufficient. It is emitted as a COPY MachineInstr. + HANDLE_TARGET_OPCODE(COPY_TO_REGCLASS) + +/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic +HANDLE_TARGET_OPCODE(DBG_VALUE) + +/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic with a variadic +/// list of locations +HANDLE_TARGET_OPCODE(DBG_VALUE_LIST) + +/// DBG_INSTR_REF - A mapping of llvm.dbg.value referring to the instruction +/// that defines the value, rather than a virtual register. +HANDLE_TARGET_OPCODE(DBG_INSTR_REF) + +/// DBG_PHI - remainder of a PHI, identifies a program point where values +/// merge under control flow. +HANDLE_TARGET_OPCODE(DBG_PHI) + +/// DBG_LABEL - a mapping of the llvm.dbg.label intrinsic +HANDLE_TARGET_OPCODE(DBG_LABEL) + +/// REG_SEQUENCE - This variadic instruction is used to form a register that +/// represents a consecutive sequence of sub-registers. It's used as a +/// register coalescing / allocation aid and must be eliminated before code +/// emission. +// In SDNode form, the first operand encodes the register class created by +// the REG_SEQUENCE, while each subsequent pair names a vreg + subreg index +// pair. Once it has been lowered to a MachineInstr, the regclass operand +// is no longer present. +/// e.g. v1027 = REG_SEQUENCE v1024, 3, v1025, 4, v1026, 5 +/// After register coalescing references of v1024 should be replace with +/// v1027:3, v1025 with v1027:4, etc. + HANDLE_TARGET_OPCODE(REG_SEQUENCE) + +/// COPY - Target-independent register copy. This instruction can also be +/// used to copy between subregisters of virtual registers. + HANDLE_TARGET_OPCODE(COPY) + +/// BUNDLE - This instruction represents an instruction bundle. Instructions +/// which immediately follow a BUNDLE instruction which are marked with +/// 'InsideBundle' flag are inside the bundle. +HANDLE_TARGET_OPCODE(BUNDLE) + +/// Lifetime markers. +HANDLE_TARGET_OPCODE(LIFETIME_START) +HANDLE_TARGET_OPCODE(LIFETIME_END) + +/// Pseudo probe +HANDLE_TARGET_OPCODE(PSEUDO_PROBE) + +/// Arithmetic fence. +HANDLE_TARGET_OPCODE(ARITH_FENCE) + +/// A Stackmap instruction captures the location of live variables at its +/// position in the instruction stream. It is followed by a shadow of bytes +/// that must lie within the function and not contain another stackmap. +HANDLE_TARGET_OPCODE(STACKMAP) + +/// FEntry all - This is a marker instruction which gets translated into a raw fentry call. +HANDLE_TARGET_OPCODE(FENTRY_CALL) + +/// Patchable call instruction - this instruction represents a call to a +/// constant address, followed by a series of NOPs. It is intended to +/// support optimizations for dynamic languages (such as javascript) that +/// rewrite calls to runtimes with more efficient code sequences. +/// This also implies a stack map. +HANDLE_TARGET_OPCODE(PATCHPOINT) + +/// This pseudo-instruction loads the stack guard value. Targets which need +/// to prevent the stack guard value or address from being spilled to the +/// stack should override TargetLowering::emitLoadStackGuardNode and +/// additionally expand this pseudo after register allocation. +HANDLE_TARGET_OPCODE(LOAD_STACK_GUARD) + +/// These are used to support call sites that must have the stack adjusted +/// before the call (e.g. to initialize an argument passed by value). +/// See llvm.call.preallocated.{setup,arg} in the LangRef for more details. +HANDLE_TARGET_OPCODE(PREALLOCATED_SETUP) +HANDLE_TARGET_OPCODE(PREALLOCATED_ARG) + +/// Call instruction with associated vm state for deoptimization and list +/// of live pointers for relocation by the garbage collector. It is +/// intended to support garbage collection with fully precise relocating +/// collectors and deoptimizations in either the callee or caller. +HANDLE_TARGET_OPCODE(STATEPOINT) + +/// Instruction that records the offset of a local stack allocation passed to +/// llvm.localescape. It has two arguments: the symbol for the label and the +/// frame index of the local stack allocation. +HANDLE_TARGET_OPCODE(LOCAL_ESCAPE) + +/// Wraps a machine instruction which can fault, bundled with associated +/// information on how to handle such a fault. +/// For example loading instruction that may page fault, bundled with associated +/// information on how to handle such a page fault. It is intended to support +/// "zero cost" null checks in managed languages by allowing LLVM to fold +/// comparisons into existing memory operations. +HANDLE_TARGET_OPCODE(FAULTING_OP) + +/// Wraps a machine instruction to add patchability constraints. An +/// instruction wrapped in PATCHABLE_OP has to either have a minimum +/// size or be preceded with a nop of that size. The first operand is +/// an immediate denoting the minimum size of the instruction, the +/// second operand is an immediate denoting the opcode of the original +/// instruction. The rest of the operands are the operands of the +/// original instruction. +/// PATCHABLE_OP can be used as second operand to only insert a nop of +/// required size. +HANDLE_TARGET_OPCODE(PATCHABLE_OP) + +/// This is a marker instruction which gets translated into a nop sled, useful +/// for inserting instrumentation instructions at runtime. +HANDLE_TARGET_OPCODE(PATCHABLE_FUNCTION_ENTER) + +/// Wraps a return instruction and its operands to enable adding nop sleds +/// either before or after the return. The nop sleds are useful for inserting +/// instrumentation instructions at runtime. +/// The patch here replaces the return instruction. +HANDLE_TARGET_OPCODE(PATCHABLE_RET) + +/// This is a marker instruction which gets translated into a nop sled, useful +/// for inserting instrumentation instructions at runtime. +/// The patch here prepends the return instruction. +/// The same thing as in x86_64 is not possible for ARM because it has multiple +/// return instructions. Furthermore, CPU allows parametrized and even +/// conditional return instructions. In the current ARM implementation we are +/// making use of the fact that currently LLVM doesn't seem to generate +/// conditional return instructions. +/// On ARM, the same instruction can be used for popping multiple registers +/// from the stack and returning (it just pops pc register too), and LLVM +/// generates it sometimes. So we can't insert the sled between this stack +/// adjustment and the return without splitting the original instruction into 2 +/// instructions. So on ARM, rather than jumping into the exit trampoline, we +/// call it, it does the tracing, preserves the stack and returns. +HANDLE_TARGET_OPCODE(PATCHABLE_FUNCTION_EXIT) + +/// Wraps a tail call instruction and its operands to enable adding nop sleds +/// either before or after the tail exit. We use this as a disambiguation from +/// PATCHABLE_RET which specifically only works for return instructions. +HANDLE_TARGET_OPCODE(PATCHABLE_TAIL_CALL) + +/// Wraps a logging call and its arguments with nop sleds. At runtime, this can +/// be patched to insert instrumentation instructions. +HANDLE_TARGET_OPCODE(PATCHABLE_EVENT_CALL) + +/// Wraps a typed logging call and its argument with nop sleds. At runtime, this +/// can be patched to insert instrumentation instructions. +HANDLE_TARGET_OPCODE(PATCHABLE_TYPED_EVENT_CALL) + +HANDLE_TARGET_OPCODE(ICALL_BRANCH_FUNNEL) + +// This is a fence with the singlethread scope. It represents a compiler memory +// barrier, but does not correspond to any generated instruction. +HANDLE_TARGET_OPCODE(MEMBARRIER) + +/// The following generic opcodes are not supposed to appear after ISel. +/// This is something we might want to relax, but for now, this is convenient +/// to produce diagnostics. + +/// Instructions which should not exist past instruction selection, but do not +/// generate code. These instructions only act as optimization hints. +HANDLE_TARGET_OPCODE(G_ASSERT_SEXT) +HANDLE_TARGET_OPCODE(G_ASSERT_ZEXT) +HANDLE_TARGET_OPCODE(G_ASSERT_ALIGN) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START, + G_ASSERT_SEXT) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END, + G_ASSERT_ALIGN) + +/// Generic ADD instruction. This is an integer add. +HANDLE_TARGET_OPCODE(G_ADD) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_START, G_ADD) + +/// Generic SUB instruction. This is an integer sub. +HANDLE_TARGET_OPCODE(G_SUB) + +// Generic multiply instruction. +HANDLE_TARGET_OPCODE(G_MUL) + +// Generic signed division instruction. +HANDLE_TARGET_OPCODE(G_SDIV) + +// Generic unsigned division instruction. +HANDLE_TARGET_OPCODE(G_UDIV) + +// Generic signed remainder instruction. +HANDLE_TARGET_OPCODE(G_SREM) + +// Generic unsigned remainder instruction. +HANDLE_TARGET_OPCODE(G_UREM) + +// Generic signed divrem instruction. +HANDLE_TARGET_OPCODE(G_SDIVREM) + +// Generic unsigned divrem instruction. +HANDLE_TARGET_OPCODE(G_UDIVREM) + +/// Generic bitwise and instruction. +HANDLE_TARGET_OPCODE(G_AND) + +/// Generic bitwise or instruction. +HANDLE_TARGET_OPCODE(G_OR) + +/// Generic bitwise exclusive-or instruction. +HANDLE_TARGET_OPCODE(G_XOR) + + +HANDLE_TARGET_OPCODE(G_IMPLICIT_DEF) + +/// Generic PHI instruction with types. +HANDLE_TARGET_OPCODE(G_PHI) + +/// Generic instruction to materialize the address of an alloca or other +/// stack-based object. +HANDLE_TARGET_OPCODE(G_FRAME_INDEX) + +/// Generic reference to global value. +HANDLE_TARGET_OPCODE(G_GLOBAL_VALUE) + +/// Generic instruction to extract blocks of bits from the register given +/// (typically a sub-register COPY after instruction selection). +HANDLE_TARGET_OPCODE(G_EXTRACT) + +HANDLE_TARGET_OPCODE(G_UNMERGE_VALUES) + +/// Generic instruction to insert blocks of bits from the registers given into +/// the source. +HANDLE_TARGET_OPCODE(G_INSERT) + +/// Generic instruction to paste a variable number of components together into a +/// larger register. +HANDLE_TARGET_OPCODE(G_MERGE_VALUES) + +/// Generic instruction to create a vector value from a number of scalar +/// components. +HANDLE_TARGET_OPCODE(G_BUILD_VECTOR) + +/// Generic instruction to create a vector value from a number of scalar +/// components, which have types larger than the result vector elt type. +HANDLE_TARGET_OPCODE(G_BUILD_VECTOR_TRUNC) + +/// Generic instruction to create a vector by concatenating multiple vectors. +HANDLE_TARGET_OPCODE(G_CONCAT_VECTORS) + +/// Generic pointer to int conversion. +HANDLE_TARGET_OPCODE(G_PTRTOINT) + +/// Generic int to pointer conversion. +HANDLE_TARGET_OPCODE(G_INTTOPTR) + +/// Generic bitcast. The source and destination types must be different, or a +/// COPY is the relevant instruction. +HANDLE_TARGET_OPCODE(G_BITCAST) + +/// Generic freeze. +HANDLE_TARGET_OPCODE(G_FREEZE) + +// INTRINSIC fptrunc_round intrinsic. +HANDLE_TARGET_OPCODE(G_INTRINSIC_FPTRUNC_ROUND) + +/// INTRINSIC trunc intrinsic. +HANDLE_TARGET_OPCODE(G_INTRINSIC_TRUNC) + +/// INTRINSIC round intrinsic. +HANDLE_TARGET_OPCODE(G_INTRINSIC_ROUND) + +/// INTRINSIC round to integer intrinsic. +HANDLE_TARGET_OPCODE(G_INTRINSIC_LRINT) + +/// INTRINSIC roundeven intrinsic. +HANDLE_TARGET_OPCODE(G_INTRINSIC_ROUNDEVEN) + +/// INTRINSIC readcyclecounter +HANDLE_TARGET_OPCODE(G_READCYCLECOUNTER) + +/// Generic load (including anyext load) +HANDLE_TARGET_OPCODE(G_LOAD) + +/// Generic signext load +HANDLE_TARGET_OPCODE(G_SEXTLOAD) + +/// Generic zeroext load +HANDLE_TARGET_OPCODE(G_ZEXTLOAD) + +/// Generic indexed load (including anyext load) +HANDLE_TARGET_OPCODE(G_INDEXED_LOAD) + +/// Generic indexed signext load +HANDLE_TARGET_OPCODE(G_INDEXED_SEXTLOAD) + +/// Generic indexed zeroext load +HANDLE_TARGET_OPCODE(G_INDEXED_ZEXTLOAD) + +/// Generic store. +HANDLE_TARGET_OPCODE(G_STORE) + +/// Generic indexed store. +HANDLE_TARGET_OPCODE(G_INDEXED_STORE) + +/// Generic atomic cmpxchg with internal success check. +HANDLE_TARGET_OPCODE(G_ATOMIC_CMPXCHG_WITH_SUCCESS) + +/// Generic atomic cmpxchg. +HANDLE_TARGET_OPCODE(G_ATOMIC_CMPXCHG) + +/// Generic atomicrmw. +HANDLE_TARGET_OPCODE(G_ATOMICRMW_XCHG) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_ADD) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_SUB) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_AND) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_NAND) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_OR) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_XOR) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_MAX) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_MIN) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_UMAX) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_UMIN) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_FADD) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_FSUB) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_FMAX) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_FMIN) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_UINC_WRAP) +HANDLE_TARGET_OPCODE(G_ATOMICRMW_UDEC_WRAP) + +// Marker for start of Generic AtomicRMW opcodes +HANDLE_TARGET_OPCODE_MARKER(GENERIC_ATOMICRMW_OP_START, G_ATOMICRMW_XCHG) + +// Marker for end of Generic AtomicRMW opcodes +HANDLE_TARGET_OPCODE_MARKER(GENERIC_ATOMICRMW_OP_END, G_ATOMICRMW_UDEC_WRAP) + +// Generic atomic fence +HANDLE_TARGET_OPCODE(G_FENCE) + +/// Generic conditional branch instruction. +HANDLE_TARGET_OPCODE(G_BRCOND) + +/// Generic indirect branch instruction. +HANDLE_TARGET_OPCODE(G_BRINDIRECT) + +/// Begin an invoke region marker. +HANDLE_TARGET_OPCODE(G_INVOKE_REGION_START) + +/// Generic intrinsic use (without side effects). +HANDLE_TARGET_OPCODE(G_INTRINSIC) + +/// Generic intrinsic use (with side effects). +HANDLE_TARGET_OPCODE(G_INTRINSIC_W_SIDE_EFFECTS) + +/// Generic extension allowing rubbish in high bits. +HANDLE_TARGET_OPCODE(G_ANYEXT) + +/// Generic instruction to discard the high bits of a register. This differs +/// from (G_EXTRACT val, 0) on its action on vectors: G_TRUNC will truncate +/// each element individually, G_EXTRACT will typically discard the high +/// elements of the vector. +HANDLE_TARGET_OPCODE(G_TRUNC) + +/// Generic integer constant. +HANDLE_TARGET_OPCODE(G_CONSTANT) + +/// Generic floating constant. +HANDLE_TARGET_OPCODE(G_FCONSTANT) + +/// Generic va_start instruction. Stores to its one pointer operand. +HANDLE_TARGET_OPCODE(G_VASTART) + +/// Generic va_start instruction. Stores to its one pointer operand. +HANDLE_TARGET_OPCODE(G_VAARG) + +// Generic sign extend +HANDLE_TARGET_OPCODE(G_SEXT) +HANDLE_TARGET_OPCODE(G_SEXT_INREG) + +// Generic zero extend +HANDLE_TARGET_OPCODE(G_ZEXT) + +// Generic left-shift +HANDLE_TARGET_OPCODE(G_SHL) + +// Generic logical right-shift +HANDLE_TARGET_OPCODE(G_LSHR) + +// Generic arithmetic right-shift +HANDLE_TARGET_OPCODE(G_ASHR) + +// Generic funnel left shift +HANDLE_TARGET_OPCODE(G_FSHL) + +// Generic funnel right shift +HANDLE_TARGET_OPCODE(G_FSHR) + +// Generic right rotate +HANDLE_TARGET_OPCODE(G_ROTR) + +// Generic left rotate +HANDLE_TARGET_OPCODE(G_ROTL) + +/// Generic integer-base comparison, also applicable to vectors of integers. +HANDLE_TARGET_OPCODE(G_ICMP) + +/// Generic floating-point comparison, also applicable to vectors. +HANDLE_TARGET_OPCODE(G_FCMP) + +/// Generic select. +HANDLE_TARGET_OPCODE(G_SELECT) + +/// Generic unsigned add instruction, consuming the normal operands and +/// producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_UADDO) + +/// Generic unsigned add instruction, consuming the normal operands plus a carry +/// flag, and similarly producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_UADDE) + +/// Generic unsigned sub instruction, consuming the normal operands and +/// producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_USUBO) + +/// Generic unsigned subtract instruction, consuming the normal operands plus a +/// carry flag, and similarly producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_USUBE) + +/// Generic signed add instruction, producing the result and a signed overflow +/// flag. +HANDLE_TARGET_OPCODE(G_SADDO) + +/// Generic signed add instruction, consuming the normal operands plus a carry +/// flag, and similarly producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_SADDE) + +/// Generic signed subtract instruction, producing the result and a signed +/// overflow flag. +HANDLE_TARGET_OPCODE(G_SSUBO) + +/// Generic signed sub instruction, consuming the normal operands plus a carry +/// flag, and similarly producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_SSUBE) + +/// Generic unsigned multiply instruction, producing the result and a signed +/// overflow flag. +HANDLE_TARGET_OPCODE(G_UMULO) + +/// Generic signed multiply instruction, producing the result and a signed +/// overflow flag. +HANDLE_TARGET_OPCODE(G_SMULO) + +// Multiply two numbers at twice the incoming bit width (unsigned) and return +// the high half of the result. +HANDLE_TARGET_OPCODE(G_UMULH) + +// Multiply two numbers at twice the incoming bit width (signed) and return +// the high half of the result. +HANDLE_TARGET_OPCODE(G_SMULH) + +/// Generic saturating unsigned addition. +HANDLE_TARGET_OPCODE(G_UADDSAT) + +/// Generic saturating signed addition. +HANDLE_TARGET_OPCODE(G_SADDSAT) + +/// Generic saturating unsigned subtraction. +HANDLE_TARGET_OPCODE(G_USUBSAT) + +/// Generic saturating signed subtraction. +HANDLE_TARGET_OPCODE(G_SSUBSAT) + +/// Generic saturating unsigned left shift. +HANDLE_TARGET_OPCODE(G_USHLSAT) + +/// Generic saturating signed left shift. +HANDLE_TARGET_OPCODE(G_SSHLSAT) + +// Perform signed fixed point multiplication +HANDLE_TARGET_OPCODE(G_SMULFIX) + +// Perform unsigned fixed point multiplication +HANDLE_TARGET_OPCODE(G_UMULFIX) + +// Perform signed, saturating fixed point multiplication +HANDLE_TARGET_OPCODE(G_SMULFIXSAT) + +// Perform unsigned, saturating fixed point multiplication +HANDLE_TARGET_OPCODE(G_UMULFIXSAT) + +// Perform signed fixed point division +HANDLE_TARGET_OPCODE(G_SDIVFIX) + +// Perform unsigned fixed point division +HANDLE_TARGET_OPCODE(G_UDIVFIX) + +// Perform signed, saturating fixed point division +HANDLE_TARGET_OPCODE(G_SDIVFIXSAT) + +// Perform unsigned, saturating fixed point division +HANDLE_TARGET_OPCODE(G_UDIVFIXSAT) + +/// Generic FP addition. +HANDLE_TARGET_OPCODE(G_FADD) + +/// Generic FP subtraction. +HANDLE_TARGET_OPCODE(G_FSUB) + +/// Generic FP multiplication. +HANDLE_TARGET_OPCODE(G_FMUL) + +/// Generic FMA multiplication. Behaves like llvm fma intrinsic +HANDLE_TARGET_OPCODE(G_FMA) + +/// Generic FP multiply and add. Behaves as separate fmul and fadd. +HANDLE_TARGET_OPCODE(G_FMAD) + +/// Generic FP division. +HANDLE_TARGET_OPCODE(G_FDIV) + +/// Generic FP remainder. +HANDLE_TARGET_OPCODE(G_FREM) + +/// Generic FP exponentiation. +HANDLE_TARGET_OPCODE(G_FPOW) + +/// Generic FP exponentiation, with an integer exponent. +HANDLE_TARGET_OPCODE(G_FPOWI) + +/// Generic base-e exponential of a value. +HANDLE_TARGET_OPCODE(G_FEXP) + +/// Generic base-2 exponential of a value. +HANDLE_TARGET_OPCODE(G_FEXP2) + +/// Floating point base-e logarithm of a value. +HANDLE_TARGET_OPCODE(G_FLOG) + +/// Floating point base-2 logarithm of a value. +HANDLE_TARGET_OPCODE(G_FLOG2) + +/// Floating point base-10 logarithm of a value. +HANDLE_TARGET_OPCODE(G_FLOG10) + +/// Generic FP negation. +HANDLE_TARGET_OPCODE(G_FNEG) + +/// Generic FP extension. +HANDLE_TARGET_OPCODE(G_FPEXT) + +/// Generic float to signed-int conversion +HANDLE_TARGET_OPCODE(G_FPTRUNC) + +/// Generic float to signed-int conversion +HANDLE_TARGET_OPCODE(G_FPTOSI) + +/// Generic float to unsigned-int conversion +HANDLE_TARGET_OPCODE(G_FPTOUI) + +/// Generic signed-int to float conversion +HANDLE_TARGET_OPCODE(G_SITOFP) + +/// Generic unsigned-int to float conversion +HANDLE_TARGET_OPCODE(G_UITOFP) + +/// Generic FP absolute value. +HANDLE_TARGET_OPCODE(G_FABS) + +/// FCOPYSIGN(X, Y) - Return the value of X with the sign of Y. NOTE: This does +/// not require that X and Y have the same type, just that they are both +/// floating point. X and the result must have the same type. FCOPYSIGN(f32, +/// f64) is allowed. +HANDLE_TARGET_OPCODE(G_FCOPYSIGN) + +/// Generic test for floating-point class. +HANDLE_TARGET_OPCODE(G_IS_FPCLASS) + +/// Generic FP canonicalize value. +HANDLE_TARGET_OPCODE(G_FCANONICALIZE) + +/// FP min/max matching libm's fmin/fmax +HANDLE_TARGET_OPCODE(G_FMINNUM) +HANDLE_TARGET_OPCODE(G_FMAXNUM) + +/// FP min/max matching IEEE-754 2008's minnum/maxnum semantics. +HANDLE_TARGET_OPCODE(G_FMINNUM_IEEE) +HANDLE_TARGET_OPCODE(G_FMAXNUM_IEEE) + +/// FP min/max matching IEEE-754 2018 draft semantics. +HANDLE_TARGET_OPCODE(G_FMINIMUM) +HANDLE_TARGET_OPCODE(G_FMAXIMUM) + +/// Generic pointer offset +HANDLE_TARGET_OPCODE(G_PTR_ADD) + +/// Clear the specified bits in a pointer. +HANDLE_TARGET_OPCODE(G_PTRMASK) + +/// Generic signed integer minimum. +HANDLE_TARGET_OPCODE(G_SMIN) + +/// Generic signed integer maximum. +HANDLE_TARGET_OPCODE(G_SMAX) + +/// Generic unsigned integer maximum. +HANDLE_TARGET_OPCODE(G_UMIN) + +/// Generic unsigned integer maximum. +HANDLE_TARGET_OPCODE(G_UMAX) + +/// Generic integer absolute value. +HANDLE_TARGET_OPCODE(G_ABS) + +HANDLE_TARGET_OPCODE(G_LROUND) +HANDLE_TARGET_OPCODE(G_LLROUND) + +/// Generic BRANCH instruction. This is an unconditional branch. +HANDLE_TARGET_OPCODE(G_BR) + +/// Generic branch to jump table entry. +HANDLE_TARGET_OPCODE(G_BRJT) + +/// Generic insertelement. +HANDLE_TARGET_OPCODE(G_INSERT_VECTOR_ELT) + +/// Generic extractelement. +HANDLE_TARGET_OPCODE(G_EXTRACT_VECTOR_ELT) + +/// Generic shufflevector. +HANDLE_TARGET_OPCODE(G_SHUFFLE_VECTOR) + +/// Generic count trailing zeroes. +HANDLE_TARGET_OPCODE(G_CTTZ) + +/// Same as above, undefined for zero inputs. +HANDLE_TARGET_OPCODE(G_CTTZ_ZERO_UNDEF) + +/// Generic count leading zeroes. +HANDLE_TARGET_OPCODE(G_CTLZ) + +/// Same as above, undefined for zero inputs. +HANDLE_TARGET_OPCODE(G_CTLZ_ZERO_UNDEF) + +/// Generic count bits. +HANDLE_TARGET_OPCODE(G_CTPOP) + +/// Generic byte swap. +HANDLE_TARGET_OPCODE(G_BSWAP) + +/// Generic bit reverse. +HANDLE_TARGET_OPCODE(G_BITREVERSE) + +/// Floating point ceil. +HANDLE_TARGET_OPCODE(G_FCEIL) + +/// Floating point cosine. +HANDLE_TARGET_OPCODE(G_FCOS) + +/// Floating point sine. +HANDLE_TARGET_OPCODE(G_FSIN) + +/// Floating point square root. +HANDLE_TARGET_OPCODE(G_FSQRT) + +/// Floating point floor. +HANDLE_TARGET_OPCODE(G_FFLOOR) + +/// Floating point round to next integer. +HANDLE_TARGET_OPCODE(G_FRINT) + +/// Floating point round to nearest integer. +HANDLE_TARGET_OPCODE(G_FNEARBYINT) + +/// Generic AddressSpaceCast. +HANDLE_TARGET_OPCODE(G_ADDRSPACE_CAST) + +/// Generic block address +HANDLE_TARGET_OPCODE(G_BLOCK_ADDR) + +/// Generic jump table address +HANDLE_TARGET_OPCODE(G_JUMP_TABLE) + +/// Generic dynamic stack allocation. +HANDLE_TARGET_OPCODE(G_DYN_STACKALLOC) + +/// Strict floating point instructions. +HANDLE_TARGET_OPCODE(G_STRICT_FADD) +HANDLE_TARGET_OPCODE(G_STRICT_FSUB) +HANDLE_TARGET_OPCODE(G_STRICT_FMUL) +HANDLE_TARGET_OPCODE(G_STRICT_FDIV) +HANDLE_TARGET_OPCODE(G_STRICT_FREM) +HANDLE_TARGET_OPCODE(G_STRICT_FMA) +HANDLE_TARGET_OPCODE(G_STRICT_FSQRT) + +/// read_register intrinsic +HANDLE_TARGET_OPCODE(G_READ_REGISTER) + +/// write_register intrinsic +HANDLE_TARGET_OPCODE(G_WRITE_REGISTER) + +/// llvm.memcpy intrinsic +HANDLE_TARGET_OPCODE(G_MEMCPY) + +/// llvm.memcpy.inline intrinsic +HANDLE_TARGET_OPCODE(G_MEMCPY_INLINE) + +/// llvm.memmove intrinsic +HANDLE_TARGET_OPCODE(G_MEMMOVE) + +/// llvm.memset intrinsic +HANDLE_TARGET_OPCODE(G_MEMSET) +HANDLE_TARGET_OPCODE(G_BZERO) + +/// Vector reductions +HANDLE_TARGET_OPCODE(G_VECREDUCE_SEQ_FADD) +HANDLE_TARGET_OPCODE(G_VECREDUCE_SEQ_FMUL) +HANDLE_TARGET_OPCODE(G_VECREDUCE_FADD) +HANDLE_TARGET_OPCODE(G_VECREDUCE_FMUL) +HANDLE_TARGET_OPCODE(G_VECREDUCE_FMAX) +HANDLE_TARGET_OPCODE(G_VECREDUCE_FMIN) +HANDLE_TARGET_OPCODE(G_VECREDUCE_ADD) +HANDLE_TARGET_OPCODE(G_VECREDUCE_MUL) +HANDLE_TARGET_OPCODE(G_VECREDUCE_AND) +HANDLE_TARGET_OPCODE(G_VECREDUCE_OR) +HANDLE_TARGET_OPCODE(G_VECREDUCE_XOR) +HANDLE_TARGET_OPCODE(G_VECREDUCE_SMAX) +HANDLE_TARGET_OPCODE(G_VECREDUCE_SMIN) +HANDLE_TARGET_OPCODE(G_VECREDUCE_UMAX) +HANDLE_TARGET_OPCODE(G_VECREDUCE_UMIN) + +HANDLE_TARGET_OPCODE(G_SBFX) +HANDLE_TARGET_OPCODE(G_UBFX) + +/// Marker for the end of the generic opcode. +/// This is used to check if an opcode is in the range of the +/// generic opcodes. +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_END, G_UBFX) + +/// BUILTIN_OP_END - This must be the last enum value in this list. +/// The target-specific post-isel opcode values start here. +HANDLE_TARGET_OPCODE_MARKER(GENERIC_OP_END, PRE_ISEL_GENERIC_OPCODE_END) diff --git a/contrib/libs/llvm16/include/llvm/Support/TargetParser.h b/contrib/libs/llvm16/include/llvm/Support/TargetParser.h new file mode 100644 index 00000000000..f5d520b8fcd --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TargetParser.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/TargetParser.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of `llvm/TargetParser/TargetParser.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/TargetParser.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ThreadPool.h b/contrib/libs/llvm16/include/llvm/Support/ThreadPool.h new file mode 100644 index 00000000000..55a5bec523a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ThreadPool.h @@ -0,0 +1,260 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/ThreadPool.h - A ThreadPool implementation -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a crude C++11 based thread pool. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_THREADPOOL_H +#define LLVM_SUPPORT_THREADPOOL_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/RWMutex.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/thread.h" + +#include <future> + +#include <condition_variable> +#include <deque> +#include <functional> +#include <memory> +#include <mutex> +#include <utility> + +namespace llvm { + +class ThreadPoolTaskGroup; + +/// A ThreadPool for asynchronous parallel execution on a defined number of +/// threads. +/// +/// The pool keeps a vector of threads alive, waiting on a condition variable +/// for some work to become available. +/// +/// It is possible to reuse one thread pool for different groups of tasks +/// by grouping tasks using ThreadPoolTaskGroup. All tasks are processed using +/// the same queue, but it is possible to wait only for a specific group of +/// tasks to finish. +/// +/// It is also possible for worker threads to submit new tasks and wait for +/// them. Note that this may result in a deadlock in cases such as when a task +/// (directly or indirectly) tries to wait for its own completion, or when all +/// available threads are used up by tasks waiting for a task that has no thread +/// left to run on (this includes waiting on the returned future). It should be +/// generally safe to wait() for a group as long as groups do not form a cycle. +class ThreadPool { +public: + /// Construct a pool using the hardware strategy \p S for mapping hardware + /// execution resources (threads, cores, CPUs) + /// Defaults to using the maximum execution resources in the system, but + /// accounting for the affinity mask. + ThreadPool(ThreadPoolStrategy S = hardware_concurrency()); + + /// Blocking destructor: the pool will wait for all the threads to complete. + ~ThreadPool(); + + /// Asynchronous submission of a task to the pool. The returned future can be + /// used to wait for the task to finish and is *non-blocking* on destruction. + template <typename Function, typename... Args> + auto async(Function &&F, Args &&...ArgList) { + auto Task = + std::bind(std::forward<Function>(F), std::forward<Args>(ArgList)...); + return async(std::move(Task)); + } + + /// Overload, task will be in the given task group. + template <typename Function, typename... Args> + auto async(ThreadPoolTaskGroup &Group, Function &&F, Args &&...ArgList) { + auto Task = + std::bind(std::forward<Function>(F), std::forward<Args>(ArgList)...); + return async(Group, std::move(Task)); + } + + /// Asynchronous submission of a task to the pool. The returned future can be + /// used to wait for the task to finish and is *non-blocking* on destruction. + template <typename Func> + auto async(Func &&F) -> std::shared_future<decltype(F())> { + return asyncImpl(std::function<decltype(F())()>(std::forward<Func>(F)), + nullptr); + } + + template <typename Func> + auto async(ThreadPoolTaskGroup &Group, Func &&F) + -> std::shared_future<decltype(F())> { + return asyncImpl(std::function<decltype(F())()>(std::forward<Func>(F)), + &Group); + } + + /// Blocking wait for all the threads to complete and the queue to be empty. + /// It is an error to try to add new tasks while blocking on this call. + /// Calling wait() from a task would deadlock waiting for itself. + void wait(); + + /// Blocking wait for only all the threads in the given group to complete. + /// It is possible to wait even inside a task, but waiting (directly or + /// indirectly) on itself will deadlock. If called from a task running on a + /// worker thread, the call may process pending tasks while waiting in order + /// not to waste the thread. + void wait(ThreadPoolTaskGroup &Group); + + // TODO: misleading legacy name warning! + // Returns the maximum number of worker threads in the pool, not the current + // number of threads! + unsigned getThreadCount() const { return MaxThreadCount; } + + /// Returns true if the current thread is a worker thread of this thread pool. + bool isWorkerThread() const; + +private: + /// Helpers to create a promise and a callable wrapper of \p Task that sets + /// the result of the promise. Returns the callable and a future to access the + /// result. + template <typename ResTy> + static std::pair<std::function<void()>, std::future<ResTy>> + createTaskAndFuture(std::function<ResTy()> Task) { + std::shared_ptr<std::promise<ResTy>> Promise = + std::make_shared<std::promise<ResTy>>(); + auto F = Promise->get_future(); + return { + [Promise = std::move(Promise), Task]() { Promise->set_value(Task()); }, + std::move(F)}; + } + static std::pair<std::function<void()>, std::future<void>> + createTaskAndFuture(std::function<void()> Task) { + std::shared_ptr<std::promise<void>> Promise = + std::make_shared<std::promise<void>>(); + auto F = Promise->get_future(); + return {[Promise = std::move(Promise), Task]() { + Task(); + Promise->set_value(); + }, + std::move(F)}; + } + + /// Returns true if all tasks in the given group have finished (nullptr means + /// all tasks regardless of their group). QueueLock must be locked. + bool workCompletedUnlocked(ThreadPoolTaskGroup *Group) const; + + /// Asynchronous submission of a task to the pool. The returned future can be + /// used to wait for the task to finish and is *non-blocking* on destruction. + template <typename ResTy> + std::shared_future<ResTy> asyncImpl(std::function<ResTy()> Task, + ThreadPoolTaskGroup *Group) { + +#if LLVM_ENABLE_THREADS + /// Wrap the Task in a std::function<void()> that sets the result of the + /// corresponding future. + auto R = createTaskAndFuture(Task); + + int requestedThreads; + { + // Lock the queue and push the new task + std::unique_lock<std::mutex> LockGuard(QueueLock); + + // Don't allow enqueueing after disabling the pool + assert(EnableFlag && "Queuing a thread during ThreadPool destruction"); + Tasks.emplace_back(std::make_pair(std::move(R.first), Group)); + requestedThreads = ActiveThreads + Tasks.size(); + } + QueueCondition.notify_one(); + grow(requestedThreads); + return R.second.share(); + +#else // LLVM_ENABLE_THREADS Disabled + + // Get a Future with launch::deferred execution using std::async + auto Future = std::async(std::launch::deferred, std::move(Task)).share(); + // Wrap the future so that both ThreadPool::wait() can operate and the + // returned future can be sync'ed on. + Tasks.emplace_back(std::make_pair([Future]() { Future.get(); }, Group)); + return Future; +#endif + } + +#if LLVM_ENABLE_THREADS + // Grow to ensure that we have at least `requested` Threads, but do not go + // over MaxThreadCount. + void grow(int requested); + + void processTasks(ThreadPoolTaskGroup *WaitingForGroup); +#endif + + /// Threads in flight + std::vector<llvm::thread> Threads; + /// Lock protecting access to the Threads vector. + mutable llvm::sys::RWMutex ThreadsLock; + + /// Tasks waiting for execution in the pool. + std::deque<std::pair<std::function<void()>, ThreadPoolTaskGroup *>> Tasks; + + /// Locking and signaling for accessing the Tasks queue. + std::mutex QueueLock; + std::condition_variable QueueCondition; + + /// Signaling for job completion (all tasks or all tasks in a group). + std::condition_variable CompletionCondition; + + /// Keep track of the number of thread actually busy + unsigned ActiveThreads = 0; + /// Number of threads active for tasks in the given group (only non-zero). + DenseMap<ThreadPoolTaskGroup *, unsigned> ActiveGroups; + +#if LLVM_ENABLE_THREADS // avoids warning for unused variable + /// Signal for the destruction of the pool, asking thread to exit. + bool EnableFlag = true; +#endif + + const ThreadPoolStrategy Strategy; + + /// Maximum number of threads to potentially grow this pool to. + const unsigned MaxThreadCount; +}; + +/// A group of tasks to be run on a thread pool. Thread pool tasks in different +/// groups can run on the same threadpool but can be waited for separately. +/// It is even possible for tasks of one group to submit and wait for tasks +/// of another group, as long as this does not form a loop. +class ThreadPoolTaskGroup { +public: + /// The ThreadPool argument is the thread pool to forward calls to. + ThreadPoolTaskGroup(ThreadPool &Pool) : Pool(Pool) {} + + /// Blocking destructor: will wait for all the tasks in the group to complete + /// by calling ThreadPool::wait(). + ~ThreadPoolTaskGroup() { wait(); } + + /// Calls ThreadPool::async() for this group. + template <typename Function, typename... Args> + inline auto async(Function &&F, Args &&...ArgList) { + return Pool.async(*this, std::forward<Function>(F), + std::forward<Args>(ArgList)...); + } + + /// Calls ThreadPool::wait() for this group. + void wait() { Pool.wait(*this); } + +private: + ThreadPool &Pool; +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_THREADPOOL_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Threading.h b/contrib/libs/llvm16/include/llvm/Support/Threading.h new file mode 100644 index 00000000000..30f348e49cb --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Threading.h @@ -0,0 +1,270 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Threading.h - Control multithreading mode --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares helper functions for running LLVM in a multi-threaded +// environment. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_THREADING_H +#define LLVM_SUPPORT_THREADING_H + +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX +#include "llvm/Support/Compiler.h" +#include <ciso646> // So we can check the C++ standard lib macros. +#include <optional> + +#if defined(_MSC_VER) +// MSVC's call_once implementation worked since VS 2015, which is the minimum +// supported version as of this writing. +#define LLVM_THREADING_USE_STD_CALL_ONCE 1 +#elif defined(LLVM_ON_UNIX) && \ + (defined(_LIBCPP_VERSION) || \ + !(defined(__NetBSD__) || defined(__OpenBSD__) || defined(__powerpc__))) +// std::call_once from libc++ is used on all Unix platforms. Other +// implementations like libstdc++ are known to have problems on NetBSD, +// OpenBSD and PowerPC. +#define LLVM_THREADING_USE_STD_CALL_ONCE 1 +#elif defined(LLVM_ON_UNIX) && \ + (defined(__powerpc__) && defined(__LITTLE_ENDIAN__)) +#define LLVM_THREADING_USE_STD_CALL_ONCE 1 +#else +#define LLVM_THREADING_USE_STD_CALL_ONCE 0 +#endif + +#if LLVM_THREADING_USE_STD_CALL_ONCE +#include <mutex> +#else +#include "llvm/Support/Atomic.h" +#endif + +namespace llvm { +class Twine; + +/// Returns true if LLVM is compiled with support for multi-threading, and +/// false otherwise. +constexpr bool llvm_is_multithreaded() { return LLVM_ENABLE_THREADS; } + +#if LLVM_THREADING_USE_STD_CALL_ONCE + + typedef std::once_flag once_flag; + +#else + + enum InitStatus { Uninitialized = 0, Wait = 1, Done = 2 }; + + /// The llvm::once_flag structure + /// + /// This type is modeled after std::once_flag to use with llvm::call_once. + /// This structure must be used as an opaque object. It is a struct to force + /// autoinitialization and behave like std::once_flag. + struct once_flag { + volatile sys::cas_flag status = Uninitialized; + }; + +#endif + + /// Execute the function specified as a parameter once. + /// + /// Typical usage: + /// \code + /// void foo() {...}; + /// ... + /// static once_flag flag; + /// call_once(flag, foo); + /// \endcode + /// + /// \param flag Flag used for tracking whether or not this has run. + /// \param F Function to call once. + template <typename Function, typename... Args> + void call_once(once_flag &flag, Function &&F, Args &&... ArgList) { +#if LLVM_THREADING_USE_STD_CALL_ONCE + std::call_once(flag, std::forward<Function>(F), + std::forward<Args>(ArgList)...); +#else + // For other platforms we use a generic (if brittle) version based on our + // atomics. + sys::cas_flag old_val = sys::CompareAndSwap(&flag.status, Wait, Uninitialized); + if (old_val == Uninitialized) { + std::forward<Function>(F)(std::forward<Args>(ArgList)...); + sys::MemoryFence(); + TsanIgnoreWritesBegin(); + TsanHappensBefore(&flag.status); + flag.status = Done; + TsanIgnoreWritesEnd(); + } else { + // Wait until any thread doing the call has finished. + sys::cas_flag tmp = flag.status; + sys::MemoryFence(); + while (tmp != Done) { + tmp = flag.status; + sys::MemoryFence(); + } + } + TsanHappensAfter(&flag.status); +#endif + } + + /// This tells how a thread pool will be used + class ThreadPoolStrategy { + public: + // The default value (0) means all available threads should be used, + // taking the affinity mask into account. If set, this value only represents + // a suggested high bound, the runtime might choose a lower value (not + // higher). + unsigned ThreadsRequested = 0; + + // If SMT is active, use hyper threads. If false, there will be only one + // std::thread per core. + bool UseHyperThreads = true; + + // If set, will constrain 'ThreadsRequested' to the number of hardware + // threads, or hardware cores. + bool Limit = false; + + /// Retrieves the max available threads for the current strategy. This + /// accounts for affinity masks and takes advantage of all CPU sockets. + unsigned compute_thread_count() const; + + /// Assign the current thread to an ideal hardware CPU or NUMA node. In a + /// multi-socket system, this ensures threads are assigned to all CPU + /// sockets. \p ThreadPoolNum represents a number bounded by [0, + /// compute_thread_count()). + void apply_thread_strategy(unsigned ThreadPoolNum) const; + + /// Finds the CPU socket where a thread should go. Returns 'std::nullopt' if + /// the thread shall remain on the actual CPU socket. + std::optional<unsigned> compute_cpu_socket(unsigned ThreadPoolNum) const; + }; + + /// Build a strategy from a number of threads as a string provided in \p Num. + /// When Num is above the max number of threads specified by the \p Default + /// strategy, we attempt to equally allocate the threads on all CPU sockets. + /// "0" or an empty string will return the \p Default strategy. + /// "all" for using all hardware threads. + std::optional<ThreadPoolStrategy> + get_threadpool_strategy(StringRef Num, ThreadPoolStrategy Default = {}); + + /// Returns a thread strategy for tasks requiring significant memory or other + /// resources. To be used for workloads where hardware_concurrency() proves to + /// be less efficient. Avoid this strategy if doing lots of I/O. Currently + /// based on physical cores, if available for the host system, otherwise falls + /// back to hardware_concurrency(). Returns 1 when LLVM is configured with + /// LLVM_ENABLE_THREADS = OFF. + inline ThreadPoolStrategy + heavyweight_hardware_concurrency(unsigned ThreadCount = 0) { + ThreadPoolStrategy S; + S.UseHyperThreads = false; + S.ThreadsRequested = ThreadCount; + return S; + } + + /// Like heavyweight_hardware_concurrency() above, but builds a strategy + /// based on the rules described for get_threadpool_strategy(). + /// If \p Num is invalid, returns a default strategy where one thread per + /// hardware core is used. + inline ThreadPoolStrategy heavyweight_hardware_concurrency(StringRef Num) { + std::optional<ThreadPoolStrategy> S = + get_threadpool_strategy(Num, heavyweight_hardware_concurrency()); + if (S) + return *S; + return heavyweight_hardware_concurrency(); + } + + /// Returns a default thread strategy where all available hardware resources + /// are to be used, except for those initially excluded by an affinity mask. + /// This function takes affinity into consideration. Returns 1 when LLVM is + /// configured with LLVM_ENABLE_THREADS=OFF. + inline ThreadPoolStrategy hardware_concurrency(unsigned ThreadCount = 0) { + ThreadPoolStrategy S; + S.ThreadsRequested = ThreadCount; + return S; + } + + /// Returns an optimal thread strategy to execute specified amount of tasks. + /// This strategy should prevent us from creating too many threads if we + /// occasionaly have an unexpectedly small amount of tasks. + inline ThreadPoolStrategy optimal_concurrency(unsigned TaskCount = 0) { + ThreadPoolStrategy S; + S.Limit = true; + S.ThreadsRequested = TaskCount; + return S; + } + + /// 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 + /// this. + uint64_t get_threadid(); + + /// Get the maximum length of a thread name on this platform. + /// A value of 0 means there is no limit. + uint32_t get_max_thread_name_length(); + + /// Set the name of the current thread. Setting a thread's name can + /// be helpful for enabling useful diagnostics under a debugger or when + /// logging. The level of support for setting a thread's name varies + /// wildly across operating systems, and we only make a best effort to + /// perform the operation on supported platforms. No indication of success + /// or failure is returned. + void set_thread_name(const Twine &Name); + + /// Get the name of the current thread. The level of support for + /// getting a thread's name varies wildly across operating systems, and it + /// is not even guaranteed that if you can successfully set a thread's name + /// that you can later get it back. This function is intended for diagnostic + /// purposes, and as with setting a thread's name no indication of whether + /// the operation succeeded or failed is returned. + void get_thread_name(SmallVectorImpl<char> &Name); + + /// Returns a mask that represents on which hardware thread, core, CPU, NUMA + /// group, the calling thread can be executed. On Windows, threads cannot + /// cross CPU sockets boundaries. + llvm::BitVector get_thread_affinity_mask(); + + /// Returns how many physical CPUs or NUMA groups the system has. + unsigned get_cpus(); + + /// Returns how many physical cores (as opposed to logical cores returned from + /// thread::hardware_concurrency(), which includes hyperthreads). + /// Returns -1 if unknown for the current host system. + int get_physical_cores(); + + enum class ThreadPriority { + /// Lower the current thread's priority as much as possible. Can be used + /// for long-running tasks that are not time critical; more energy- + /// efficient than Low. + Background = 0, + + /// Lower the current thread's priority such that it does not affect + /// foreground tasks significantly. This is a good default for long- + /// running, latency-insensitive tasks to make sure cpu is not hogged + /// by this task. + Low = 1, + + /// Restore the current thread's priority to default scheduling priority. + Default = 2, + }; + enum class SetThreadPriorityResult { FAILURE, SUCCESS }; + SetThreadPriorityResult set_thread_priority(ThreadPriority Priority); +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TimeProfiler.h b/contrib/libs/llvm16/include/llvm/Support/TimeProfiler.h new file mode 100644 index 00000000000..3acd8fe2c66 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TimeProfiler.h @@ -0,0 +1,173 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/TimeProfiler.h - Hierarchical Time Profiler -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This provides lightweight and dependency-free machinery to trace execution +// time around arbitrary code. Two API flavors are available. +// +// The primary API uses a RAII object to trigger tracing: +// +// \code +// { +// TimeTraceScope scope("my_event_name"); +// ...my code... +// } +// \endcode +// +// If the code to be profiled does not have a natural lexical scope then +// it is also possible to start and end events with respect to an implicit +// per-thread stack of profiling entries: +// +// \code +// timeTraceProfilerBegin("my_event_name"); +// ...my code... +// timeTraceProfilerEnd(); // must be called on all control flow paths +// \endcode +// +// Time profiling entries can be given an arbitrary name and, optionally, +// an arbitrary 'detail' string. The resulting trace will include 'Total' +// entries summing the time spent for each name. Thus, it's best to choose +// names to be fairly generic, and rely on the detail field to capture +// everything else of interest. +// +// To avoid lifetime issues name and detail strings are copied into the event +// entries at their time of creation. Care should be taken to make string +// construction cheap to prevent 'Heisenperf' effects. In particular, the +// 'detail' argument may be a string-returning closure: +// +// \code +// int n; +// { +// TimeTraceScope scope("my_event_name", +// [n]() { return (Twine("x=") + Twine(n)).str(); }); +// ...my code... +// } +// \endcode +// The closure will not be called if tracing is disabled. Otherwise, the +// resulting string will be directly moved into the entry. +// +// The main process should begin with a timeTraceProfilerInitialize, and +// finish with timeTraceProfileWrite and timeTraceProfilerCleanup calls. +// Each new thread should begin with a timeTraceProfilerInitialize, and +// finish with a timeTraceProfilerFinishThread call. +// +// Timestamps come from std::chrono::stable_clock. Note that threads need +// not see the same time from that clock, and the resolution may not be +// the best available. +// +// Currently, there are a number of compatible viewers: +// - chrome://tracing is the original chromium trace viewer. +// - http://ui.perfetto.dev is the replacement for the above, under active +// development by Google as part of the 'Perfetto' project. +// - https://www.speedscope.app/ has also been reported as an option. +// +// Future work: +// - Support akin to LLVM_DEBUG for runtime enable/disable of named tracing +// families for non-debug builds which wish to support optional tracing. +// - Evaluate the detail closures at profile write time to avoid +// stringification costs interfering with tracing. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TIMEPROFILER_H +#define LLVM_SUPPORT_TIMEPROFILER_H + +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +class raw_pwrite_stream; + +struct TimeTraceProfiler; +TimeTraceProfiler *getTimeTraceProfilerInstance(); + +/// Initialize the time trace profiler. +/// This sets up the global \p TimeTraceProfilerInstance +/// variable to be the profiler instance. +void timeTraceProfilerInitialize(unsigned TimeTraceGranularity, + StringRef ProcName); + +/// Cleanup the time trace profiler, if it was initialized. +void timeTraceProfilerCleanup(); + +/// Finish a time trace profiler running on a worker thread. +void timeTraceProfilerFinishThread(); + +/// Is the time trace profiler enabled, i.e. initialized? +inline bool timeTraceProfilerEnabled() { + return getTimeTraceProfilerInstance() != nullptr; +} + +/// Write profiling data to output stream. +/// Data produced is JSON, in Chrome "Trace Event" format, see +/// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview +void timeTraceProfilerWrite(raw_pwrite_stream &OS); + +/// Write profiling data to a file. +/// The function will write to \p PreferredFileName if provided, if not +/// then will write to \p FallbackFileName appending .time-trace. +/// Returns a StringError indicating a failure if the function is +/// unable to open the file for writing. +Error timeTraceProfilerWrite(StringRef PreferredFileName, + StringRef FallbackFileName); + +/// Manually begin a time section, with the given \p Name and \p Detail. +/// Profiler copies the string data, so the pointers can be given into +/// temporaries. Time sections can be hierarchical; every Begin must have a +/// matching End pair but they can nest. +void timeTraceProfilerBegin(StringRef Name, StringRef Detail); +void timeTraceProfilerBegin(StringRef Name, + llvm::function_ref<std::string()> Detail); + +/// Manually end the last time section. +void timeTraceProfilerEnd(); + +/// The TimeTraceScope is a helper class to call the begin and end functions +/// of the time trace profiler. When the object is constructed, it begins +/// the section; and when it is destroyed, it stops it. If the time profiler +/// is not initialized, the overhead is a single branch. +struct TimeTraceScope { + + TimeTraceScope() = delete; + TimeTraceScope(const TimeTraceScope &) = delete; + TimeTraceScope &operator=(const TimeTraceScope &) = delete; + TimeTraceScope(TimeTraceScope &&) = delete; + TimeTraceScope &operator=(TimeTraceScope &&) = delete; + + TimeTraceScope(StringRef Name) { + if (getTimeTraceProfilerInstance() != nullptr) + timeTraceProfilerBegin(Name, StringRef("")); + } + TimeTraceScope(StringRef Name, StringRef Detail) { + if (getTimeTraceProfilerInstance() != nullptr) + timeTraceProfilerBegin(Name, Detail); + } + TimeTraceScope(StringRef Name, llvm::function_ref<std::string()> Detail) { + if (getTimeTraceProfilerInstance() != nullptr) + timeTraceProfilerBegin(Name, Detail); + } + ~TimeTraceScope() { + if (getTimeTraceProfilerInstance() != nullptr) + timeTraceProfilerEnd(); + } +}; + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Timer.h b/contrib/libs/llvm16/include/llvm/Support/Timer.h new file mode 100644 index 00000000000..4e5e0be25ca --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Timer.h @@ -0,0 +1,267 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Timer.h - Interval Timing Support ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TIMER_H +#define LLVM_SUPPORT_TIMER_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include <cassert> +#include <memory> +#include <string> +#include <vector> + +namespace llvm { + +class TimerGroup; +class raw_ostream; + +class TimeRecord { + double WallTime; ///< Wall clock time elapsed in seconds. + double UserTime; ///< User time elapsed. + double SystemTime; ///< System time elapsed. + ssize_t MemUsed; ///< Memory allocated (in bytes). + uint64_t InstructionsExecuted; ///< Number of instructions executed +public: + TimeRecord() + : WallTime(0), UserTime(0), SystemTime(0), MemUsed(0), + InstructionsExecuted(0) {} + + /// Get the current time and memory usage. If Start is true we get the memory + /// usage before the time, otherwise we get time before memory usage. This + /// matters if the time to get the memory usage is significant and shouldn't + /// be counted as part of a duration. + static TimeRecord getCurrentTime(bool Start = true); + + double getProcessTime() const { return UserTime + SystemTime; } + double getUserTime() const { return UserTime; } + double getSystemTime() const { return SystemTime; } + double getWallTime() const { return WallTime; } + ssize_t getMemUsed() const { return MemUsed; } + uint64_t getInstructionsExecuted() const { return InstructionsExecuted; } + + bool operator<(const TimeRecord &T) const { + // Sort by Wall Time elapsed, as it is the only thing really accurate + return WallTime < T.WallTime; + } + + void operator+=(const TimeRecord &RHS) { + WallTime += RHS.WallTime; + UserTime += RHS.UserTime; + SystemTime += RHS.SystemTime; + MemUsed += RHS.MemUsed; + InstructionsExecuted += RHS.InstructionsExecuted; + } + void operator-=(const TimeRecord &RHS) { + WallTime -= RHS.WallTime; + UserTime -= RHS.UserTime; + SystemTime -= RHS.SystemTime; + MemUsed -= RHS.MemUsed; + InstructionsExecuted -= RHS.InstructionsExecuted; + } + + /// Print the current time record to \p OS, with a breakdown showing + /// contributions to the \p Total time record. + void print(const TimeRecord &Total, raw_ostream &OS) const; +}; + +/// This class is used to track the amount of time spent between invocations of +/// its startTimer()/stopTimer() methods. Given appropriate OS support it can +/// also keep track of the RSS of the program at various points. By default, +/// the Timer will print the amount of time it has captured to standard error +/// when the last timer is destroyed, otherwise it is printed when its +/// TimerGroup is destroyed. Timers do not print their information if they are +/// never started. +class Timer { + TimeRecord Time; ///< The total time captured. + TimeRecord StartTime; ///< The time startTimer() was last called. + std::string Name; ///< The name of this time variable. + std::string Description; ///< Description of this time variable. + bool Running = false; ///< Is the timer currently running? + bool Triggered = false; ///< Has the timer ever been triggered? + TimerGroup *TG = nullptr; ///< The TimerGroup this Timer is in. + + Timer **Prev = nullptr; ///< Pointer to \p Next of previous timer in group. + Timer *Next = nullptr; ///< Next timer in the group. +public: + explicit Timer(StringRef TimerName, StringRef TimerDescription) { + init(TimerName, TimerDescription); + } + Timer(StringRef TimerName, StringRef TimerDescription, TimerGroup &tg) { + init(TimerName, TimerDescription, tg); + } + Timer(const Timer &RHS) { + assert(!RHS.TG && "Can only copy uninitialized timers"); + } + const Timer &operator=(const Timer &T) { + assert(!TG && !T.TG && "Can only assign uninit timers"); + return *this; + } + ~Timer(); + + /// Create an uninitialized timer, client must use 'init'. + explicit Timer() = default; + void init(StringRef TimerName, StringRef TimerDescription); + void init(StringRef TimerName, StringRef TimerDescription, TimerGroup &tg); + + const std::string &getName() const { return Name; } + const std::string &getDescription() const { return Description; } + bool isInitialized() const { return TG != nullptr; } + + /// Check if the timer is currently running. + bool isRunning() const { return Running; } + + /// Check if startTimer() has ever been called on this timer. + bool hasTriggered() const { return Triggered; } + + /// Start the timer running. Time between calls to startTimer/stopTimer is + /// counted by the Timer class. Note that these calls must be correctly + /// paired. + void startTimer(); + + /// Stop the timer. + void stopTimer(); + + /// Clear the timer state. + void clear(); + + /// Return the duration for which this timer has been running. + TimeRecord getTotalTime() const { return Time; } + +private: + friend class TimerGroup; +}; + +/// The TimeRegion class is used as a helper class to call the startTimer() and +/// stopTimer() methods of the Timer class. When the object is constructed, it +/// starts the timer specified as its argument. When it is destroyed, it stops +/// the relevant timer. This makes it easy to time a region of code. +class TimeRegion { + Timer *T; + TimeRegion(const TimeRegion &) = delete; + +public: + explicit TimeRegion(Timer &t) : T(&t) { + T->startTimer(); + } + explicit TimeRegion(Timer *t) : T(t) { + if (T) T->startTimer(); + } + ~TimeRegion() { + if (T) T->stopTimer(); + } +}; + +/// This class is basically a combination of TimeRegion and Timer. It allows +/// you to declare a new timer, AND specify the region to time, all in one +/// statement. All timers with the same name are merged. This is primarily +/// used for debugging and for hunting performance problems. +struct NamedRegionTimer : public TimeRegion { + explicit NamedRegionTimer(StringRef Name, StringRef Description, + StringRef GroupName, + StringRef GroupDescription, bool Enabled = true); +}; + +/// The TimerGroup class is used to group together related timers into a single +/// report that is printed when the TimerGroup is destroyed. It is illegal to +/// destroy a TimerGroup object before all of the Timers in it are gone. A +/// TimerGroup can be specified for a newly created timer in its constructor. +class TimerGroup { + struct PrintRecord { + TimeRecord Time; + std::string Name; + std::string Description; + + PrintRecord(const PrintRecord &Other) = default; + PrintRecord &operator=(const PrintRecord &Other) = default; + PrintRecord(const TimeRecord &Time, const std::string &Name, + const std::string &Description) + : Time(Time), Name(Name), Description(Description) {} + + bool operator <(const PrintRecord &Other) const { + return Time < Other.Time; + } + }; + std::string Name; + std::string Description; + Timer *FirstTimer = nullptr; ///< First timer in the group. + std::vector<PrintRecord> TimersToPrint; + + TimerGroup **Prev; ///< Pointer to Next field of previous timergroup in list. + TimerGroup *Next; ///< Pointer to next timergroup in list. + TimerGroup(const TimerGroup &TG) = delete; + void operator=(const TimerGroup &TG) = delete; + +public: + explicit TimerGroup(StringRef Name, StringRef Description); + + explicit TimerGroup(StringRef Name, StringRef Description, + const StringMap<TimeRecord> &Records); + + ~TimerGroup(); + + void setName(StringRef NewName, StringRef NewDescription) { + Name.assign(NewName.begin(), NewName.end()); + Description.assign(NewDescription.begin(), NewDescription.end()); + } + + /// Print any started timers in this group, optionally resetting timers after + /// printing them. + void print(raw_ostream &OS, bool ResetAfterPrint = false); + + /// Clear all timers in this group. + void clear(); + + /// This static method prints all timers. + static void printAll(raw_ostream &OS); + + /// Clear out all timers. This is mostly used to disable automatic + /// printing on shutdown, when timers have already been printed explicitly + /// using \c printAll or \c printJSONValues. + static void clearAll(); + + const char *printJSONValues(raw_ostream &OS, const char *delim); + + /// Prints all timers as JSON key/value pairs. + static const char *printAllJSONValues(raw_ostream &OS, const char *delim); + + /// Ensure global objects required for statistics printing are initialized. + /// This function is used by the Statistic code to ensure correct order of + /// global constructors and destructors. + static void constructForStatistics(); + + /// This makes the default group unmanaged, and lets the user manage the + /// group's lifetime. + static std::unique_ptr<TimerGroup> aquireDefaultGroup(); + +private: + friend class Timer; + friend void PrintStatisticsJSON(raw_ostream &OS); + void addTimer(Timer &T); + void removeTimer(Timer &T); + void prepareToPrintList(bool reset_time = false); + void PrintQueuedTimers(raw_ostream &OS); + void printJSONValue(raw_ostream &OS, const PrintRecord &R, + const char *suffix, double Value); +}; + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/ToolOutputFile.h b/contrib/libs/llvm16/include/llvm/Support/ToolOutputFile.h new file mode 100644 index 00000000000..91d0d5ad500 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/ToolOutputFile.h @@ -0,0 +1,85 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- ToolOutputFile.h - Output files for compiler-like tools --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the ToolOutputFile class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TOOLOUTPUTFILE_H +#define LLVM_SUPPORT_TOOLOUTPUTFILE_H + +#include "llvm/Support/raw_ostream.h" +#include <optional> + +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 ToolOutputFile +/// object is destroyed unless the client calls keep(). +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 + /// uninstalls them in its destructor. + class CleanupInstaller { + public: + /// The name of the file. + std::string Filename; + + /// The flag which indicates whether we should not delete the file. + bool Keep; + + StringRef getFilename() { return Filename; } + explicit CleanupInstaller(StringRef Filename); + ~CleanupInstaller(); + } Installer; + + /// Storage for the stream, if we're owning our own stream. This is + /// intentionally declared after Installer. + std::optional<raw_fd_ostream> OSHolder; + + /// The actual stream to use. + raw_fd_ostream *OS; + +public: + /// This constructor's arguments are passed to raw_fd_ostream's + /// constructor. + ToolOutputFile(StringRef Filename, std::error_code &EC, + sys::fs::OpenFlags Flags); + + ToolOutputFile(StringRef Filename, int FD); + + /// Return the contained raw_fd_ostream. + raw_fd_ostream &os() { return *OS; } + + /// Return the filename initialized with. + StringRef getFilename() { return Installer.getFilename(); } + + /// Indicate that the tool's job wrt this output file has been successful and + /// the file should not be deleted. + void keep() { Installer.Keep = true; } + + const std::string &outputFilename() { return Installer.Filename; } +}; + +} // end llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TrailingObjects.h b/contrib/libs/llvm16/include/llvm/Support/TrailingObjects.h new file mode 100644 index 00000000000..871f69c6e2e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TrailingObjects.h @@ -0,0 +1,396 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- TrailingObjects.h - Variable-length classes ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header defines support for implementing classes that have +/// some trailing object (or arrays of objects) appended to them. The +/// main purpose is to make it obvious where this idiom is being used, +/// and to make the usage more idiomatic and more difficult to get +/// wrong. +/// +/// The TrailingObject template abstracts away the reinterpret_cast, +/// pointer arithmetic, and size calculations used for the allocation +/// and access of appended arrays of objects, and takes care that they +/// are all allocated at their required alignment. Additionally, it +/// ensures that the base type is final -- deriving from a class that +/// expects data appended immediately after it is typically not safe. +/// +/// Users are expected to derive from this template, and provide +/// numTrailingObjects implementations for each trailing type except +/// the last, e.g. like this sample: +/// +/// \code +/// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> { +/// friend TrailingObjects; +/// +/// unsigned NumInts, NumDoubles; +/// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; } +/// }; +/// \endcode +/// +/// You can access the appended arrays via 'getTrailingObjects', and +/// determine the size needed for allocation via +/// 'additionalSizeToAlloc' and 'totalSizeToAlloc'. +/// +/// All the methods implemented by this class are are intended for use +/// by the implementation of the class, not as part of its interface +/// (thus, private inheritance is suggested). +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TRAILINGOBJECTS_H +#define LLVM_SUPPORT_TRAILINGOBJECTS_H + +#include "llvm/Support/AlignOf.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/type_traits.h" +#include <new> +#include <type_traits> + +namespace llvm { + +namespace trailing_objects_internal { +/// Helper template to calculate the max alignment requirement for a set of +/// objects. +template <typename First, typename... Rest> class AlignmentCalcHelper { +private: + enum { + FirstAlignment = alignof(First), + RestAlignment = AlignmentCalcHelper<Rest...>::Alignment, + }; + +public: + enum { + Alignment = FirstAlignment > RestAlignment ? FirstAlignment : RestAlignment + }; +}; + +template <typename First> class AlignmentCalcHelper<First> { +public: + enum { Alignment = alignof(First) }; +}; + +/// The base class for TrailingObjects* classes. +class TrailingObjectsBase { +protected: + /// OverloadToken's purpose is to allow specifying function overloads + /// for different types, without actually taking the types as + /// parameters. (Necessary because member function templates cannot + /// be specialized, so overloads must be used instead of + /// specialization.) + template <typename T> struct OverloadToken {}; +}; + +// Just a little helper for transforming a type pack into the same +// number of a different type. e.g.: +// ExtractSecondType<Foo..., int>::type +template <typename Ty1, typename Ty2> struct ExtractSecondType { + typedef Ty2 type; +}; + +// TrailingObjectsImpl is somewhat complicated, because it is a +// recursively inheriting template, in order to handle the template +// varargs. Each level of inheritance picks off a single trailing type +// then recurses on the rest. The "Align", "BaseTy", and +// "TopTrailingObj" arguments are passed through unchanged through the +// recursion. "PrevTy" is, at each level, the type handled by the +// level right above it. + +template <int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy, + typename... MoreTys> +class TrailingObjectsImpl { + // The main template definition is never used -- the two + // specializations cover all possibilities. +}; + +template <int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy, + typename NextTy, typename... MoreTys> +class TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy, NextTy, + MoreTys...> + : public TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, NextTy, + MoreTys...> { + + typedef TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, NextTy, MoreTys...> + ParentType; + + struct RequiresRealignment { + static const bool value = alignof(PrevTy) < alignof(NextTy); + }; + + static constexpr bool requiresRealignment() { + return RequiresRealignment::value; + } + +protected: + // Ensure the inherited getTrailingObjectsImpl is not hidden. + using ParentType::getTrailingObjectsImpl; + + // These two functions are helper functions for + // TrailingObjects::getTrailingObjects. They recurse to the left -- + // the result for each type in the list of trailing types depends on + // the result of calling the function on the type to the + // left. However, the function for the type to the left is + // implemented by a *subclass* of this class, so we invoke it via + // the TopTrailingObj, which is, via the + // curiously-recurring-template-pattern, the most-derived type in + // this recursion, and thus, contains all the overloads. + static const NextTy * + getTrailingObjectsImpl(const BaseTy *Obj, + TrailingObjectsBase::OverloadToken<NextTy>) { + auto *Ptr = TopTrailingObj::getTrailingObjectsImpl( + Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) + + TopTrailingObj::callNumTrailingObjects( + Obj, TrailingObjectsBase::OverloadToken<PrevTy>()); + + if (requiresRealignment()) + return reinterpret_cast<const NextTy *>( + alignAddr(Ptr, Align::Of<NextTy>())); + else + return reinterpret_cast<const NextTy *>(Ptr); + } + + static NextTy * + getTrailingObjectsImpl(BaseTy *Obj, + TrailingObjectsBase::OverloadToken<NextTy>) { + auto *Ptr = TopTrailingObj::getTrailingObjectsImpl( + Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) + + TopTrailingObj::callNumTrailingObjects( + Obj, TrailingObjectsBase::OverloadToken<PrevTy>()); + + if (requiresRealignment()) + return reinterpret_cast<NextTy *>(alignAddr(Ptr, Align::Of<NextTy>())); + else + return reinterpret_cast<NextTy *>(Ptr); + } + + // Helper function for TrailingObjects::additionalSizeToAlloc: this + // function recurses to superclasses, each of which requires one + // fewer size_t argument, and adds its own size. + static constexpr size_t additionalSizeToAllocImpl( + size_t SizeSoFar, size_t Count1, + typename ExtractSecondType<MoreTys, size_t>::type... MoreCounts) { + return ParentType::additionalSizeToAllocImpl( + (requiresRealignment() ? llvm::alignTo<alignof(NextTy)>(SizeSoFar) + : SizeSoFar) + + sizeof(NextTy) * Count1, + MoreCounts...); + } +}; + +// The base case of the TrailingObjectsImpl inheritance recursion, +// when there's no more trailing types. +template <int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy> +class alignas(Align) TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy> + : public TrailingObjectsBase { +protected: + // This is a dummy method, only here so the "using" doesn't fail -- + // it will never be called, because this function recurses backwards + // up the inheritance chain to subclasses. + static void getTrailingObjectsImpl(); + + static constexpr size_t additionalSizeToAllocImpl(size_t SizeSoFar) { + return SizeSoFar; + } + + template <bool CheckAlignment> static void verifyTrailingObjectsAlignment() {} +}; + +} // end namespace trailing_objects_internal + +// Finally, the main type defined in this file, the one intended for users... + +/// See the file comment for details on the usage of the +/// TrailingObjects type. +template <typename BaseTy, typename... TrailingTys> +class TrailingObjects : private trailing_objects_internal::TrailingObjectsImpl< + trailing_objects_internal::AlignmentCalcHelper< + TrailingTys...>::Alignment, + BaseTy, TrailingObjects<BaseTy, TrailingTys...>, + BaseTy, TrailingTys...> { + + template <int A, typename B, typename T, typename P, typename... M> + friend class trailing_objects_internal::TrailingObjectsImpl; + + template <typename... Tys> class Foo {}; + + typedef trailing_objects_internal::TrailingObjectsImpl< + trailing_objects_internal::AlignmentCalcHelper<TrailingTys...>::Alignment, + BaseTy, TrailingObjects<BaseTy, TrailingTys...>, BaseTy, TrailingTys...> + ParentType; + using TrailingObjectsBase = trailing_objects_internal::TrailingObjectsBase; + + using ParentType::getTrailingObjectsImpl; + + // This function contains only a static_assert BaseTy is final. The + // static_assert must be in a function, and not at class-level + // because BaseTy isn't complete at class instantiation time, but + // will be by the time this function is instantiated. + static void verifyTrailingObjectsAssertions() { + static_assert(std::is_final<BaseTy>(), "BaseTy must be final."); + } + + // These two methods are the base of the recursion for this method. + static const BaseTy * + getTrailingObjectsImpl(const BaseTy *Obj, + TrailingObjectsBase::OverloadToken<BaseTy>) { + return Obj; + } + + static BaseTy * + getTrailingObjectsImpl(BaseTy *Obj, + TrailingObjectsBase::OverloadToken<BaseTy>) { + return Obj; + } + + // callNumTrailingObjects simply calls numTrailingObjects on the + // provided Obj -- except when the type being queried is BaseTy + // itself. There is always only one of the base object, so that case + // is handled here. (An additional benefit of indirecting through + // this function is that consumers only say "friend + // TrailingObjects", and thus, only this class itself can call the + // numTrailingObjects function.) + static size_t + callNumTrailingObjects(const BaseTy *Obj, + TrailingObjectsBase::OverloadToken<BaseTy>) { + return 1; + } + + template <typename T> + static size_t callNumTrailingObjects(const BaseTy *Obj, + TrailingObjectsBase::OverloadToken<T>) { + return Obj->numTrailingObjects(TrailingObjectsBase::OverloadToken<T>()); + } + +public: + // Make this (privately inherited) member public. +#ifndef _MSC_VER + using ParentType::OverloadToken; +#else + // An MSVC bug prevents the above from working, (last tested at CL version + // 19.28). "Class5" in TrailingObjectsTest.cpp tests the problematic case. + template <typename T> + using OverloadToken = typename ParentType::template OverloadToken<T>; +#endif + + /// Returns a pointer to the trailing object array of the given type + /// (which must be one of those specified in the class template). The + /// array may have zero or more elements in it. + template <typename T> const T *getTrailingObjects() const { + verifyTrailingObjectsAssertions(); + // Forwards to an impl function with overloads, since member + // function templates can't be specialized. + return this->getTrailingObjectsImpl( + static_cast<const BaseTy *>(this), + TrailingObjectsBase::OverloadToken<T>()); + } + + /// Returns a pointer to the trailing object array of the given type + /// (which must be one of those specified in the class template). The + /// array may have zero or more elements in it. + template <typename T> T *getTrailingObjects() { + verifyTrailingObjectsAssertions(); + // Forwards to an impl function with overloads, since member + // function templates can't be specialized. + return this->getTrailingObjectsImpl( + static_cast<BaseTy *>(this), TrailingObjectsBase::OverloadToken<T>()); + } + + /// Returns the size of the trailing data, if an object were + /// allocated with the given counts (The counts are in the same order + /// as the template arguments). This does not include the size of the + /// base object. The template arguments must be the same as those + /// used in the class; they are supplied here redundantly only so + /// that it's clear what the counts are counting in callers. + template <typename... Tys> + static constexpr std::enable_if_t< + std::is_same<Foo<TrailingTys...>, Foo<Tys...>>::value, size_t> + additionalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType< + TrailingTys, size_t>::type... Counts) { + return ParentType::additionalSizeToAllocImpl(0, Counts...); + } + + /// Returns the total size of an object if it were allocated with the + /// given trailing object counts. This is the same as + /// additionalSizeToAlloc, except it *does* include the size of the base + /// object. + template <typename... Tys> + static constexpr std::enable_if_t< + std::is_same<Foo<TrailingTys...>, Foo<Tys...>>::value, size_t> + totalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType< + TrailingTys, size_t>::type... Counts) { + return sizeof(BaseTy) + ParentType::additionalSizeToAllocImpl(0, Counts...); + } + + TrailingObjects() = default; + TrailingObjects(const TrailingObjects &) = delete; + TrailingObjects(TrailingObjects &&) = delete; + TrailingObjects &operator=(const TrailingObjects &) = delete; + TrailingObjects &operator=(TrailingObjects &&) = delete; + + /// A type where its ::with_counts template member has a ::type member + /// suitable for use as uninitialized storage for an object with the given + /// trailing object counts. The template arguments are similar to those + /// of additionalSizeToAlloc. + /// + /// Use with FixedSizeStorageOwner, e.g.: + /// + /// \code{.cpp} + /// + /// MyObj::FixedSizeStorage<void *>::with_counts<1u>::type myStackObjStorage; + /// MyObj::FixedSizeStorageOwner + /// myStackObjOwner(new ((void *)&myStackObjStorage) MyObj); + /// MyObj *const myStackObjPtr = myStackObjOwner.get(); + /// + /// \endcode + template <typename... Tys> struct FixedSizeStorage { + template <size_t... Counts> struct with_counts { + enum { Size = totalSizeToAlloc<Tys...>(Counts...) }; + struct type { + alignas(BaseTy) char buffer[Size]; + }; + }; + }; + + /// A type that acts as the owner for an object placed into fixed storage. + class FixedSizeStorageOwner { + public: + FixedSizeStorageOwner(BaseTy *p) : p(p) {} + ~FixedSizeStorageOwner() { + assert(p && "FixedSizeStorageOwner owns null?"); + p->~BaseTy(); + } + + BaseTy *get() { return p; } + const BaseTy *get() const { return p; } + + private: + FixedSizeStorageOwner(const FixedSizeStorageOwner &) = delete; + FixedSizeStorageOwner(FixedSizeStorageOwner &&) = delete; + FixedSizeStorageOwner &operator=(const FixedSizeStorageOwner &) = delete; + FixedSizeStorageOwner &operator=(FixedSizeStorageOwner &&) = delete; + + BaseTy *const p; + }; +}; + +} // end namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TrigramIndex.h b/contrib/libs/llvm16/include/llvm/Support/TrigramIndex.h new file mode 100644 index 00000000000..d8ad0884f6a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TrigramIndex.h @@ -0,0 +1,78 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- TrigramIndex.h - a heuristic for SpecialCaseList --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// +// +// TrigramIndex implements a heuristic for SpecialCaseList that allows to +// filter out ~99% incoming queries when all regular expressions in the +// SpecialCaseList are simple wildcards with '*' and '.'. If rules are more +// complicated, the check is defeated and it will always pass the queries to a +// full regex. +// +// The basic idea is that in order for a wildcard to match a query, the query +// needs to have all trigrams which occur in the wildcard. We create a trigram +// index (trigram -> list of rules with it) and then count trigrams in the query +// for each rule. If the count for one of the rules reaches the expected value, +// the check passes the query to a regex. If none of the rules got enough +// trigrams, the check tells that the query is definitely not matched by any +// of the rules, and no regex matching is needed. +// A similar idea was used in Google Code Search as described in the blog post: +// https://swtch.com/~rsc/regexp/regexp4.html +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TRIGRAMINDEX_H +#define LLVM_SUPPORT_TRIGRAMINDEX_H + +#include "llvm/ADT/SmallVector.h" +#include <string> +#include <unordered_map> +#include <vector> + +namespace llvm { +class StringRef; + +class TrigramIndex { + public: + /// Inserts a new Regex into the index. + void insert(const std::string &Regex); + + /// Returns true, if special case list definitely does not have a line + /// that matches the query. Returns false, if it's not sure. + bool isDefinitelyOut(StringRef Query) const; + + /// Returned true, iff the heuristic is defeated and not useful. + /// In this case isDefinitelyOut always returns false. + bool isDefeated() { return Defeated; } + private: + // If true, the rules are too complicated for the check to work, and full + // regex matching is needed for every rule. + bool Defeated = false; + // The minimum number of trigrams which should match for a rule to have a + // chance to match the query. The number of elements equals the number of + // regex rules in the SpecialCaseList. + std::vector<unsigned> Counts; + // Index holds a list of rules indices for each trigram. The same indices + // are used in Counts to store per-rule limits. + // If a trigram is too common (>4 rules with it), we stop tracking it, + // which increases the probability for a need to match using regex, but + // decreases the costs in the regular case. + std::unordered_map<unsigned, SmallVector<size_t, 4>> Index{256}; +}; + +} // namespace llvm + +#endif // LLVM_SUPPORT_TRIGRAMINDEX_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TypeName.h b/contrib/libs/llvm16/include/llvm/Support/TypeName.h new file mode 100644 index 00000000000..5f993e83f51 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TypeName.h @@ -0,0 +1,75 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- TypeName.h -----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TYPENAME_H +#define LLVM_SUPPORT_TYPENAME_H + +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +/// We provide a function which tries to compute the (demangled) name of a type +/// statically. +/// +/// This routine may fail on some platforms or for particularly unusual types. +/// Do not use it for anything other than logging and debugging aids. It isn't +/// portable or dependendable in any real sense. +/// +/// The returned StringRef will point into a static storage duration string. +/// However, it may not be null terminated and may be some strangely aligned +/// inner substring of a larger string. +template <typename DesiredTypeName> +inline StringRef getTypeName() { +#if defined(__clang__) || defined(__GNUC__) + StringRef Name = __PRETTY_FUNCTION__; + + StringRef Key = "DesiredTypeName = "; + Name = Name.substr(Name.find(Key)); + assert(!Name.empty() && "Unable to find the template parameter!"); + Name = Name.drop_front(Key.size()); + + assert(Name.endswith("]") && "Name doesn't end in the substitution key!"); + return Name.drop_back(1); +#elif defined(_MSC_VER) + StringRef Name = __FUNCSIG__; + + StringRef Key = "getTypeName<"; + Name = Name.substr(Name.find(Key)); + assert(!Name.empty() && "Unable to find the function name!"); + Name = Name.drop_front(Key.size()); + + for (StringRef Prefix : {"class ", "struct ", "union ", "enum "}) + if (Name.startswith(Prefix)) { + Name = Name.drop_front(Prefix.size()); + break; + } + + auto AnglePos = Name.rfind('>'); + assert(AnglePos != StringRef::npos && "Unable to find the closing '>'!"); + return Name.substr(0, AnglePos); +#else + // No known technique for statically extracting a type name on this compiler. + // We return a string that is unlikely to look like any type in LLVM. + return "UNKNOWN_TYPE"; +#endif +} + +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/TypeSize.h b/contrib/libs/llvm16/include/llvm/Support/TypeSize.h new file mode 100644 index 00000000000..64cd61d0a63 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/TypeSize.h @@ -0,0 +1,451 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- TypeSize.h - Wrapper around type sizes -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides a struct that can be used to query the size of IR types +// which may be scalable vectors. It provides convenience operators so that +// it can be used in much the same way as a single scalar value. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TYPESIZE_H +#define LLVM_SUPPORT_TYPESIZE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstdint> +#include <type_traits> + +namespace llvm { + +/// Reports a diagnostic message to indicate an invalid size request has been +/// done on a scalable vector. This function may not return. +void reportInvalidSizeRequest(const char *Msg); + +/// StackOffset holds a fixed and a scalable offset in bytes. +class StackOffset { + int64_t Fixed = 0; + int64_t Scalable = 0; + + StackOffset(int64_t Fixed, int64_t Scalable) + : Fixed(Fixed), Scalable(Scalable) {} + +public: + StackOffset() = default; + static StackOffset getFixed(int64_t Fixed) { return {Fixed, 0}; } + static StackOffset getScalable(int64_t Scalable) { return {0, Scalable}; } + static StackOffset get(int64_t Fixed, int64_t Scalable) { + return {Fixed, Scalable}; + } + + /// Returns the fixed component of the stack. + int64_t getFixed() const { return Fixed; } + + /// Returns the scalable component of the stack. + int64_t getScalable() const { return Scalable; } + + // Arithmetic operations. + StackOffset operator+(const StackOffset &RHS) const { + return {Fixed + RHS.Fixed, Scalable + RHS.Scalable}; + } + StackOffset operator-(const StackOffset &RHS) const { + return {Fixed - RHS.Fixed, Scalable - RHS.Scalable}; + } + StackOffset &operator+=(const StackOffset &RHS) { + Fixed += RHS.Fixed; + Scalable += RHS.Scalable; + return *this; + } + StackOffset &operator-=(const StackOffset &RHS) { + Fixed -= RHS.Fixed; + Scalable -= RHS.Scalable; + return *this; + } + StackOffset operator-() const { return {-Fixed, -Scalable}; } + + // Equality comparisons. + bool operator==(const StackOffset &RHS) const { + return Fixed == RHS.Fixed && Scalable == RHS.Scalable; + } + bool operator!=(const StackOffset &RHS) const { + return Fixed != RHS.Fixed || Scalable != RHS.Scalable; + } + + // The bool operator returns true iff any of the components is non zero. + explicit operator bool() const { return Fixed != 0 || Scalable != 0; } +}; + +namespace details { + +// Base class for ElementCount and TypeSize below. +template <typename LeafTy, typename ValueTy> class FixedOrScalableQuantity { +public: + using ScalarTy = ValueTy; + +protected: + ScalarTy Quantity = 0; + bool Scalable = false; + + constexpr FixedOrScalableQuantity() = default; + constexpr FixedOrScalableQuantity(ScalarTy Quantity, bool Scalable) + : Quantity(Quantity), Scalable(Scalable) {} + + friend constexpr LeafTy &operator+=(LeafTy &LHS, const LeafTy &RHS) { + assert(LHS.Scalable == RHS.Scalable && "Incompatible types"); + LHS.Quantity += RHS.Quantity; + return LHS; + } + + friend constexpr LeafTy &operator-=(LeafTy &LHS, const LeafTy &RHS) { + assert(LHS.Scalable == RHS.Scalable && "Incompatible types"); + LHS.Quantity -= RHS.Quantity; + return LHS; + } + + friend constexpr LeafTy &operator*=(LeafTy &LHS, ScalarTy RHS) { + LHS.Quantity *= RHS; + return LHS; + } + + friend constexpr LeafTy operator+(const LeafTy &LHS, const LeafTy &RHS) { + LeafTy Copy = LHS; + return Copy += RHS; + } + + friend constexpr LeafTy operator-(const LeafTy &LHS, const LeafTy &RHS) { + LeafTy Copy = LHS; + return Copy -= RHS; + } + + friend constexpr LeafTy operator*(const LeafTy &LHS, ScalarTy RHS) { + LeafTy Copy = LHS; + return Copy *= RHS; + } + + template <typename U = ScalarTy> + friend constexpr std::enable_if_t<std::is_signed<U>::value, LeafTy> + operator-(const LeafTy &LHS) { + LeafTy Copy = LHS; + return Copy *= -1; + } + +public: + constexpr bool operator==(const FixedOrScalableQuantity &RHS) const { + return Quantity == RHS.Quantity && Scalable == RHS.Scalable; + } + + constexpr bool operator!=(const FixedOrScalableQuantity &RHS) const { + return Quantity != RHS.Quantity || Scalable != RHS.Scalable; + } + + constexpr bool isZero() const { return Quantity == 0; } + + constexpr bool isNonZero() const { return Quantity != 0; } + + explicit operator bool() const { return isNonZero(); } + + /// Add \p RHS to the underlying quantity. + constexpr LeafTy getWithIncrement(ScalarTy RHS) const { + return LeafTy::get(Quantity + RHS, Scalable); + } + + /// Returns the minimum value this quantity can represent. + constexpr ScalarTy getKnownMinValue() const { return Quantity; } + + /// Returns whether the quantity is scaled by a runtime quantity (vscale). + constexpr bool isScalable() const { return Scalable; } + + /// A return value of true indicates we know at compile time that the number + /// of elements (vscale * Min) is definitely even. However, returning false + /// does not guarantee that the total number of elements is odd. + constexpr bool isKnownEven() const { return (getKnownMinValue() & 0x1) == 0; } + + /// This function tells the caller whether the element count is known at + /// compile time to be a multiple of the scalar value RHS. + constexpr bool isKnownMultipleOf(ScalarTy RHS) const { + return getKnownMinValue() % RHS == 0; + } + + // Return the minimum value with the assumption that the count is exact. + // Use in places where a scalable count doesn't make sense (e.g. non-vector + // types, or vectors in backends which don't support scalable vectors). + constexpr ScalarTy getFixedValue() const { + assert(!isScalable() && + "Request for a fixed element count on a scalable object"); + return getKnownMinValue(); + } + + // For some cases, quantity ordering between scalable and fixed quantity types + // cannot be determined at compile time, so such comparisons aren't allowed. + // + // e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime + // vscale >= 5, equal sized with a vscale of 4, and smaller with + // a vscale <= 3. + // + // All the functions below make use of the fact vscale is always >= 1, which + // means that <vscale x 4 x i32> is guaranteed to be >= <4 x i32>, etc. + + static constexpr bool isKnownLT(const FixedOrScalableQuantity &LHS, + const FixedOrScalableQuantity &RHS) { + if (!LHS.isScalable() || RHS.isScalable()) + return LHS.getKnownMinValue() < RHS.getKnownMinValue(); + return false; + } + + static constexpr bool isKnownGT(const FixedOrScalableQuantity &LHS, + const FixedOrScalableQuantity &RHS) { + if (LHS.isScalable() || !RHS.isScalable()) + return LHS.getKnownMinValue() > RHS.getKnownMinValue(); + return false; + } + + static constexpr bool isKnownLE(const FixedOrScalableQuantity &LHS, + const FixedOrScalableQuantity &RHS) { + if (!LHS.isScalable() || RHS.isScalable()) + return LHS.getKnownMinValue() <= RHS.getKnownMinValue(); + return false; + } + + static constexpr bool isKnownGE(const FixedOrScalableQuantity &LHS, + const FixedOrScalableQuantity &RHS) { + if (LHS.isScalable() || !RHS.isScalable()) + return LHS.getKnownMinValue() >= RHS.getKnownMinValue(); + return false; + } + + /// We do not provide the '/' operator here because division for polynomial + /// types does not work in the same way as for normal integer types. We can + /// only divide the minimum value (or coefficient) by RHS, which is not the + /// same as + /// (Min * Vscale) / RHS + /// The caller is recommended to use this function in combination with + /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to + /// perform a lossless divide by RHS. + constexpr LeafTy divideCoefficientBy(ScalarTy RHS) const { + return LeafTy::get(getKnownMinValue() / RHS, isScalable()); + } + + constexpr LeafTy multiplyCoefficientBy(ScalarTy RHS) const { + return LeafTy::get(getKnownMinValue() * RHS, isScalable()); + } + + constexpr LeafTy coefficientNextPowerOf2() const { + return LeafTy::get( + static_cast<ScalarTy>(llvm::NextPowerOf2(getKnownMinValue())), + isScalable()); + } + + /// Returns true if there exists a value X where RHS.multiplyCoefficientBy(X) + /// will result in a value whose quantity matches our own. + constexpr bool + hasKnownScalarFactor(const FixedOrScalableQuantity &RHS) const { + return isScalable() == RHS.isScalable() && + getKnownMinValue() % RHS.getKnownMinValue() == 0; + } + + /// Returns a value X where RHS.multiplyCoefficientBy(X) will result in a + /// value whose quantity matches our own. + constexpr ScalarTy + getKnownScalarFactor(const FixedOrScalableQuantity &RHS) const { + assert(hasKnownScalarFactor(RHS) && "Expected RHS to be a known factor!"); + return getKnownMinValue() / RHS.getKnownMinValue(); + } + + /// Printing function. + void print(raw_ostream &OS) const { + if (isScalable()) + OS << "vscale x "; + OS << getKnownMinValue(); + } +}; + +} // namespace details + +// Stores the number of elements for a type and whether this type is fixed +// (N-Elements) or scalable (e.g., SVE). +// - ElementCount::getFixed(1) : A scalar value. +// - ElementCount::getFixed(2) : A vector type holding 2 values. +// - ElementCount::getScalable(4) : A scalable vector type holding 4 values. +class ElementCount + : public details::FixedOrScalableQuantity<ElementCount, unsigned> { + constexpr ElementCount(ScalarTy MinVal, bool Scalable) + : FixedOrScalableQuantity(MinVal, Scalable) {} + + constexpr ElementCount( + const FixedOrScalableQuantity<ElementCount, unsigned> &V) + : FixedOrScalableQuantity(V) {} + +public: + constexpr ElementCount() : FixedOrScalableQuantity() {} + + static constexpr ElementCount getFixed(ScalarTy MinVal) { + return ElementCount(MinVal, false); + } + static constexpr ElementCount getScalable(ScalarTy MinVal) { + return ElementCount(MinVal, true); + } + static constexpr ElementCount get(ScalarTy MinVal, bool Scalable) { + return ElementCount(MinVal, Scalable); + } + + /// Exactly one element. + constexpr bool isScalar() const { + return !isScalable() && getKnownMinValue() == 1; + } + /// One or more elements. + constexpr bool isVector() const { + return (isScalable() && getKnownMinValue() != 0) || getKnownMinValue() > 1; + } +}; + +// Stores the size of a type. If the type is of fixed size, it will represent +// the exact size. If the type is a scalable vector, it will represent the known +// minimum size. +class TypeSize : public details::FixedOrScalableQuantity<TypeSize, uint64_t> { + TypeSize(const FixedOrScalableQuantity<TypeSize, uint64_t> &V) + : FixedOrScalableQuantity(V) {} + +public: + constexpr TypeSize(ScalarTy Quantity, bool Scalable) + : FixedOrScalableQuantity(Quantity, Scalable) {} + + static constexpr TypeSize getFixed(ScalarTy ExactSize) { + return TypeSize(ExactSize, false); + } + static constexpr TypeSize getScalable(ScalarTy MinimunSize) { + return TypeSize(MinimunSize, true); + } + static constexpr TypeSize get(ScalarTy Quantity, bool Scalable) { + return TypeSize(Quantity, Scalable); + } + static constexpr TypeSize Fixed(ScalarTy ExactSize) { + return TypeSize(ExactSize, false); + } + static constexpr TypeSize Scalable(ScalarTy MinimumSize) { + return TypeSize(MinimumSize, true); + } + + LLVM_DEPRECATED("Use getFixedValue() instead", "getFixedValue") + constexpr ScalarTy getFixedSize() const { return getFixedValue(); } + + LLVM_DEPRECATED("Use getKnownMinValue() instead", "getKnownMinValue") + constexpr ScalarTy getKnownMinSize() const { return getKnownMinValue(); } + + // All code for this class below this point is needed because of the + // temporary implicit conversion to uint64_t. The operator overloads are + // needed because otherwise the conversion of the parent class + // UnivariateLinearPolyBase -> TypeSize is ambiguous. + // TODO: Remove the implicit conversion. + + // Casts to a uint64_t if this is a fixed-width size. + // + // This interface is deprecated and will be removed in a future version + // of LLVM in favour of upgrading uses that rely on this implicit conversion + // to uint64_t. Calls to functions that return a TypeSize should use the + // proper interfaces to TypeSize. + // In practice this is mostly calls to MVT/EVT::getSizeInBits(). + // + // To determine how to upgrade the code: + // + // if (<algorithm works for both scalable and fixed-width vectors>) + // use getKnownMinValue() + // else if (<algorithm works only for fixed-width vectors>) { + // if <algorithm can be adapted for both scalable and fixed-width vectors> + // update the algorithm and use getKnownMinValue() + // else + // bail out early for scalable vectors and use getFixedValue() + // } + operator ScalarTy() const; + + // Additional operators needed to avoid ambiguous parses + // because of the implicit conversion hack. + friend constexpr TypeSize operator*(const TypeSize &LHS, const int RHS) { + return LHS * (ScalarTy)RHS; + } + friend constexpr TypeSize operator*(const TypeSize &LHS, const unsigned RHS) { + return LHS * (ScalarTy)RHS; + } + friend constexpr TypeSize operator*(const TypeSize &LHS, const int64_t RHS) { + return LHS * (ScalarTy)RHS; + } + friend constexpr TypeSize operator*(const int LHS, const TypeSize &RHS) { + return RHS * LHS; + } + friend constexpr TypeSize operator*(const unsigned LHS, const TypeSize &RHS) { + return RHS * LHS; + } + friend constexpr TypeSize operator*(const int64_t LHS, const TypeSize &RHS) { + return RHS * LHS; + } + friend constexpr TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) { + return RHS * LHS; + } +}; + +//===----------------------------------------------------------------------===// +// Utilities +//===----------------------------------------------------------------------===// + +/// Returns a TypeSize with a known minimum size that is the next integer +/// (mod 2**64) that is greater than or equal to \p Quantity and is a multiple +/// of \p Align. \p Align must be non-zero. +/// +/// Similar to the alignTo functions in MathExtras.h +inline constexpr TypeSize alignTo(TypeSize Size, uint64_t Align) { + assert(Align != 0u && "Align must be non-zero"); + return {(Size.getKnownMinValue() + Align - 1) / Align * Align, + Size.isScalable()}; +} + +/// Stream operator function for `FixedOrScalableQuantity`. +template <typename LeafTy, typename ScalarTy> +inline raw_ostream & +operator<<(raw_ostream &OS, + const details::FixedOrScalableQuantity<LeafTy, ScalarTy> &PS) { + PS.print(OS); + return OS; +} + +template <> struct DenseMapInfo<ElementCount, void> { + static inline ElementCount getEmptyKey() { + return ElementCount::getScalable(~0U); + } + static inline ElementCount getTombstoneKey() { + return ElementCount::getFixed(~0U - 1); + } + static unsigned getHashValue(const ElementCount &EltCnt) { + unsigned HashVal = EltCnt.getKnownMinValue() * 37U; + if (EltCnt.isScalable()) + return (HashVal - 1U); + + return HashVal; + } + static bool isEqual(const ElementCount &LHS, const ElementCount &RHS) { + return LHS == RHS; + } +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_TYPESIZE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Unicode.h b/contrib/libs/llvm16/include/llvm/Support/Unicode.h new file mode 100644 index 00000000000..6a0590d75bc --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Unicode.h @@ -0,0 +1,103 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Unicode.h - Unicode character properties -*- C++ -*-=====// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines functions that allow querying certain properties of Unicode +// characters. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_UNICODE_H +#define LLVM_SUPPORT_UNICODE_H + +#include "llvm/ADT/SmallString.h" +#include <optional> +#include <string> + +namespace llvm { +class StringRef; + +namespace sys { +namespace unicode { + +enum ColumnWidthErrors { + ErrorInvalidUTF8 = -2, + ErrorNonPrintableCharacter = -1 +}; + +/// Determines if a character is likely to be displayed correctly on the +/// terminal. Exact implementation would have to depend on the specific +/// terminal, so we define the semantic that should be suitable for generic case +/// of a terminal capable to output Unicode characters. +/// +/// Printable codepoints are those in the categories L, M, N, P, S and Zs +/// \return true if the character is considered printable. +bool isPrintable(int UCS); + +// Formatting codepoints are codepoints in the Cf category. +bool isFormatting(int UCS); + +/// Gets the number of positions the UTF8-encoded \p Text is likely to occupy +/// when output on a terminal ("character width"). This depends on the +/// implementation of the terminal, and there's no standard definition of +/// character width. +/// +/// The implementation defines it in a way that is expected to be compatible +/// with a generic Unicode-capable terminal. +/// +/// \return Character width: +/// * ErrorNonPrintableCharacter (-1) if \p Text contains non-printable +/// characters (as identified by isPrintable); +/// * 0 for each non-spacing and enclosing combining mark; +/// * 2 for each CJK character excluding halfwidth forms; +/// * 1 for each of the remaining characters. +int columnWidthUTF8(StringRef Text); + +/// Fold input unicode character according the Simple unicode case folding +/// rules. +int foldCharSimple(int C); + +/// Maps the name or the alias of a Unicode character to its associated +/// codepoints. +/// The names and aliases are derived from UnicodeData.txt and NameAliases.txt +/// For compatibility with the semantics of named character escape sequences in +/// C++, this mapping does an exact match sensitive to casing and spacing. +/// \return The codepoint of the corresponding character, if any. +std::optional<char32_t> nameToCodepointStrict(StringRef Name); + +struct LooseMatchingResult { + char32_t CodePoint; + SmallString<64> Name; +}; + +std::optional<LooseMatchingResult> nameToCodepointLooseMatching(StringRef Name); + +struct MatchForCodepointName { + std::string Name; + uint32_t Distance = 0; + char32_t Value = 0; +}; + +SmallVector<MatchForCodepointName> +nearestMatchesForCodepointName(StringRef Pattern, std::size_t MaxMatchesCount); + +} // namespace unicode +} // namespace sys +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/UnicodeCharRanges.h b/contrib/libs/llvm16/include/llvm/Support/UnicodeCharRanges.h new file mode 100644 index 00000000000..2aaa878b600 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/UnicodeCharRanges.h @@ -0,0 +1,114 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- UnicodeCharRanges.h - Types and functions for character ranges ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_UNICODECHARRANGES_H +#define LLVM_SUPPORT_UNICODECHARRANGES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +#define DEBUG_TYPE "unicode" + +namespace llvm { +namespace sys { + +/// Represents a closed range of Unicode code points [Lower, Upper]. +struct UnicodeCharRange { + uint32_t Lower; + uint32_t Upper; +}; + +inline bool operator<(uint32_t Value, UnicodeCharRange Range) { + return Value < Range.Lower; +} +inline bool operator<(UnicodeCharRange Range, uint32_t Value) { + return Range.Upper < Value; +} + +/// Holds a reference to an ordered array of UnicodeCharRange and allows +/// to quickly check if a code point is contained in the set represented by this +/// array. +class UnicodeCharSet { +public: + typedef ArrayRef<UnicodeCharRange> CharRanges; + + /// Constructs a UnicodeCharSet instance from an array of + /// UnicodeCharRanges. + /// + /// Array pointed by \p Ranges should have the lifetime at least as long as + /// the UnicodeCharSet instance, and should not change. Array is validated by + /// the constructor, so it makes sense to create as few UnicodeCharSet + /// instances per each array of ranges, as possible. +#ifdef NDEBUG + + // FIXME: This could use constexpr + static_assert. This way we + // may get rid of NDEBUG in this header. Unfortunately there are some + // problems to get this working with MSVC 2013. Change this when + // the support for MSVC 2013 is dropped. + constexpr UnicodeCharSet(CharRanges Ranges) : Ranges(Ranges) {} +#else + UnicodeCharSet(CharRanges Ranges) : Ranges(Ranges) { + assert(rangesAreValid()); + } +#endif + + /// Returns true if the character set contains the Unicode code point + /// \p C. + bool contains(uint32_t C) const { + return std::binary_search(Ranges.begin(), Ranges.end(), C); + } + +private: + /// Returns true if each of the ranges is a proper closed range + /// [min, max], and if the ranges themselves are ordered and non-overlapping. + bool rangesAreValid() const { + uint32_t Prev = 0; + for (CharRanges::const_iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) { + if (I != Ranges.begin() && Prev >= I->Lower) { + LLVM_DEBUG(dbgs() << "Upper bound 0x"); + LLVM_DEBUG(dbgs().write_hex(Prev)); + LLVM_DEBUG(dbgs() << " should be less than succeeding lower bound 0x"); + LLVM_DEBUG(dbgs().write_hex(I->Lower) << "\n"); + return false; + } + if (I->Upper < I->Lower) { + LLVM_DEBUG(dbgs() << "Upper bound 0x"); + LLVM_DEBUG(dbgs().write_hex(I->Lower)); + LLVM_DEBUG(dbgs() << " should not be less than lower bound 0x"); + LLVM_DEBUG(dbgs().write_hex(I->Upper) << "\n"); + return false; + } + Prev = I->Upper; + } + + return true; + } + + const CharRanges Ranges; +}; + +} // namespace sys +} // namespace llvm + +#undef DEBUG_TYPE // "unicode" + +#endif // LLVM_SUPPORT_UNICODECHARRANGES_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/VCSRevision.h b/contrib/libs/llvm16/include/llvm/Support/VCSRevision.h new file mode 100644 index 00000000000..c4940ef5ffe --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/VCSRevision.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#undef LLVM_REVISION +#undef LLVM_REPOSITORY + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Valgrind.h b/contrib/libs/llvm16/include/llvm/Support/Valgrind.h new file mode 100644 index 00000000000..15c6803100d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Valgrind.h @@ -0,0 +1,42 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/Valgrind.h - Communication with Valgrind ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Methods for communicating with a valgrind instance this program is running +// under. These are all no-ops unless LLVM was configured on a system with the +// valgrind headers installed and valgrind is controlling this process. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_VALGRIND_H +#define LLVM_SUPPORT_VALGRIND_H + +#include <cstddef> + +namespace llvm { +namespace sys { + // True if Valgrind is controlling this process. + bool RunningOnValgrind(); + + // Discard valgrind's translation of code in the range [Addr .. Addr + Len). + // Otherwise valgrind may continue to execute the old version of the code. + void ValgrindDiscardTranslations(const void *Addr, size_t Len); +} // namespace sys +} // end namespace llvm + +#endif // LLVM_SUPPORT_VALGRIND_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/VersionTuple.h b/contrib/libs/llvm16/include/llvm/Support/VersionTuple.h new file mode 100644 index 00000000000..a0e442498e4 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/VersionTuple.h @@ -0,0 +1,230 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the llvm::VersionTuple class, which represents a version in +/// the form major[.minor[.subminor]]. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_VERSIONTUPLE_H +#define LLVM_SUPPORT_VERSIONTUPLE_H + +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Support/Endian.h" +#include <optional> +#include <string> +#include <tuple> + +namespace llvm { +template <typename HasherT, support::endianness Endianness> +class HashBuilderImpl; +class raw_ostream; +class StringRef; + +/// Represents a version number in the form major[.minor[.subminor[.build]]]. +class VersionTuple { + unsigned Major : 32; + + unsigned Minor : 31; + unsigned HasMinor : 1; + + unsigned Subminor : 31; + unsigned HasSubminor : 1; + + unsigned Build : 31; + unsigned HasBuild : 1; + +public: + constexpr VersionTuple() + : Major(0), Minor(0), HasMinor(false), Subminor(0), HasSubminor(false), + Build(0), HasBuild(false) {} + + explicit constexpr VersionTuple(unsigned Major) + : Major(Major), Minor(0), HasMinor(false), Subminor(0), + HasSubminor(false), Build(0), HasBuild(false) {} + + explicit constexpr VersionTuple(unsigned Major, unsigned Minor) + : Major(Major), Minor(Minor), HasMinor(true), Subminor(0), + HasSubminor(false), Build(0), HasBuild(false) {} + + explicit constexpr VersionTuple(unsigned Major, unsigned Minor, + unsigned Subminor) + : Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor), + HasSubminor(true), Build(0), HasBuild(false) {} + + explicit constexpr VersionTuple(unsigned Major, unsigned Minor, + unsigned Subminor, unsigned Build) + : Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor), + HasSubminor(true), Build(Build), HasBuild(true) {} + + /// Determine whether this version information is empty + /// (e.g., all version components are zero). + bool empty() const { + return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; + } + + /// Retrieve the major version number. + unsigned getMajor() const { return Major; } + + /// Retrieve the minor version number, if provided. + std::optional<unsigned> getMinor() const { + if (!HasMinor) + return std::nullopt; + return Minor; + } + + /// Retrieve the subminor version number, if provided. + std::optional<unsigned> getSubminor() const { + if (!HasSubminor) + return std::nullopt; + return Subminor; + } + + /// Retrieve the build version number, if provided. + std::optional<unsigned> getBuild() const { + if (!HasBuild) + return std::nullopt; + return Build; + } + + /// Return a version tuple that contains only the first 3 version components. + VersionTuple withoutBuild() const { + if (HasBuild) + return VersionTuple(Major, Minor, Subminor); + return *this; + } + + /// Return a version tuple that contains a different major version but + /// everything else is the same. + VersionTuple withMajorReplaced(unsigned NewMajor) const { + return VersionTuple(NewMajor, Minor, Subminor, Build); + } + + /// Return a version tuple that contains only components that are non-zero. + VersionTuple normalize() const { + VersionTuple Result = *this; + if (Result.Build == 0) { + Result.HasBuild = false; + if (Result.Subminor == 0) { + Result.HasSubminor = false; + if (Result.Minor == 0) + Result.HasMinor = false; + } + } + return Result; + } + + /// Determine if two version numbers are equivalent. If not + /// provided, minor and subminor version numbers are considered to be zero. + friend bool operator==(const VersionTuple &X, const VersionTuple &Y) { + return X.Major == Y.Major && X.Minor == Y.Minor && + X.Subminor == Y.Subminor && X.Build == Y.Build; + } + + /// Determine if two version numbers are not equivalent. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) { + return !(X == Y); + } + + /// Determine whether one version number precedes another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator<(const VersionTuple &X, const VersionTuple &Y) { + return std::tie(X.Major, X.Minor, X.Subminor, X.Build) < + std::tie(Y.Major, Y.Minor, Y.Subminor, Y.Build); + } + + /// Determine whether one version number follows another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator>(const VersionTuple &X, const VersionTuple &Y) { + return Y < X; + } + + /// Determine whether one version number precedes or is + /// equivalent to another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) { + return !(Y < X); + } + + /// Determine whether one version number follows or is + /// equivalent to another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) { + return !(X < Y); + } + + friend hash_code hash_value(const VersionTuple &VT) { + return hash_combine(VT.Major, VT.Minor, VT.Subminor, VT.Build); + } + + template <typename HasherT, llvm::support::endianness Endianness> + friend void addHash(HashBuilderImpl<HasherT, Endianness> &HBuilder, + const VersionTuple &VT) { + HBuilder.add(VT.Major, VT.Minor, VT.Subminor, VT.Build); + } + + /// Retrieve a string representation of the version number. + std::string getAsString() const; + + /// Try to parse the given string as a version number. + /// \returns \c true if the string does not match the regular expression + /// [0-9]+(\.[0-9]+){0,3} + bool tryParse(StringRef string); +}; + +/// Print a version number. +raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V); + +// Provide DenseMapInfo for version tuples. +template <> struct DenseMapInfo<VersionTuple> { + static inline VersionTuple getEmptyKey() { return VersionTuple(0x7FFFFFFF); } + static inline VersionTuple getTombstoneKey() { + return VersionTuple(0x7FFFFFFE); + } + static unsigned getHashValue(const VersionTuple &Value) { + unsigned Result = Value.getMajor(); + if (auto Minor = Value.getMinor()) + Result = detail::combineHashValue(Result, *Minor); + if (auto Subminor = Value.getSubminor()) + Result = detail::combineHashValue(Result, *Subminor); + if (auto Build = Value.getBuild()) + Result = detail::combineHashValue(Result, *Build); + + return Result; + } + + static bool isEqual(const VersionTuple &LHS, const VersionTuple &RHS) { + return LHS == RHS; + } +}; + +} // end namespace llvm +#endif // LLVM_SUPPORT_VERSIONTUPLE_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/VirtualFileSystem.h b/contrib/libs/llvm16/include/llvm/Support/VirtualFileSystem.h new file mode 100644 index 00000000000..50cfd62002a --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/VirtualFileSystem.h @@ -0,0 +1,1135 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the virtual file system interface vfs::FileSystem. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_VIRTUALFILESYSTEM_H +#define LLVM_SUPPORT_VIRTUALFILESYSTEM_H + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" +#include <cassert> +#include <cstdint> +#include <ctime> +#include <memory> +#include <optional> +#include <stack> +#include <string> +#include <system_error> +#include <utility> +#include <vector> + +namespace llvm { + +class MemoryBuffer; +class MemoryBufferRef; +class Twine; + +namespace vfs { + +/// The result of a \p status operation. +class Status { + std::string Name; + llvm::sys::fs::UniqueID UID; + llvm::sys::TimePoint<> MTime; + uint32_t User; + uint32_t Group; + uint64_t Size; + llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::status_error; + llvm::sys::fs::perms Perms; + +public: + // FIXME: remove when files support multiple names + bool IsVFSMapped = false; + + /// Whether this entity has an external path different from the virtual path, + /// and the external path is exposed by leaking it through the abstraction. + /// For example, a RedirectingFileSystem will set this for paths where + /// UseExternalName is true. + /// + /// FIXME: Currently the external path is exposed by replacing the virtual + /// path in this Status object. Instead, we should leave the path in the + /// Status intact (matching the requested virtual path) - see + /// FileManager::getFileRef for how how we plan to fix this. + bool ExposesExternalVFSPath = false; + + Status() = default; + Status(const llvm::sys::fs::file_status &Status); + Status(const Twine &Name, llvm::sys::fs::UniqueID UID, + llvm::sys::TimePoint<> MTime, uint32_t User, uint32_t Group, + uint64_t Size, llvm::sys::fs::file_type Type, + llvm::sys::fs::perms Perms); + + /// Get a copy of a Status with a different size. + static Status copyWithNewSize(const Status &In, uint64_t NewSize); + /// Get a copy of a Status with a different name. + static Status copyWithNewName(const Status &In, const Twine &NewName); + static Status copyWithNewName(const llvm::sys::fs::file_status &In, + const Twine &NewName); + + /// Returns the name that should be used for this file or directory. + StringRef getName() const { return Name; } + + /// @name Status interface from llvm::sys::fs + /// @{ + llvm::sys::fs::file_type getType() const { return Type; } + llvm::sys::fs::perms getPermissions() const { return Perms; } + llvm::sys::TimePoint<> getLastModificationTime() const { return MTime; } + llvm::sys::fs::UniqueID getUniqueID() const { return UID; } + uint32_t getUser() const { return User; } + uint32_t getGroup() const { return Group; } + uint64_t getSize() const { return Size; } + /// @} + /// @name Status queries + /// These are static queries in llvm::sys::fs. + /// @{ + bool equivalent(const Status &Other) const; + bool isDirectory() const; + bool isRegularFile() const; + bool isOther() const; + bool isSymlink() const; + bool isStatusKnown() const; + bool exists() const; + /// @} +}; + +/// Represents an open file. +class File { +public: + /// Destroy the file after closing it (if open). + /// Sub-classes should generally call close() inside their destructors. We + /// cannot do that from the base class, since close is virtual. + virtual ~File(); + + /// Get the status of the file. + virtual llvm::ErrorOr<Status> status() = 0; + + /// Get the name of the file + virtual llvm::ErrorOr<std::string> getName() { + if (auto Status = status()) + return Status->getName().str(); + else + return Status.getError(); + } + + /// Get the contents of the file as a \p MemoryBuffer. + virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> + getBuffer(const Twine &Name, int64_t FileSize = -1, + bool RequiresNullTerminator = true, bool IsVolatile = false) = 0; + + /// Closes the file. + virtual std::error_code close() = 0; + + // Get the same file with a different path. + static ErrorOr<std::unique_ptr<File>> + getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P); + +protected: + // Set the file's underlying path. + virtual void setPath(const Twine &Path) {} +}; + +/// A member of a directory, yielded by a directory_iterator. +/// Only information available on most platforms is included. +class directory_entry { + std::string Path; + llvm::sys::fs::file_type Type = llvm::sys::fs::file_type::type_unknown; + +public: + directory_entry() = default; + directory_entry(std::string Path, llvm::sys::fs::file_type Type) + : Path(std::move(Path)), Type(Type) {} + + llvm::StringRef path() const { return Path; } + llvm::sys::fs::file_type type() const { return Type; } +}; + +namespace detail { + +/// An interface for virtual file systems to provide an iterator over the +/// (non-recursive) contents of a directory. +struct DirIterImpl { + virtual ~DirIterImpl(); + + /// Sets \c CurrentEntry to the next entry in the directory on success, + /// to directory_entry() at end, or returns a system-defined \c error_code. + virtual std::error_code increment() = 0; + + directory_entry CurrentEntry; +}; + +} // namespace detail + +/// An input iterator over the entries in a virtual path, similar to +/// llvm::sys::fs::directory_iterator. +class directory_iterator { + std::shared_ptr<detail::DirIterImpl> Impl; // Input iterator semantics on copy + +public: + directory_iterator(std::shared_ptr<detail::DirIterImpl> I) + : Impl(std::move(I)) { + assert(Impl.get() != nullptr && "requires non-null implementation"); + if (Impl->CurrentEntry.path().empty()) + Impl.reset(); // Normalize the end iterator to Impl == nullptr. + } + + /// Construct an 'end' iterator. + directory_iterator() = default; + + /// Equivalent to operator++, with an error code. + directory_iterator &increment(std::error_code &EC) { + assert(Impl && "attempting to increment past end"); + EC = Impl->increment(); + if (Impl->CurrentEntry.path().empty()) + Impl.reset(); // Normalize the end iterator to Impl == nullptr. + return *this; + } + + const directory_entry &operator*() const { return Impl->CurrentEntry; } + const directory_entry *operator->() const { return &Impl->CurrentEntry; } + + bool operator==(const directory_iterator &RHS) const { + if (Impl && RHS.Impl) + return Impl->CurrentEntry.path() == RHS.Impl->CurrentEntry.path(); + return !Impl && !RHS.Impl; + } + bool operator!=(const directory_iterator &RHS) const { + return !(*this == RHS); + } +}; + +class FileSystem; + +namespace detail { + +/// Keeps state for the recursive_directory_iterator. +struct RecDirIterState { + std::stack<directory_iterator, std::vector<directory_iterator>> Stack; + bool HasNoPushRequest = false; +}; + +} // end namespace detail + +/// An input iterator over the recursive contents of a virtual path, +/// similar to llvm::sys::fs::recursive_directory_iterator. +class recursive_directory_iterator { + FileSystem *FS; + std::shared_ptr<detail::RecDirIterState> + State; // Input iterator semantics on copy. + +public: + recursive_directory_iterator(FileSystem &FS, const Twine &Path, + std::error_code &EC); + + /// Construct an 'end' iterator. + recursive_directory_iterator() = default; + + /// Equivalent to operator++, with an error code. + recursive_directory_iterator &increment(std::error_code &EC); + + const directory_entry &operator*() const { return *State->Stack.top(); } + const directory_entry *operator->() const { return &*State->Stack.top(); } + + bool operator==(const recursive_directory_iterator &Other) const { + return State == Other.State; // identity + } + bool operator!=(const recursive_directory_iterator &RHS) const { + return !(*this == RHS); + } + + /// Gets the current level. Starting path is at level 0. + int level() const { + assert(!State->Stack.empty() && + "Cannot get level without any iteration state"); + return State->Stack.size() - 1; + } + + void no_push() { State->HasNoPushRequest = true; } +}; + +/// The virtual file system interface. +class FileSystem : public llvm::ThreadSafeRefCountedBase<FileSystem> { +public: + virtual ~FileSystem(); + + /// Get the status of the entry at \p Path, if one exists. + virtual llvm::ErrorOr<Status> status(const Twine &Path) = 0; + + /// Get a \p File object for the file at \p Path, if one exists. + virtual llvm::ErrorOr<std::unique_ptr<File>> + openFileForRead(const Twine &Path) = 0; + + /// This is a convenience method that opens a file, gets its content and then + /// closes the file. + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> + getBufferForFile(const Twine &Name, int64_t FileSize = -1, + bool RequiresNullTerminator = true, bool IsVolatile = false); + + /// Get a directory_iterator for \p Dir. + /// \note The 'end' iterator is directory_iterator(). + virtual directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) = 0; + + /// Set the working directory. This will affect all following operations on + /// this file system and may propagate down for nested file systems. + virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) = 0; + + /// Get the working directory of this file system. + virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const = 0; + + /// Gets real path of \p Path e.g. collapse all . and .. patterns, resolve + /// symlinks. For real file system, this uses `llvm::sys::fs::real_path`. + /// This returns errc::operation_not_permitted if not implemented by subclass. + virtual std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const; + + /// Check whether a file exists. Provided for convenience. + bool exists(const Twine &Path); + + /// Is the file mounted on a local filesystem? + virtual std::error_code isLocal(const Twine &Path, bool &Result); + + /// Make \a Path an absolute path. + /// + /// Makes \a Path absolute using the current directory if it is not already. + /// An empty \a Path will result in the current directory. + /// + /// /absolute/path => /absolute/path + /// relative/../path => <current-directory>/relative/../path + /// + /// \param Path A path that is modified to be an absolute path. + /// \returns success if \a path has been made absolute, otherwise a + /// platform-specific error_code. + virtual std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const; + + enum class PrintType { Summary, Contents, RecursiveContents }; + void print(raw_ostream &OS, PrintType Type = PrintType::Contents, + unsigned IndentLevel = 0) const { + printImpl(OS, Type, IndentLevel); + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const; +#endif + +protected: + virtual void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const { + printIndent(OS, IndentLevel); + OS << "FileSystem\n"; + } + + void printIndent(raw_ostream &OS, unsigned IndentLevel) const { + for (unsigned i = 0; i < IndentLevel; ++i) + OS << " "; + } +}; + +/// Gets an \p vfs::FileSystem for the 'real' file system, as seen by +/// the operating system. +/// The working directory is linked to the process's working directory. +/// (This is usually thread-hostile). +IntrusiveRefCntPtr<FileSystem> getRealFileSystem(); + +/// Create an \p vfs::FileSystem for the 'real' file system, as seen by +/// the operating system. +/// It has its own working directory, independent of (but initially equal to) +/// that of the process. +std::unique_ptr<FileSystem> createPhysicalFileSystem(); + +/// A file system that allows overlaying one \p AbstractFileSystem on top +/// of another. +/// +/// Consists of a stack of >=1 \p FileSystem objects, which are treated as being +/// one merged file system. When there is a directory that exists in more than +/// one file system, the \p OverlayFileSystem contains a directory containing +/// the union of their contents. The attributes (permissions, etc.) of the +/// top-most (most recently added) directory are used. When there is a file +/// that exists in more than one file system, the file in the top-most file +/// system overrides the other(s). +class OverlayFileSystem : public FileSystem { + using FileSystemList = SmallVector<IntrusiveRefCntPtr<FileSystem>, 1>; + + /// The stack of file systems, implemented as a list in order of + /// their addition. + FileSystemList FSList; + +public: + OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> Base); + + /// Pushes a file system on top of the stack. + void pushOverlay(IntrusiveRefCntPtr<FileSystem> FS); + + llvm::ErrorOr<Status> status(const Twine &Path) override; + llvm::ErrorOr<std::unique_ptr<File>> + openFileForRead(const Twine &Path) override; + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + std::error_code isLocal(const Twine &Path, bool &Result) override; + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override; + + using iterator = FileSystemList::reverse_iterator; + using const_iterator = FileSystemList::const_reverse_iterator; + using reverse_iterator = FileSystemList::iterator; + using const_reverse_iterator = FileSystemList::const_iterator; + using range = iterator_range<iterator>; + using const_range = iterator_range<const_iterator>; + + /// Get an iterator pointing to the most recently added file system. + iterator overlays_begin() { return FSList.rbegin(); } + const_iterator overlays_begin() const { return FSList.rbegin(); } + + /// Get an iterator pointing one-past the least recently added file system. + iterator overlays_end() { return FSList.rend(); } + const_iterator overlays_end() const { return FSList.rend(); } + + /// Get an iterator pointing to the least recently added file system. + reverse_iterator overlays_rbegin() { return FSList.begin(); } + const_reverse_iterator overlays_rbegin() const { return FSList.begin(); } + + /// Get an iterator pointing one-past the most recently added file system. + reverse_iterator overlays_rend() { return FSList.end(); } + const_reverse_iterator overlays_rend() const { return FSList.end(); } + + range overlays_range() { return llvm::reverse(FSList); } + const_range overlays_range() const { return llvm::reverse(FSList); } + +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; +}; + +class CaseInsensitiveFileSystem : public FileSystem { + IntrusiveRefCntPtr<FileSystem> Base; + + /// Try to find Path by means of case-insensitive lookup. Stores the result in + /// FoundPath on success, or returns an error code otherwise. + std::error_code findCaseInsensitivePath(StringRef Path, + SmallVectorImpl<char> &FoundPath); + + /// Attempt to exclude the possibility that File exists in Dir based on + /// previous information. + bool exclude(StringRef Dir, StringRef File); + + /// Map from directory to map from lowercase to real-case filename. + llvm::StringMap<llvm::StringMap<std::string>> Maps; + +public: + CaseInsensitiveFileSystem(IntrusiveRefCntPtr<FileSystem> Base) : Base(Base) {} + + llvm::ErrorOr<Status> status(const Twine &Path) override; + llvm::ErrorOr<std::unique_ptr<File>> + openFileForRead(const Twine &Path) override; + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { + return Base->getCurrentWorkingDirectory(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + Maps.clear(); + return Base->setCurrentWorkingDirectory(Path); + } +}; + +/// By default, this delegates all calls to the underlying file system. This +/// is useful when derived file systems want to override some calls and still +/// proxy other calls. +class ProxyFileSystem : public FileSystem { +public: + explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS) + : FS(std::move(FS)) {} + + llvm::ErrorOr<Status> status(const Twine &Path) override { + return FS->status(Path); + } + llvm::ErrorOr<std::unique_ptr<File>> + openFileForRead(const Twine &Path) override { + return FS->openFileForRead(Path); + } + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { + return FS->dir_begin(Dir, EC); + } + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override { + return FS->getRealPath(Path, Output); + } + std::error_code isLocal(const Twine &Path, bool &Result) override { + return FS->isLocal(Path, Result); + } + +protected: + FileSystem &getUnderlyingFS() const { return *FS; } + +private: + IntrusiveRefCntPtr<FileSystem> FS; + + virtual void anchor(); +}; + +namespace detail { + +class InMemoryDirectory; +class InMemoryNode; + +struct NewInMemoryNodeInfo { + llvm::sys::fs::UniqueID DirUID; + StringRef Path; + StringRef Name; + time_t ModificationTime; + std::unique_ptr<llvm::MemoryBuffer> Buffer; + uint32_t User; + uint32_t Group; + llvm::sys::fs::file_type Type; + llvm::sys::fs::perms Perms; + + Status makeStatus() const; +}; + +class NamedNodeOrError { + ErrorOr<std::pair<llvm::SmallString<128>, const detail::InMemoryNode *>> + Value; + +public: + NamedNodeOrError(llvm::SmallString<128> Name, + const detail::InMemoryNode *Node) + : Value(std::make_pair(Name, Node)) {} + NamedNodeOrError(std::error_code EC) : Value(EC) {} + NamedNodeOrError(llvm::errc EC) : Value(EC) {} + + StringRef getName() const { return (*Value).first; } + explicit operator bool() const { return static_cast<bool>(Value); } + operator std::error_code() const { return Value.getError(); } + std::error_code getError() const { return Value.getError(); } + const detail::InMemoryNode *operator*() const { return (*Value).second; } +}; + +} // namespace detail + +/// An in-memory file system. +class InMemoryFileSystem : public FileSystem { + std::unique_ptr<detail::InMemoryDirectory> Root; + std::string WorkingDirectory; + bool UseNormalizedPaths = true; + + using MakeNodeFn = llvm::function_ref<std::unique_ptr<detail::InMemoryNode>( + detail::NewInMemoryNodeInfo)>; + + /// Create node with \p MakeNode and add it into this filesystem at \p Path. + bool addFile(const Twine &Path, time_t ModificationTime, + std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::optional<uint32_t> User, std::optional<uint32_t> Group, + std::optional<llvm::sys::fs::file_type> Type, + std::optional<llvm::sys::fs::perms> Perms, MakeNodeFn MakeNode); + + /// Looks up the in-memory node for the path \p P. + /// If \p FollowFinalSymlink is true, the returned node is guaranteed to + /// not be a symlink and its path may differ from \p P. + detail::NamedNodeOrError lookupNode(const Twine &P, bool FollowFinalSymlink, + size_t SymlinkDepth = 0) const; + + class DirIterator; + +public: + explicit InMemoryFileSystem(bool UseNormalizedPaths = true); + ~InMemoryFileSystem() override; + + /// Add a file containing a buffer or a directory to the VFS with a + /// path. The VFS owns the buffer. If present, User, Group, Type + /// and Perms apply to the newly-created file or directory. + /// \return true if the file or directory was successfully added, + /// false if the file or directory already exists in the file system with + /// different contents. + bool addFile(const Twine &Path, time_t ModificationTime, + std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::optional<uint32_t> User = std::nullopt, + std::optional<uint32_t> Group = std::nullopt, + std::optional<llvm::sys::fs::file_type> Type = std::nullopt, + std::optional<llvm::sys::fs::perms> Perms = std::nullopt); + + /// Add a hard link to a file. + /// + /// Here hard links are not intended to be fully equivalent to the classical + /// filesystem. Both the hard link and the file share the same buffer and + /// status (and thus have the same UniqueID). Because of this there is no way + /// to distinguish between the link and the file after the link has been + /// added. + /// + /// The \p Target path must be an existing file or a hardlink. The + /// \p NewLink file must not have been added before. The \p Target + /// path must not be a directory. The \p NewLink node is added as a hard + /// link which points to the resolved file of \p Target node. + /// \return true if the above condition is satisfied and hardlink was + /// successfully created, false otherwise. + bool addHardLink(const Twine &NewLink, const Twine &Target); + + /// Arbitrary max depth to search through symlinks. We can get into problems + /// if a link links to a link that links back to the link, for example. + static constexpr size_t MaxSymlinkDepth = 16; + + /// Add a symbolic link. Unlike a HardLink, because \p Target doesn't need + /// to refer to a file (or refer to anything, as it happens). Also, an + /// in-memory directory for \p Target isn't automatically created. + bool + addSymbolicLink(const Twine &NewLink, const Twine &Target, + time_t ModificationTime, + std::optional<uint32_t> User = std::nullopt, + std::optional<uint32_t> Group = std::nullopt, + std::optional<llvm::sys::fs::perms> Perms = std::nullopt); + + /// Add a buffer to the VFS with a path. The VFS does not own the buffer. + /// If present, User, Group, Type and Perms apply to the newly-created file + /// or directory. + /// \return true if the file or directory was successfully added, + /// false if the file or directory already exists in the file system with + /// different contents. + bool addFileNoOwn(const Twine &Path, time_t ModificationTime, + const llvm::MemoryBufferRef &Buffer, + std::optional<uint32_t> User = std::nullopt, + std::optional<uint32_t> Group = std::nullopt, + std::optional<llvm::sys::fs::file_type> Type = std::nullopt, + std::optional<llvm::sys::fs::perms> Perms = std::nullopt); + + std::string toString() const; + + /// Return true if this file system normalizes . and .. in paths. + bool useNormalizedPaths() const { return UseNormalizedPaths; } + + llvm::ErrorOr<Status> status(const Twine &Path) override; + llvm::ErrorOr<std::unique_ptr<File>> + openFileForRead(const Twine &Path) override; + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { + return WorkingDirectory; + } + /// Canonicalizes \p Path by combining with the current working + /// directory and normalizing the path (e.g. remove dots). If the current + /// working directory is not set, this returns errc::operation_not_permitted. + /// + /// This doesn't resolve symlinks as they are not supported in in-memory file + /// system. + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override; + std::error_code isLocal(const Twine &Path, bool &Result) override; + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; +}; + +/// Get a globally unique ID for a virtual file or directory. +llvm::sys::fs::UniqueID getNextVirtualUniqueID(); + +/// Gets a \p FileSystem for a virtual file system described in YAML +/// format. +std::unique_ptr<FileSystem> +getVFSFromYAML(std::unique_ptr<llvm::MemoryBuffer> Buffer, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, void *DiagContext = nullptr, + IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem()); + +struct YAMLVFSEntry { + template <typename T1, typename T2> + YAMLVFSEntry(T1 &&VPath, T2 &&RPath, bool IsDirectory = false) + : VPath(std::forward<T1>(VPath)), RPath(std::forward<T2>(RPath)), + IsDirectory(IsDirectory) {} + std::string VPath; + std::string RPath; + bool IsDirectory = false; +}; + +class RedirectingFSDirIterImpl; +class RedirectingFileSystemParser; + +/// A virtual file system parsed from a YAML file. +/// +/// Currently, this class allows creating virtual files and directories. Virtual +/// files map to existing external files in \c ExternalFS, and virtual +/// directories may either map to existing directories in \c ExternalFS or list +/// their contents in the form of other virtual directories and/or files. +/// +/// The basic structure of the parsed file is: +/// \verbatim +/// { +/// 'version': <version number>, +/// <optional configuration> +/// 'roots': [ +/// <directory entries> +/// ] +/// } +/// \endverbatim +/// +/// The roots may be absolute or relative. If relative they will be made +/// absolute against either current working directory or the directory where +/// the Overlay YAML file is located, depending on the 'root-relative' +/// configuration. +/// +/// All configuration options are optional. +/// 'case-sensitive': <boolean, default=(true for Posix, false for Windows)> +/// 'use-external-names': <boolean, default=true> +/// 'root-relative': <string, one of 'cwd' or 'overlay-dir', default='cwd'> +/// 'overlay-relative': <boolean, default=false> +/// 'fallthrough': <boolean, default=true, deprecated - use 'redirecting-with' +/// instead> +/// 'redirecting-with': <string, one of 'fallthrough', 'fallback', or +/// 'redirect-only', default='fallthrough'> +/// +/// To clarify, 'root-relative' option will prepend the current working +/// directory, or the overlay directory to the 'roots->name' field only if +/// 'roots->name' is a relative path. On the other hand, when 'overlay-relative' +/// is set to 'true', external paths will always be prepended with the overlay +/// directory, even if external paths are not relative paths. The +/// 'root-relative' option has no interaction with the 'overlay-relative' +/// option. +/// +/// Virtual directories that list their contents are represented as +/// \verbatim +/// { +/// 'type': 'directory', +/// 'name': <string>, +/// 'contents': [ <file or directory entries> ] +/// } +/// \endverbatim +/// +/// The default attributes for such virtual directories are: +/// \verbatim +/// MTime = now() when created +/// Perms = 0777 +/// User = Group = 0 +/// Size = 0 +/// UniqueID = unspecified unique value +/// \endverbatim +/// +/// When a path prefix matches such a directory, the next component in the path +/// is matched against the entries in the 'contents' array. +/// +/// Re-mapped directories, on the other hand, are represented as +/// /// \verbatim +/// { +/// 'type': 'directory-remap', +/// 'name': <string>, +/// 'use-external-name': <boolean>, # Optional +/// 'external-contents': <path to external directory> +/// } +/// \endverbatim +/// +/// and inherit their attributes from the external directory. When a path +/// prefix matches such an entry, the unmatched components are appended to the +/// 'external-contents' path, and the resulting path is looked up in the +/// external file system instead. +/// +/// Re-mapped files are represented as +/// \verbatim +/// { +/// 'type': 'file', +/// 'name': <string>, +/// 'use-external-name': <boolean>, # Optional +/// 'external-contents': <path to external file> +/// } +/// \endverbatim +/// +/// Their attributes and file contents are determined by looking up the file at +/// their 'external-contents' path in the external file system. +/// +/// For 'file', 'directory' and 'directory-remap' entries the 'name' field may +/// contain multiple path components (e.g. /path/to/file). However, any +/// directory in such a path that contains more than one child must be uniquely +/// represented by a 'directory' entry. +/// +/// When the 'use-external-name' field is set, calls to \a vfs::File::status() +/// give the external (remapped) filesystem name instead of the name the file +/// was accessed by. This is an intentional leak through the \a +/// RedirectingFileSystem abstraction layer. It enables clients to discover +/// (and use) the external file location when communicating with users or tools +/// that don't use the same VFS overlay. +/// +/// FIXME: 'use-external-name' causes behaviour that's inconsistent with how +/// "real" filesystems behave. Maybe there should be a separate channel for +/// this information. +class RedirectingFileSystem : public vfs::FileSystem { +public: + enum EntryKind { EK_Directory, EK_DirectoryRemap, EK_File }; + enum NameKind { NK_NotSet, NK_External, NK_Virtual }; + + /// The type of redirection to perform. + enum class RedirectKind { + /// Lookup the redirected path first (ie. the one specified in + /// 'external-contents') and if that fails "fallthrough" to a lookup of the + /// originally provided path. + Fallthrough, + /// Lookup the provided path first and if that fails, "fallback" to a + /// lookup of the redirected path. + Fallback, + /// Only lookup the redirected path, do not lookup the originally provided + /// path. + RedirectOnly + }; + + /// The type of relative path used by Roots. + enum class RootRelativeKind { + /// The roots are relative to the current working directory. + CWD, + /// The roots are relative to the directory where the Overlay YAML file + // locates. + OverlayDir + }; + + /// A single file or directory in the VFS. + class Entry { + EntryKind Kind; + std::string Name; + + public: + Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {} + virtual ~Entry() = default; + + StringRef getName() const { return Name; } + EntryKind getKind() const { return Kind; } + }; + + /// A directory in the vfs with explicitly specified contents. + class DirectoryEntry : public Entry { + std::vector<std::unique_ptr<Entry>> Contents; + Status S; + + public: + /// Constructs a directory entry with explicitly specified contents. + DirectoryEntry(StringRef Name, std::vector<std::unique_ptr<Entry>> Contents, + Status S) + : Entry(EK_Directory, Name), Contents(std::move(Contents)), + S(std::move(S)) {} + + /// Constructs an empty directory entry. + DirectoryEntry(StringRef Name, Status S) + : Entry(EK_Directory, Name), S(std::move(S)) {} + + Status getStatus() { return S; } + + void addContent(std::unique_ptr<Entry> Content) { + Contents.push_back(std::move(Content)); + } + + Entry *getLastContent() const { return Contents.back().get(); } + + using iterator = decltype(Contents)::iterator; + + iterator contents_begin() { return Contents.begin(); } + iterator contents_end() { return Contents.end(); } + + static bool classof(const Entry *E) { return E->getKind() == EK_Directory; } + }; + + /// A file or directory in the vfs that is mapped to a file or directory in + /// the external filesystem. + class RemapEntry : public Entry { + std::string ExternalContentsPath; + NameKind UseName; + + protected: + RemapEntry(EntryKind K, StringRef Name, StringRef ExternalContentsPath, + NameKind UseName) + : Entry(K, Name), ExternalContentsPath(ExternalContentsPath), + UseName(UseName) {} + + public: + StringRef getExternalContentsPath() const { return ExternalContentsPath; } + + /// Whether to use the external path as the name for this file or directory. + bool useExternalName(bool GlobalUseExternalName) const { + return UseName == NK_NotSet ? GlobalUseExternalName + : (UseName == NK_External); + } + + NameKind getUseName() const { return UseName; } + + static bool classof(const Entry *E) { + switch (E->getKind()) { + case EK_DirectoryRemap: + [[fallthrough]]; + case EK_File: + return true; + case EK_Directory: + return false; + } + llvm_unreachable("invalid entry kind"); + } + }; + + /// A directory in the vfs that maps to a directory in the external file + /// system. + class DirectoryRemapEntry : public RemapEntry { + public: + DirectoryRemapEntry(StringRef Name, StringRef ExternalContentsPath, + NameKind UseName) + : RemapEntry(EK_DirectoryRemap, Name, ExternalContentsPath, UseName) {} + + static bool classof(const Entry *E) { + return E->getKind() == EK_DirectoryRemap; + } + }; + + /// A file in the vfs that maps to a file in the external file system. + class FileEntry : public RemapEntry { + public: + FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName) + : RemapEntry(EK_File, Name, ExternalContentsPath, UseName) {} + + static bool classof(const Entry *E) { return E->getKind() == EK_File; } + }; + + /// Represents the result of a path lookup into the RedirectingFileSystem. + struct LookupResult { + /// The entry the looked-up path corresponds to. + Entry *E; + + private: + /// When the found Entry is a DirectoryRemapEntry, stores the path in the + /// external file system that the looked-up path in the virtual file system + // corresponds to. + std::optional<std::string> ExternalRedirect; + + public: + LookupResult(Entry *E, sys::path::const_iterator Start, + sys::path::const_iterator End); + + /// If the found Entry maps the the input path to a path in the external + /// file system (i.e. it is a FileEntry or DirectoryRemapEntry), returns + /// that path. + std::optional<StringRef> getExternalRedirect() const { + if (isa<DirectoryRemapEntry>(E)) + return StringRef(*ExternalRedirect); + if (auto *FE = dyn_cast<FileEntry>(E)) + return FE->getExternalContentsPath(); + return std::nullopt; + } + }; + +private: + friend class RedirectingFSDirIterImpl; + friend class RedirectingFileSystemParser; + + /// Canonicalize path by removing ".", "..", "./", components. This is + /// a VFS request, do not bother about symlinks in the path components + /// but canonicalize in order to perform the correct entry search. + std::error_code makeCanonical(SmallVectorImpl<char> &Path) const; + + /// Get the File status, or error, from the underlying external file system. + /// This returns the status with the originally requested name, while looking + /// up the entry using the canonical path. + ErrorOr<Status> getExternalStatus(const Twine &CanonicalPath, + const Twine &OriginalPath) const; + + /// Make \a Path an absolute path. + /// + /// Makes \a Path absolute using the \a WorkingDir if it is not already. + /// + /// /absolute/path => /absolute/path + /// relative/../path => <WorkingDir>/relative/../path + /// + /// \param WorkingDir A path that will be used as the base Dir if \a Path + /// is not already absolute. + /// \param Path A path that is modified to be an absolute path. + /// \returns success if \a path has been made absolute, otherwise a + /// platform-specific error_code. + std::error_code makeAbsolute(StringRef WorkingDir, + SmallVectorImpl<char> &Path) const; + + // In a RedirectingFileSystem, keys can be specified in Posix or Windows + // style (or even a mixture of both), so this comparison helper allows + // slashes (representing a root) to match backslashes (and vice versa). Note + // that, other than the root, path components should not contain slashes or + // backslashes. + bool pathComponentMatches(llvm::StringRef lhs, llvm::StringRef rhs) const { + if ((CaseSensitive ? lhs.equals(rhs) : lhs.equals_insensitive(rhs))) + return true; + return (lhs == "/" && rhs == "\\") || (lhs == "\\" && rhs == "/"); + } + + /// The root(s) of the virtual file system. + std::vector<std::unique_ptr<Entry>> Roots; + + /// The current working directory of the file system. + std::string WorkingDirectory; + + /// The file system to use for external references. + IntrusiveRefCntPtr<FileSystem> ExternalFS; + + /// This represents the directory path that the YAML file is located. + /// This will be prefixed to each 'external-contents' if IsRelativeOverlay + /// is set. This will also be prefixed to each 'roots->name' if RootRelative + /// is set to RootRelativeKind::OverlayDir and the path is relative. + std::string OverlayFileDir; + + /// @name Configuration + /// @{ + + /// Whether to perform case-sensitive comparisons. + /// + /// Currently, case-insensitive matching only works correctly with ASCII. + bool CaseSensitive = is_style_posix(sys::path::Style::native); + + /// IsRelativeOverlay marks whether a OverlayFileDir path must + /// be prefixed in every 'external-contents' when reading from YAML files. + bool IsRelativeOverlay = false; + + /// Whether to use to use the value of 'external-contents' for the + /// names of files. This global value is overridable on a per-file basis. + bool UseExternalNames = true; + + /// Determines the lookups to perform, as well as their order. See + /// \c RedirectKind for details. + RedirectKind Redirection = RedirectKind::Fallthrough; + + /// Determine the prefix directory if the roots are relative paths. See + /// \c RootRelativeKind for details. + RootRelativeKind RootRelative = RootRelativeKind::CWD; + /// @} + + RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS); + + /// Looks up the path <tt>[Start, End)</tt> in \p From, possibly recursing + /// into the contents of \p From if it is a directory. Returns a LookupResult + /// giving the matched entry and, if that entry is a FileEntry or + /// DirectoryRemapEntry, the path it redirects to in the external file system. + ErrorOr<LookupResult> lookupPathImpl(llvm::sys::path::const_iterator Start, + llvm::sys::path::const_iterator End, + Entry *From) const; + + /// Get the status for a path with the provided \c LookupResult. + ErrorOr<Status> status(const Twine &CanonicalPath, const Twine &OriginalPath, + const LookupResult &Result); + +public: + /// Looks up \p Path in \c Roots and returns a LookupResult giving the + /// matched entry and, if the entry was a FileEntry or DirectoryRemapEntry, + /// the path it redirects to in the external file system. + ErrorOr<LookupResult> lookupPath(StringRef Path) const; + + /// Parses \p Buffer, which is expected to be in YAML format and + /// returns a virtual file system representing its contents. + static std::unique_ptr<RedirectingFileSystem> + create(std::unique_ptr<MemoryBuffer> Buffer, + SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, + void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS); + + /// Redirect each of the remapped files from first to second. + static std::unique_ptr<RedirectingFileSystem> + create(ArrayRef<std::pair<std::string, std::string>> RemappedFiles, + bool UseExternalNames, FileSystem &ExternalFS); + + ErrorOr<Status> status(const Twine &Path) override; + ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; + + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override; + + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; + + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + + std::error_code isLocal(const Twine &Path, bool &Result) override; + + std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const override; + + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + + void setOverlayFileDir(StringRef PrefixDir); + + StringRef getOverlayFileDir() const; + + /// Sets the redirection kind to \c Fallthrough if true or \c RedirectOnly + /// otherwise. Will removed in the future, use \c setRedirection instead. + void setFallthrough(bool Fallthrough); + + void setRedirection(RedirectingFileSystem::RedirectKind Kind); + + std::vector<llvm::StringRef> getRoots() const; + + void printEntry(raw_ostream &OS, Entry *E, unsigned IndentLevel = 0) const; + +protected: + void printImpl(raw_ostream &OS, PrintType Type, + unsigned IndentLevel) const override; +}; + +/// Collect all pairs of <virtual path, real path> entries from the +/// \p YAMLFilePath. This is used by the module dependency collector to forward +/// the entries into the reproducer output VFS YAML file. +void collectVFSFromYAML( + std::unique_ptr<llvm::MemoryBuffer> Buffer, + llvm::SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, + SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, + void *DiagContext = nullptr, + IntrusiveRefCntPtr<FileSystem> ExternalFS = getRealFileSystem()); + +class YAMLVFSWriter { + std::vector<YAMLVFSEntry> Mappings; + std::optional<bool> IsCaseSensitive; + std::optional<bool> IsOverlayRelative; + std::optional<bool> UseExternalNames; + std::string OverlayDir; + + void addEntry(StringRef VirtualPath, StringRef RealPath, bool IsDirectory); + +public: + YAMLVFSWriter() = default; + + void addFileMapping(StringRef VirtualPath, StringRef RealPath); + void addDirectoryMapping(StringRef VirtualPath, StringRef RealPath); + + void setCaseSensitivity(bool CaseSensitive) { + IsCaseSensitive = CaseSensitive; + } + + void setUseExternalNames(bool UseExtNames) { UseExternalNames = UseExtNames; } + + void setOverlayDir(StringRef OverlayDirectory) { + IsOverlayRelative = true; + OverlayDir.assign(OverlayDirectory.str()); + } + + const std::vector<YAMLVFSEntry> &getMappings() const { return Mappings; } + + void write(llvm::raw_ostream &OS); +}; + +} // namespace vfs +} // namespace llvm + +#endif // LLVM_SUPPORT_VIRTUALFILESYSTEM_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Watchdog.h b/contrib/libs/llvm16/include/llvm/Support/Watchdog.h new file mode 100644 index 00000000000..190f8bc057e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Watchdog.h @@ -0,0 +1,48 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- Watchdog.h - Watchdog timer ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the llvm::sys::Watchdog class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_WATCHDOG_H +#define LLVM_SUPPORT_WATCHDOG_H + +#include "llvm/Support/Compiler.h" + +namespace llvm { + namespace sys { + + /// This class provides an abstraction for a timeout around an operation + /// that must complete in a given amount of time. Failure to complete before + /// the timeout is an unrecoverable situation and no mechanisms to attempt + /// to handle it are provided. + class Watchdog { + public: + Watchdog(unsigned int seconds); + ~Watchdog(); + private: + // Noncopyable. + Watchdog(const Watchdog &other) = delete; + Watchdog &operator=(const Watchdog &other) = delete; + }; + } +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Win64EH.h b/contrib/libs/llvm16/include/llvm/Support/Win64EH.h new file mode 100644 index 00000000000..088d0596930 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Win64EH.h @@ -0,0 +1,230 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/Win64EH.h ---Win64 EH Constants-------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains constants and structures used for implementing +// exception handling on Win64 platforms. For more information, see +// http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_WIN64EH_H +#define LLVM_SUPPORT_WIN64EH_H + +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace Win64EH { + +/// UnwindOpcodes - Enumeration whose values specify a single operation in +/// the prolog of a function. +enum UnwindOpcodes { + // The following set of unwind opcodes is for x86_64. They are documented at + // https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64. + // Some generic values from this set are used for other architectures too. + UOP_PushNonVol = 0, + UOP_AllocLarge, + UOP_AllocSmall, + UOP_SetFPReg, + UOP_SaveNonVol, + UOP_SaveNonVolBig, + UOP_Epilog, + UOP_SpareCode, + UOP_SaveXMM128, + UOP_SaveXMM128Big, + UOP_PushMachFrame, + // The following set of unwind opcodes is for ARM64. They are documented at + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + UOP_AllocMedium, + UOP_SaveR19R20X, + UOP_SaveFPLRX, + UOP_SaveFPLR, + UOP_SaveReg, + UOP_SaveRegX, + UOP_SaveRegP, + UOP_SaveRegPX, + UOP_SaveLRPair, + UOP_SaveFReg, + UOP_SaveFRegX, + UOP_SaveFRegP, + UOP_SaveFRegPX, + UOP_SetFP, + UOP_AddFP, + UOP_Nop, + UOP_End, + UOP_SaveNext, + UOP_TrapFrame, + UOP_Context, + UOP_ClearUnwoundToCall, + UOP_PACSignLR, + UOP_SaveAnyRegI, + UOP_SaveAnyRegIP, + UOP_SaveAnyRegD, + UOP_SaveAnyRegDP, + UOP_SaveAnyRegQ, + UOP_SaveAnyRegQP, + UOP_SaveAnyRegIX, + UOP_SaveAnyRegIPX, + UOP_SaveAnyRegDX, + UOP_SaveAnyRegDPX, + UOP_SaveAnyRegQX, + UOP_SaveAnyRegQPX, + + // The following set of unwind opcodes is for ARM. They are documented at + // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling + + // Stack allocations use UOP_AllocSmall, UOP_AllocLarge from above, plus + // the following. AllocSmall, AllocLarge and AllocHuge represent a 16 bit + // instruction, while the WideAlloc* opcodes represent a 32 bit instruction. + // Small can represent a stack offset of 0x7f*4 (252) bytes, Medium can + // represent up to 0x3ff*4 (4092) bytes, Large up to 0xffff*4 (262140) bytes, + // and Huge up to 0xffffff*4 (67108860) bytes. + UOP_AllocHuge, + UOP_WideAllocMedium, + UOP_WideAllocLarge, + UOP_WideAllocHuge, + + UOP_WideSaveRegMask, + UOP_SaveSP, + UOP_SaveRegsR4R7LR, + UOP_WideSaveRegsR4R11LR, + UOP_SaveFRegD8D15, + UOP_SaveRegMask, + UOP_SaveLR, + UOP_SaveFRegD0D15, + UOP_SaveFRegD16D31, + // Using UOP_Nop from above + UOP_WideNop, + // Using UOP_End from above + UOP_EndNop, + UOP_WideEndNop, + // A custom unspecified opcode, consisting of one or more bytes. This + // allows producing opcodes in the implementation defined/reserved range. + UOP_Custom, +}; + +/// UnwindCode - This union describes a single operation in a function prolog, +/// or part thereof. +union UnwindCode { + struct { + uint8_t CodeOffset; + uint8_t UnwindOpAndOpInfo; + } u; + support::ulittle16_t FrameOffset; + + uint8_t getUnwindOp() const { + return u.UnwindOpAndOpInfo & 0x0F; + } + uint8_t getOpInfo() const { + return (u.UnwindOpAndOpInfo >> 4) & 0x0F; + } +}; + +enum { + /// UNW_ExceptionHandler - Specifies that this function has an exception + /// handler. + UNW_ExceptionHandler = 0x01, + /// UNW_TerminateHandler - Specifies that this function has a termination + /// handler. + UNW_TerminateHandler = 0x02, + /// UNW_ChainInfo - Specifies that this UnwindInfo structure is chained to + /// another one. + UNW_ChainInfo = 0x04 +}; + +/// RuntimeFunction - An entry in the table of functions with unwind info. +struct RuntimeFunction { + support::ulittle32_t StartAddress; + support::ulittle32_t EndAddress; + support::ulittle32_t UnwindInfoOffset; +}; + +/// UnwindInfo - An entry in the exception table. +struct UnwindInfo { + uint8_t VersionAndFlags; + uint8_t PrologSize; + uint8_t NumCodes; + uint8_t FrameRegisterAndOffset; + UnwindCode UnwindCodes[1]; + + uint8_t getVersion() const { + return VersionAndFlags & 0x07; + } + uint8_t getFlags() const { + return (VersionAndFlags >> 3) & 0x1f; + } + uint8_t getFrameRegister() const { + return FrameRegisterAndOffset & 0x0f; + } + uint8_t getFrameOffset() const { + return (FrameRegisterAndOffset >> 4) & 0x0f; + } + + // The data after unwindCodes depends on flags. + // If UNW_ExceptionHandler or UNW_TerminateHandler is set then follows + // the address of the language-specific exception handler. + // If UNW_ChainInfo is set then follows a RuntimeFunction which defines + // the chained unwind info. + // For more information please see MSDN at: + // http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx + + /// Return pointer to language specific data part of UnwindInfo. + void *getLanguageSpecificData() { + return reinterpret_cast<void *>(&UnwindCodes[(NumCodes+1) & ~1]); + } + + /// Return pointer to language specific data part of UnwindInfo. + const void *getLanguageSpecificData() const { + return reinterpret_cast<const void *>(&UnwindCodes[(NumCodes + 1) & ~1]); + } + + /// Return image-relative offset of language-specific exception handler. + uint32_t getLanguageSpecificHandlerOffset() const { + return *reinterpret_cast<const support::ulittle32_t *>( + getLanguageSpecificData()); + } + + /// Set image-relative offset of language-specific exception handler. + void setLanguageSpecificHandlerOffset(uint32_t offset) { + *reinterpret_cast<support::ulittle32_t *>(getLanguageSpecificData()) = + offset; + } + + /// Return pointer to exception-specific data. + void *getExceptionData() { + return reinterpret_cast<void *>(reinterpret_cast<uint32_t *>( + getLanguageSpecificData())+1); + } + + /// Return pointer to chained unwind info. + RuntimeFunction *getChainedFunctionEntry() { + return reinterpret_cast<RuntimeFunction *>(getLanguageSpecificData()); + } + + /// Return pointer to chained unwind info. + const RuntimeFunction *getChainedFunctionEntry() const { + return reinterpret_cast<const RuntimeFunction *>(getLanguageSpecificData()); + } +}; + + +} // End of namespace Win64EH +} // End of namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/Windows/WindowsSupport.h b/contrib/libs/llvm16/include/llvm/Support/Windows/WindowsSupport.h new file mode 100644 index 00000000000..183b011f564 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/Windows/WindowsSupport.h @@ -0,0 +1,263 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines things specific to Windows implementations. In addition to +// providing some helpers for working with win32 APIs, this header wraps +// <windows.h> with some portability macros. Always include WindowsSupport.h +// instead of including <windows.h> directly. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic Win32 code that +//=== is guaranteed to work on *all* Win32 variants. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H +#define LLVM_SUPPORT_WINDOWSSUPPORT_H + +// mingw-w64 tends to define it as 0x0502 in its headers. +#undef _WIN32_WINNT +#undef _WIN32_IE + +// Require at least Windows 7 API. +#define _WIN32_WINNT 0x0601 +#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed. +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Config/llvm-config.h" // Get build system configuration settings +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/VersionTuple.h" +#include <cassert> +#include <string> +#include <system_error> +#include <windows.h> + +// Must be included after windows.h +#include <wincrypt.h> + +namespace llvm { + +/// Determines if the program is running on Windows 8 or newer. This +/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended +/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't +/// yet have VersionHelpers.h, so we have our own helper. +bool RunningWindows8OrGreater(); + +/// Determines if the program is running on Windows 11 or Windows Server 2022. +bool RunningWindows11OrGreater(); + +/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses +/// RtlGetVersion or GetVersionEx under the hood depending on what is available. +/// GetVersionEx is deprecated, but this API exposes the build number which can +/// be useful for working around certain kernel bugs. +llvm::VersionTuple GetWindowsOSVersion(); + +bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix); + +// Include GetLastError() in a fatal error message. +[[noreturn]] inline void ReportLastErrorFatal(const char *Msg) { + std::string ErrMsg; + MakeErrMsg(&ErrMsg, Msg); + llvm::report_fatal_error(Twine(ErrMsg)); +} + +template <typename HandleTraits> +class ScopedHandle { + typedef typename HandleTraits::handle_type handle_type; + handle_type Handle; + + ScopedHandle(const ScopedHandle &other) = delete; + void operator=(const ScopedHandle &other) = delete; +public: + ScopedHandle() + : Handle(HandleTraits::GetInvalid()) {} + + explicit ScopedHandle(handle_type h) + : Handle(h) {} + + ~ScopedHandle() { + if (HandleTraits::IsValid(Handle)) + HandleTraits::Close(Handle); + } + + handle_type take() { + handle_type t = Handle; + Handle = HandleTraits::GetInvalid(); + return t; + } + + ScopedHandle &operator=(handle_type h) { + if (HandleTraits::IsValid(Handle)) + HandleTraits::Close(Handle); + Handle = h; + return *this; + } + + // True if Handle is valid. + explicit operator bool() const { + return HandleTraits::IsValid(Handle) ? true : false; + } + + operator handle_type() const { + return Handle; + } +}; + +struct CommonHandleTraits { + typedef HANDLE handle_type; + + static handle_type GetInvalid() { + return INVALID_HANDLE_VALUE; + } + + static void Close(handle_type h) { + ::CloseHandle(h); + } + + static bool IsValid(handle_type h) { + return h != GetInvalid(); + } +}; + +struct JobHandleTraits : CommonHandleTraits { + static handle_type GetInvalid() { + return NULL; + } +}; + +struct CryptContextTraits : CommonHandleTraits { + typedef HCRYPTPROV handle_type; + + static handle_type GetInvalid() { + return 0; + } + + static void Close(handle_type h) { + ::CryptReleaseContext(h, 0); + } + + static bool IsValid(handle_type h) { + return h != GetInvalid(); + } +}; + +struct RegTraits : CommonHandleTraits { + typedef HKEY handle_type; + + static handle_type GetInvalid() { + return NULL; + } + + static void Close(handle_type h) { + ::RegCloseKey(h); + } + + static bool IsValid(handle_type h) { + return h != GetInvalid(); + } +}; + +struct FindHandleTraits : CommonHandleTraits { + static void Close(handle_type h) { + ::FindClose(h); + } +}; + +struct FileHandleTraits : CommonHandleTraits {}; + +typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle; +typedef ScopedHandle<FileHandleTraits> ScopedFileHandle; +typedef ScopedHandle<CryptContextTraits> ScopedCryptContext; +typedef ScopedHandle<RegTraits> ScopedRegHandle; +typedef ScopedHandle<FindHandleTraits> ScopedFindHandle; +typedef ScopedHandle<JobHandleTraits> ScopedJobHandle; + +template <class T> +class SmallVectorImpl; + +template <class T> +typename SmallVectorImpl<T>::const_pointer +c_str(SmallVectorImpl<T> &str) { + str.push_back(0); + str.pop_back(); + return str.data(); +} + +namespace sys { + +inline std::chrono::nanoseconds toDuration(FILETIME Time) { + ULARGE_INTEGER TimeInteger; + TimeInteger.LowPart = Time.dwLowDateTime; + TimeInteger.HighPart = Time.dwHighDateTime; + + // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond) + return std::chrono::nanoseconds(100 * TimeInteger.QuadPart); +} + +inline TimePoint<> toTimePoint(FILETIME Time) { + ULARGE_INTEGER TimeInteger; + TimeInteger.LowPart = Time.dwLowDateTime; + TimeInteger.HighPart = Time.dwHighDateTime; + + // Adjust for different epoch + TimeInteger.QuadPart -= 11644473600ll * 10000000; + + // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond) + return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart)); +} + +inline FILETIME toFILETIME(TimePoint<> TP) { + ULARGE_INTEGER TimeInteger; + TimeInteger.QuadPart = TP.time_since_epoch().count() / 100; + TimeInteger.QuadPart += 11644473600ll * 10000000; + + FILETIME Time; + Time.dwLowDateTime = TimeInteger.LowPart; + Time.dwHighDateTime = TimeInteger.HighPart; + return Time; +} + +namespace windows { +// Returns command line arguments. Unlike arguments given to main(), +// this function guarantees that the returned arguments are encoded in +// UTF-8 regardless of the current code page setting. +std::error_code GetCommandLineArguments(SmallVectorImpl<const char *> &Args, + BumpPtrAllocator &Alloc); + +/// Convert UTF-8 path to a suitable UTF-16 path for use with the Win32 Unicode +/// File API. +std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16, + size_t MaxPathLen = MAX_PATH); + +} // end namespace windows +} // end namespace sys +} // end namespace llvm. + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/WindowsError.h b/contrib/libs/llvm16/include/llvm/Support/WindowsError.h new file mode 100644 index 00000000000..b128f46261e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/WindowsError.h @@ -0,0 +1,29 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- WindowsError.h - Support for mapping windows errors to posix-------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_WINDOWSERROR_H +#define LLVM_SUPPORT_WINDOWSERROR_H + +#include <system_error> + +namespace llvm { +std::error_code mapWindowsError(unsigned EV); +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/WithColor.h b/contrib/libs/llvm16/include/llvm/Support/WithColor.h new file mode 100644 index 00000000000..473303d95c5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/WithColor.h @@ -0,0 +1,162 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- WithColor.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_WITHCOLOR_H +#define LLVM_SUPPORT_WITHCOLOR_H + +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +class Error; +class StringRef; + +namespace cl { +class OptionCategory; +} + +extern cl::OptionCategory &getColorCategory(); + +// Symbolic names for various syntax elements. +enum class HighlightColor { + Address, + String, + Tag, + Attribute, + Enumerator, + Macro, + Error, + Warning, + Note, + Remark +}; + +enum class ColorMode { + /// Determine whether to use color based on the command line argument and the + /// raw_ostream. + Auto, + /// Enable colors. Because raw_ostream is the one implementing colors, this + /// has no effect if the stream does not support colors or has colors + /// disabled. + Enable, + /// Disable colors. + Disable, +}; + +/// An RAII object that temporarily switches an output stream to a specific +/// color. +class WithColor { +public: + using AutoDetectFunctionType = bool (*)(const raw_ostream &OS); + + /// To be used like this: WithColor(OS, HighlightColor::String) << "text"; + /// @param OS The output stream + /// @param S Symbolic name for syntax element to color + /// @param Mode Enable, disable or compute whether to use colors. + WithColor(raw_ostream &OS, HighlightColor S, + ColorMode Mode = ColorMode::Auto); + /// To be used like this: WithColor(OS, raw_ostream::Black) << "text"; + /// @param OS The output stream + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold Bold/brighter text, default false + /// @param BG If true, change the background, default: change foreground + /// @param Mode Enable, disable or compute whether to use colors. + WithColor(raw_ostream &OS, + raw_ostream::Colors Color = raw_ostream::SAVEDCOLOR, + bool Bold = false, bool BG = false, + ColorMode Mode = ColorMode::Auto) + : OS(OS), Mode(Mode) { + changeColor(Color, Bold, BG); + } + ~WithColor(); + + raw_ostream &get() { return OS; } + operator raw_ostream &() { return OS; } + template <typename T> WithColor &operator<<(T &O) { + OS << O; + return *this; + } + template <typename T> WithColor &operator<<(const T &O) { + OS << O; + return *this; + } + + /// Convenience method for printing "error: " to stderr. + static raw_ostream &error(); + /// Convenience method for printing "warning: " to stderr. + static raw_ostream &warning(); + /// Convenience method for printing "note: " to stderr. + static raw_ostream ¬e(); + /// Convenience method for printing "remark: " to stderr. + static raw_ostream &remark(); + + /// Convenience method for printing "error: " to the given stream. + static raw_ostream &error(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + /// Convenience method for printing "warning: " to the given stream. + static raw_ostream &warning(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + /// Convenience method for printing "note: " to the given stream. + static raw_ostream ¬e(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + /// Convenience method for printing "remark: " to the given stream. + static raw_ostream &remark(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + + /// Determine whether colors are displayed. + bool colorsEnabled(); + + /// Change the color of text that will be output from this point forward. + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold Bold/brighter text, default false + /// @param BG If true, change the background, default: change foreground + WithColor &changeColor(raw_ostream::Colors Color, bool Bold = false, + bool BG = false); + + /// Reset the colors to terminal defaults. Call this when you are done + /// outputting colored text, or before program exit. + WithColor &resetColor(); + + /// Implement default handling for Error. + /// Print "error: " to stderr. + static void defaultErrorHandler(Error Err); + + /// Implement default handling for Warning. + /// Print "warning: " to stderr. + static void defaultWarningHandler(Error Warning); + + /// Retrieve the default color auto detection function. + static AutoDetectFunctionType defaultAutoDetectFunction(); + + /// Change the global auto detection function. + static void + setAutoDetectFunction(AutoDetectFunctionType NewAutoDetectFunction); + +private: + raw_ostream &OS; + ColorMode Mode; + + static AutoDetectFunctionType AutoDetectFunction; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_WITHCOLOR_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/X86DisassemblerDecoderCommon.h b/contrib/libs/llvm16/include/llvm/Support/X86DisassemblerDecoderCommon.h new file mode 100644 index 00000000000..61724262f08 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/X86DisassemblerDecoderCommon.h @@ -0,0 +1,487 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- X86DisassemblerDecoderCommon.h - Disassembler decoder ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is part of the X86 Disassembler. +// It contains common definitions used by both the disassembler and the table +// generator. +// Documentation for the disassembler can be found in X86Disassembler.h. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_X86DISASSEMBLERDECODERCOMMON_H +#define LLVM_SUPPORT_X86DISASSEMBLERDECODERCOMMON_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { +namespace X86Disassembler { + +#define INSTRUCTIONS_SYM x86DisassemblerInstrSpecifiers +#define CONTEXTS_SYM x86DisassemblerContexts +#define ONEBYTE_SYM x86DisassemblerOneByteOpcodes +#define TWOBYTE_SYM x86DisassemblerTwoByteOpcodes +#define THREEBYTE38_SYM x86DisassemblerThreeByte38Opcodes +#define THREEBYTE3A_SYM x86DisassemblerThreeByte3AOpcodes +#define XOP8_MAP_SYM x86DisassemblerXOP8Opcodes +#define XOP9_MAP_SYM x86DisassemblerXOP9Opcodes +#define XOPA_MAP_SYM x86DisassemblerXOPAOpcodes +#define THREEDNOW_MAP_SYM x86Disassembler3DNowOpcodes +#define MAP5_SYM x86DisassemblerMap5Opcodes +#define MAP6_SYM x86DisassemblerMap6Opcodes + +#define INSTRUCTIONS_STR "x86DisassemblerInstrSpecifiers" +#define CONTEXTS_STR "x86DisassemblerContexts" +#define ONEBYTE_STR "x86DisassemblerOneByteOpcodes" +#define TWOBYTE_STR "x86DisassemblerTwoByteOpcodes" +#define THREEBYTE38_STR "x86DisassemblerThreeByte38Opcodes" +#define THREEBYTE3A_STR "x86DisassemblerThreeByte3AOpcodes" +#define XOP8_MAP_STR "x86DisassemblerXOP8Opcodes" +#define XOP9_MAP_STR "x86DisassemblerXOP9Opcodes" +#define XOPA_MAP_STR "x86DisassemblerXOPAOpcodes" +#define THREEDNOW_MAP_STR "x86Disassembler3DNowOpcodes" +#define MAP5_STR "x86DisassemblerMap5Opcodes" +#define MAP6_STR "x86DisassemblerMap6Opcodes" + +// Attributes of an instruction that must be known before the opcode can be +// processed correctly. Most of these indicate the presence of particular +// prefixes, but ATTR_64BIT is simply an attribute of the decoding context. +enum attributeBits { + ATTR_NONE = 0x00, + ATTR_64BIT = 0x1 << 0, + ATTR_XS = 0x1 << 1, + ATTR_XD = 0x1 << 2, + ATTR_REXW = 0x1 << 3, + ATTR_OPSIZE = 0x1 << 4, + ATTR_ADSIZE = 0x1 << 5, + ATTR_VEX = 0x1 << 6, + ATTR_VEXL = 0x1 << 7, + ATTR_EVEX = 0x1 << 8, + ATTR_EVEXL2 = 0x1 << 9, + ATTR_EVEXK = 0x1 << 10, + ATTR_EVEXKZ = 0x1 << 11, + ATTR_EVEXB = 0x1 << 12, + ATTR_max = 0x1 << 13, +}; + +// Combinations of the above attributes that are relevant to instruction +// decode. Although other combinations are possible, they can be reduced to +// these without affecting the ultimately decoded instruction. + +// Class name Rank Rationale for rank assignment +#define INSTRUCTION_CONTEXTS \ + ENUM_ENTRY(IC, 0, "says nothing about the instruction") \ + ENUM_ENTRY(IC_64BIT, 1, "says the instruction applies in " \ + "64-bit mode but no more") \ + ENUM_ENTRY(IC_OPSIZE, 3, "requires an OPSIZE prefix, so " \ + "operands change width") \ + ENUM_ENTRY(IC_ADSIZE, 3, "requires an ADSIZE prefix, so " \ + "operands change width") \ + ENUM_ENTRY(IC_OPSIZE_ADSIZE, 4, "requires ADSIZE and OPSIZE prefixes") \ + ENUM_ENTRY(IC_XD, 2, "may say something about the opcode " \ + "but not the operands") \ + ENUM_ENTRY(IC_XS, 2, "may say something about the opcode " \ + "but not the operands") \ + ENUM_ENTRY(IC_XD_OPSIZE, 3, "requires an OPSIZE prefix, so " \ + "operands change width") \ + ENUM_ENTRY(IC_XS_OPSIZE, 3, "requires an OPSIZE prefix, so " \ + "operands change width") \ + ENUM_ENTRY(IC_XD_ADSIZE, 3, "requires an ADSIZE prefix, so " \ + "operands change width") \ + ENUM_ENTRY(IC_XS_ADSIZE, 3, "requires an ADSIZE prefix, so " \ + "operands change width") \ + ENUM_ENTRY(IC_64BIT_REXW, 5, "requires a REX.W prefix, so operands "\ + "change width; overrides IC_OPSIZE") \ + ENUM_ENTRY(IC_64BIT_REXW_ADSIZE, 6, "requires a REX.W prefix and 0x67 " \ + "prefix") \ + ENUM_ENTRY(IC_64BIT_OPSIZE, 3, "Just as meaningful as IC_OPSIZE") \ + ENUM_ENTRY(IC_64BIT_ADSIZE, 3, "Just as meaningful as IC_ADSIZE") \ + ENUM_ENTRY(IC_64BIT_OPSIZE_ADSIZE, 4, "Just as meaningful as IC_OPSIZE/" \ + "IC_ADSIZE") \ + ENUM_ENTRY(IC_64BIT_XD, 6, "XD instructions are SSE; REX.W is " \ + "secondary") \ + ENUM_ENTRY(IC_64BIT_XS, 6, "Just as meaningful as IC_64BIT_XD") \ + ENUM_ENTRY(IC_64BIT_XD_OPSIZE, 3, "Just as meaningful as IC_XD_OPSIZE") \ + ENUM_ENTRY(IC_64BIT_XS_OPSIZE, 3, "Just as meaningful as IC_XS_OPSIZE") \ + ENUM_ENTRY(IC_64BIT_XD_ADSIZE, 3, "Just as meaningful as IC_XD_ADSIZE") \ + ENUM_ENTRY(IC_64BIT_XS_ADSIZE, 3, "Just as meaningful as IC_XS_ADSIZE") \ + ENUM_ENTRY(IC_64BIT_REXW_XS, 7, "OPSIZE could mean a different " \ + "opcode") \ + ENUM_ENTRY(IC_64BIT_REXW_XD, 7, "Just as meaningful as " \ + "IC_64BIT_REXW_XS") \ + ENUM_ENTRY(IC_64BIT_REXW_OPSIZE, 8, "The Dynamic Duo! Prefer over all " \ + "else because this changes most " \ + "operands' meaning") \ + ENUM_ENTRY(IC_VEX, 1, "requires a VEX prefix") \ + ENUM_ENTRY(IC_VEX_XS, 2, "requires VEX and the XS prefix") \ + ENUM_ENTRY(IC_VEX_XD, 2, "requires VEX and the XD prefix") \ + ENUM_ENTRY(IC_VEX_OPSIZE, 2, "requires VEX and the OpSize prefix") \ + ENUM_ENTRY(IC_VEX_W, 3, "requires VEX and the W prefix") \ + ENUM_ENTRY(IC_VEX_W_XS, 4, "requires VEX, W, and XS prefix") \ + ENUM_ENTRY(IC_VEX_W_XD, 4, "requires VEX, W, and XD prefix") \ + ENUM_ENTRY(IC_VEX_W_OPSIZE, 4, "requires VEX, W, and OpSize") \ + ENUM_ENTRY(IC_VEX_L, 3, "requires VEX and the L prefix") \ + ENUM_ENTRY(IC_VEX_L_XS, 4, "requires VEX and the L and XS prefix")\ + ENUM_ENTRY(IC_VEX_L_XD, 4, "requires VEX and the L and XD prefix")\ + ENUM_ENTRY(IC_VEX_L_OPSIZE, 4, "requires VEX, L, and OpSize") \ + ENUM_ENTRY(IC_VEX_L_W, 4, "requires VEX, L and W") \ + ENUM_ENTRY(IC_VEX_L_W_XS, 5, "requires VEX, L, W and XS prefix") \ + ENUM_ENTRY(IC_VEX_L_W_XD, 5, "requires VEX, L, W and XD prefix") \ + ENUM_ENTRY(IC_VEX_L_W_OPSIZE, 5, "requires VEX, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX, 1, "requires an EVEX prefix") \ + ENUM_ENTRY(IC_EVEX_XS, 2, "requires EVEX and the XS prefix") \ + ENUM_ENTRY(IC_EVEX_XD, 2, "requires EVEX and the XD prefix") \ + ENUM_ENTRY(IC_EVEX_OPSIZE, 2, "requires EVEX and the OpSize prefix") \ + ENUM_ENTRY(IC_EVEX_W, 3, "requires EVEX and the W prefix") \ + ENUM_ENTRY(IC_EVEX_W_XS, 4, "requires EVEX, W, and XS prefix") \ + ENUM_ENTRY(IC_EVEX_W_XD, 4, "requires EVEX, W, and XD prefix") \ + ENUM_ENTRY(IC_EVEX_W_OPSIZE, 4, "requires EVEX, W, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L, 3, "requires EVEX and the L prefix") \ + ENUM_ENTRY(IC_EVEX_L_XS, 4, "requires EVEX and the L and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L_XD, 4, "requires EVEX and the L and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L_OPSIZE, 4, "requires EVEX, L, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_W, 3, "requires EVEX, L and W") \ + ENUM_ENTRY(IC_EVEX_L_W_XS, 4, "requires EVEX, L, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_XD, 4, "requires EVEX, L, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_OPSIZE, 4, "requires EVEX, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2, 3, "requires EVEX and the L2 prefix") \ + ENUM_ENTRY(IC_EVEX_L2_XS, 4, "requires EVEX and the L2 and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L2_XD, 4, "requires EVEX and the L2 and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L2_OPSIZE, 4, "requires EVEX, L2, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_W, 3, "requires EVEX, L2 and W") \ + ENUM_ENTRY(IC_EVEX_L2_W_XS, 4, "requires EVEX, L2, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_XD, 4, "requires EVEX, L2, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE, 4, "requires EVEX, L2, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_K, 1, "requires an EVEX_K prefix") \ + ENUM_ENTRY(IC_EVEX_XS_K, 2, "requires EVEX_K and the XS prefix") \ + ENUM_ENTRY(IC_EVEX_XD_K, 2, "requires EVEX_K and the XD prefix") \ + ENUM_ENTRY(IC_EVEX_OPSIZE_K, 2, "requires EVEX_K and the OpSize prefix") \ + ENUM_ENTRY(IC_EVEX_W_K, 3, "requires EVEX_K and the W prefix") \ + ENUM_ENTRY(IC_EVEX_W_XS_K, 4, "requires EVEX_K, W, and XS prefix") \ + ENUM_ENTRY(IC_EVEX_W_XD_K, 4, "requires EVEX_K, W, and XD prefix") \ + ENUM_ENTRY(IC_EVEX_W_OPSIZE_K, 4, "requires EVEX_K, W, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_K, 3, "requires EVEX_K and the L prefix") \ + ENUM_ENTRY(IC_EVEX_L_XS_K, 4, "requires EVEX_K and the L and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L_XD_K, 4, "requires EVEX_K and the L and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L_OPSIZE_K, 4, "requires EVEX_K, L, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_W_K, 3, "requires EVEX_K, L and W") \ + ENUM_ENTRY(IC_EVEX_L_W_XS_K, 4, "requires EVEX_K, L, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_XD_K, 4, "requires EVEX_K, L, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_K, 4, "requires EVEX_K, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_K, 3, "requires EVEX_K and the L2 prefix") \ + ENUM_ENTRY(IC_EVEX_L2_XS_K, 4, "requires EVEX_K and the L2 and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L2_XD_K, 4, "requires EVEX_K and the L2 and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L2_OPSIZE_K, 4, "requires EVEX_K, L2, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_W_K, 3, "requires EVEX_K, L2 and W") \ + ENUM_ENTRY(IC_EVEX_L2_W_XS_K, 4, "requires EVEX_K, L2, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_XD_K, 4, "requires EVEX_K, L2, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_K, 4, "requires EVEX_K, L2, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_B, 1, "requires an EVEX_B prefix") \ + ENUM_ENTRY(IC_EVEX_XS_B, 2, "requires EVEX_B and the XS prefix") \ + ENUM_ENTRY(IC_EVEX_XD_B, 2, "requires EVEX_B and the XD prefix") \ + ENUM_ENTRY(IC_EVEX_OPSIZE_B, 2, "requires EVEX_B and the OpSize prefix") \ + ENUM_ENTRY(IC_EVEX_W_B, 3, "requires EVEX_B and the W prefix") \ + ENUM_ENTRY(IC_EVEX_W_XS_B, 4, "requires EVEX_B, W, and XS prefix") \ + ENUM_ENTRY(IC_EVEX_W_XD_B, 4, "requires EVEX_B, W, and XD prefix") \ + ENUM_ENTRY(IC_EVEX_W_OPSIZE_B, 4, "requires EVEX_B, W, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_B, 3, "requires EVEX_B and the L prefix") \ + ENUM_ENTRY(IC_EVEX_L_XS_B, 4, "requires EVEX_B and the L and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L_XD_B, 4, "requires EVEX_B and the L and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L_OPSIZE_B, 4, "requires EVEX_B, L, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_W_B, 3, "requires EVEX_B, L and W") \ + ENUM_ENTRY(IC_EVEX_L_W_XS_B, 4, "requires EVEX_B, L, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_XD_B, 4, "requires EVEX_B, L, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_B, 4, "requires EVEX_B, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_B, 3, "requires EVEX_B and the L2 prefix") \ + ENUM_ENTRY(IC_EVEX_L2_XS_B, 4, "requires EVEX_B and the L2 and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L2_XD_B, 4, "requires EVEX_B and the L2 and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L2_OPSIZE_B, 4, "requires EVEX_B, L2, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_W_B, 3, "requires EVEX_B, L2 and W") \ + ENUM_ENTRY(IC_EVEX_L2_W_XS_B, 4, "requires EVEX_B, L2, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_XD_B, 4, "requires EVEX_B, L2, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_B, 4, "requires EVEX_B, L2, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_K_B, 1, "requires EVEX_B and EVEX_K prefix") \ + ENUM_ENTRY(IC_EVEX_XS_K_B, 2, "requires EVEX_B, EVEX_K and the XS prefix") \ + ENUM_ENTRY(IC_EVEX_XD_K_B, 2, "requires EVEX_B, EVEX_K and the XD prefix") \ + ENUM_ENTRY(IC_EVEX_OPSIZE_K_B, 2, "requires EVEX_B, EVEX_K and the OpSize prefix") \ + ENUM_ENTRY(IC_EVEX_W_K_B, 3, "requires EVEX_B, EVEX_K and the W prefix") \ + ENUM_ENTRY(IC_EVEX_W_XS_K_B, 4, "requires EVEX_B, EVEX_K, W, and XS prefix") \ + ENUM_ENTRY(IC_EVEX_W_XD_K_B, 4, "requires EVEX_B, EVEX_K, W, and XD prefix") \ + ENUM_ENTRY(IC_EVEX_W_OPSIZE_K_B, 4, "requires EVEX_B, EVEX_K, W, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_K_B, 3, "requires EVEX_B, EVEX_K and the L prefix") \ + ENUM_ENTRY(IC_EVEX_L_XS_K_B, 4, "requires EVEX_B, EVEX_K and the L and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L_XD_K_B, 4, "requires EVEX_B, EVEX_K and the L and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L_OPSIZE_K_B, 4, "requires EVEX_B, EVEX_K, L, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_W_K_B, 3, "requires EVEX_B, EVEX_K, L and W") \ + ENUM_ENTRY(IC_EVEX_L_W_XS_K_B, 4, "requires EVEX_B, EVEX_K, L, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_XD_K_B, 4, "requires EVEX_B, EVEX_K, L, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_K_B,4, "requires EVEX_B, EVEX_K, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_K_B, 3, "requires EVEX_B, EVEX_K and the L2 prefix") \ + ENUM_ENTRY(IC_EVEX_L2_XS_K_B, 4, "requires EVEX_B, EVEX_K and the L2 and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L2_XD_K_B, 4, "requires EVEX_B, EVEX_K and the L2 and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L2_OPSIZE_K_B, 4, "requires EVEX_B, EVEX_K, L2, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_W_K_B, 3, "requires EVEX_B, EVEX_K, L2 and W") \ + ENUM_ENTRY(IC_EVEX_L2_W_XS_K_B, 4, "requires EVEX_B, EVEX_K, L2, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_XD_K_B, 4, "requires EVEX_B, EVEX_K, L2, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_K_B,4, "requires EVEX_B, EVEX_K, L2, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_KZ_B, 1, "requires EVEX_B and EVEX_KZ prefix") \ + ENUM_ENTRY(IC_EVEX_XS_KZ_B, 2, "requires EVEX_B, EVEX_KZ and the XS prefix") \ + ENUM_ENTRY(IC_EVEX_XD_KZ_B, 2, "requires EVEX_B, EVEX_KZ and the XD prefix") \ + ENUM_ENTRY(IC_EVEX_OPSIZE_KZ_B, 2, "requires EVEX_B, EVEX_KZ and the OpSize prefix") \ + ENUM_ENTRY(IC_EVEX_W_KZ_B, 3, "requires EVEX_B, EVEX_KZ and the W prefix") \ + ENUM_ENTRY(IC_EVEX_W_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ, W, and XS prefix") \ + ENUM_ENTRY(IC_EVEX_W_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ, W, and XD prefix") \ + ENUM_ENTRY(IC_EVEX_W_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, W, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_KZ_B, 3, "requires EVEX_B, EVEX_KZ and the L prefix") \ + ENUM_ENTRY(IC_EVEX_L_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_W_KZ_B, 3, "requires EVEX_B, EVEX_KZ, L and W") \ + ENUM_ENTRY(IC_EVEX_L_W_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_KZ_B, 3, "requires EVEX_B, EVEX_KZ and the L2 prefix") \ + ENUM_ENTRY(IC_EVEX_L2_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L2 and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L2_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ and the L2 and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L2_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_W_KZ_B, 3, "requires EVEX_B, EVEX_KZ, L2 and W") \ + ENUM_ENTRY(IC_EVEX_L2_W_XS_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_XD_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_KZ_B, 4, "requires EVEX_B, EVEX_KZ, L2, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_KZ, 1, "requires an EVEX_KZ prefix") \ + ENUM_ENTRY(IC_EVEX_XS_KZ, 2, "requires EVEX_KZ and the XS prefix") \ + ENUM_ENTRY(IC_EVEX_XD_KZ, 2, "requires EVEX_KZ and the XD prefix") \ + ENUM_ENTRY(IC_EVEX_OPSIZE_KZ, 2, "requires EVEX_KZ and the OpSize prefix") \ + ENUM_ENTRY(IC_EVEX_W_KZ, 3, "requires EVEX_KZ and the W prefix") \ + ENUM_ENTRY(IC_EVEX_W_XS_KZ, 4, "requires EVEX_KZ, W, and XS prefix") \ + ENUM_ENTRY(IC_EVEX_W_XD_KZ, 4, "requires EVEX_KZ, W, and XD prefix") \ + ENUM_ENTRY(IC_EVEX_W_OPSIZE_KZ, 4, "requires EVEX_KZ, W, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_KZ, 3, "requires EVEX_KZ and the L prefix") \ + ENUM_ENTRY(IC_EVEX_L_XS_KZ, 4, "requires EVEX_KZ and the L and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L_XD_KZ, 4, "requires EVEX_KZ and the L and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L_OPSIZE_KZ, 4, "requires EVEX_KZ, L, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L_W_KZ, 3, "requires EVEX_KZ, L and W") \ + ENUM_ENTRY(IC_EVEX_L_W_XS_KZ, 4, "requires EVEX_KZ, L, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_XD_KZ, 4, "requires EVEX_KZ, L, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L_W_OPSIZE_KZ, 4, "requires EVEX_KZ, L, W and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_KZ, 3, "requires EVEX_KZ and the L2 prefix") \ + ENUM_ENTRY(IC_EVEX_L2_XS_KZ, 4, "requires EVEX_KZ and the L2 and XS prefix")\ + ENUM_ENTRY(IC_EVEX_L2_XD_KZ, 4, "requires EVEX_KZ and the L2 and XD prefix")\ + ENUM_ENTRY(IC_EVEX_L2_OPSIZE_KZ, 4, "requires EVEX_KZ, L2, and OpSize") \ + ENUM_ENTRY(IC_EVEX_L2_W_KZ, 3, "requires EVEX_KZ, L2 and W") \ + ENUM_ENTRY(IC_EVEX_L2_W_XS_KZ, 4, "requires EVEX_KZ, L2, W and XS prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_XD_KZ, 4, "requires EVEX_KZ, L2, W and XD prefix") \ + ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_KZ, 4, "requires EVEX_KZ, L2, W and OpSize") + +#define ENUM_ENTRY(n, r, d) n, +enum InstructionContext { + INSTRUCTION_CONTEXTS + IC_max +}; +#undef ENUM_ENTRY + +// Opcode types, which determine which decode table to use, both in the Intel +// manual and also for the decoder. +enum OpcodeType { + ONEBYTE = 0, + TWOBYTE = 1, + THREEBYTE_38 = 2, + THREEBYTE_3A = 3, + XOP8_MAP = 4, + XOP9_MAP = 5, + XOPA_MAP = 6, + THREEDNOW_MAP = 7, + MAP5 = 8, + MAP6 = 9 +}; + +// The following structs are used for the hierarchical decode table. After +// determining the instruction's class (i.e., which IC_* constant applies to +// it), the decoder reads the opcode. Some instructions require specific +// values of the ModR/M byte, so the ModR/M byte indexes into the final table. +// +// If a ModR/M byte is not required, "required" is left unset, and the values +// for each instructionID are identical. +typedef uint16_t InstrUID; + +// ModRMDecisionType - describes the type of ModR/M decision, allowing the +// consumer to determine the number of entries in it. +// +// MODRM_ONEENTRY - No matter what the value of the ModR/M byte is, the decoded +// instruction is the same. +// MODRM_SPLITRM - If the ModR/M byte is between 0x00 and 0xbf, the opcode +// corresponds to one instruction; otherwise, it corresponds to +// a different instruction. +// MODRM_SPLITMISC- If the ModR/M byte is between 0x00 and 0xbf, ModR/M byte +// divided by 8 is used to select instruction; otherwise, each +// value of the ModR/M byte could correspond to a different +// instruction. +// MODRM_SPLITREG - ModR/M byte divided by 8 is used to select instruction. This +// corresponds to instructions that use reg field as opcode +// MODRM_FULL - Potentially, each value of the ModR/M byte could correspond +// to a different instruction. +#define MODRMTYPES \ + ENUM_ENTRY(MODRM_ONEENTRY) \ + ENUM_ENTRY(MODRM_SPLITRM) \ + ENUM_ENTRY(MODRM_SPLITMISC) \ + ENUM_ENTRY(MODRM_SPLITREG) \ + ENUM_ENTRY(MODRM_FULL) + +#define ENUM_ENTRY(n) n, +enum ModRMDecisionType { + MODRMTYPES + MODRM_max +}; +#undef ENUM_ENTRY + +#define CASE_ENCODING_RM \ + case ENCODING_RM: \ + case ENCODING_RM_CD2: \ + case ENCODING_RM_CD4: \ + case ENCODING_RM_CD8: \ + case ENCODING_RM_CD16: \ + case ENCODING_RM_CD32: \ + case ENCODING_RM_CD64 + +#define CASE_ENCODING_VSIB \ + case ENCODING_VSIB: \ + case ENCODING_VSIB_CD2: \ + case ENCODING_VSIB_CD4: \ + case ENCODING_VSIB_CD8: \ + case ENCODING_VSIB_CD16: \ + case ENCODING_VSIB_CD32: \ + case ENCODING_VSIB_CD64 + +// Physical encodings of instruction operands. +#define ENCODINGS \ + ENUM_ENTRY(ENCODING_NONE, "") \ + ENUM_ENTRY(ENCODING_REG, "Register operand in ModR/M byte.") \ + ENUM_ENTRY(ENCODING_RM, "R/M operand in ModR/M byte.") \ + ENUM_ENTRY(ENCODING_RM_CD2, "R/M operand with CDisp scaling of 2") \ + ENUM_ENTRY(ENCODING_RM_CD4, "R/M operand with CDisp scaling of 4") \ + ENUM_ENTRY(ENCODING_RM_CD8, "R/M operand with CDisp scaling of 8") \ + ENUM_ENTRY(ENCODING_RM_CD16,"R/M operand with CDisp scaling of 16") \ + ENUM_ENTRY(ENCODING_RM_CD32,"R/M operand with CDisp scaling of 32") \ + ENUM_ENTRY(ENCODING_RM_CD64,"R/M operand with CDisp scaling of 64") \ + ENUM_ENTRY(ENCODING_SIB, "Force SIB operand in ModR/M byte.") \ + ENUM_ENTRY(ENCODING_VSIB, "VSIB operand in ModR/M byte.") \ + ENUM_ENTRY(ENCODING_VSIB_CD2, "VSIB operand with CDisp scaling of 2") \ + ENUM_ENTRY(ENCODING_VSIB_CD4, "VSIB operand with CDisp scaling of 4") \ + ENUM_ENTRY(ENCODING_VSIB_CD8, "VSIB operand with CDisp scaling of 8") \ + ENUM_ENTRY(ENCODING_VSIB_CD16,"VSIB operand with CDisp scaling of 16") \ + ENUM_ENTRY(ENCODING_VSIB_CD32,"VSIB operand with CDisp scaling of 32") \ + ENUM_ENTRY(ENCODING_VSIB_CD64,"VSIB operand with CDisp scaling of 64") \ + ENUM_ENTRY(ENCODING_VVVV, "Register operand in VEX.vvvv byte.") \ + ENUM_ENTRY(ENCODING_WRITEMASK, "Register operand in EVEX.aaa byte.") \ + ENUM_ENTRY(ENCODING_IB, "1-byte immediate") \ + ENUM_ENTRY(ENCODING_IW, "2-byte") \ + ENUM_ENTRY(ENCODING_ID, "4-byte") \ + ENUM_ENTRY(ENCODING_IO, "8-byte") \ + ENUM_ENTRY(ENCODING_RB, "(AL..DIL, R8B..R15B) Register code added to " \ + "the opcode byte") \ + ENUM_ENTRY(ENCODING_RW, "(AX..DI, R8W..R15W)") \ + ENUM_ENTRY(ENCODING_RD, "(EAX..EDI, R8D..R15D)") \ + ENUM_ENTRY(ENCODING_RO, "(RAX..RDI, R8..R15)") \ + ENUM_ENTRY(ENCODING_FP, "Position on floating-point stack in ModR/M " \ + "byte.") \ + \ + ENUM_ENTRY(ENCODING_Iv, "Immediate of operand size") \ + ENUM_ENTRY(ENCODING_Ia, "Immediate of address size") \ + ENUM_ENTRY(ENCODING_IRC, "Immediate for static rounding control") \ + ENUM_ENTRY(ENCODING_Rv, "Register code of operand size added to the " \ + "opcode byte") \ + ENUM_ENTRY(ENCODING_CC, "Condition code encoded in opcode") \ + ENUM_ENTRY(ENCODING_DUP, "Duplicate of another operand; ID is encoded " \ + "in type") \ + ENUM_ENTRY(ENCODING_SI, "Source index; encoded in OpSize/Adsize prefix") \ + ENUM_ENTRY(ENCODING_DI, "Destination index; encoded in prefixes") + +#define ENUM_ENTRY(n, d) n, +enum OperandEncoding { + ENCODINGS + ENCODING_max +}; +#undef ENUM_ENTRY + +// Semantic interpretations of instruction operands. +#define TYPES \ + ENUM_ENTRY(TYPE_NONE, "") \ + ENUM_ENTRY(TYPE_REL, "immediate address") \ + ENUM_ENTRY(TYPE_R8, "1-byte register operand") \ + ENUM_ENTRY(TYPE_R16, "2-byte") \ + ENUM_ENTRY(TYPE_R32, "4-byte") \ + ENUM_ENTRY(TYPE_R64, "8-byte") \ + ENUM_ENTRY(TYPE_IMM, "immediate operand") \ + ENUM_ENTRY(TYPE_UIMM8, "1-byte unsigned immediate operand") \ + ENUM_ENTRY(TYPE_M, "Memory operand") \ + ENUM_ENTRY(TYPE_MSIB, "Memory operand force sib encoding") \ + ENUM_ENTRY(TYPE_MVSIBX, "Memory operand using XMM index") \ + ENUM_ENTRY(TYPE_MVSIBY, "Memory operand using YMM index") \ + ENUM_ENTRY(TYPE_MVSIBZ, "Memory operand using ZMM index") \ + ENUM_ENTRY(TYPE_SRCIDX, "memory at source index") \ + ENUM_ENTRY(TYPE_DSTIDX, "memory at destination index") \ + ENUM_ENTRY(TYPE_MOFFS, "memory offset (relative to segment base)") \ + ENUM_ENTRY(TYPE_ST, "Position on the floating-point stack") \ + ENUM_ENTRY(TYPE_MM64, "8-byte MMX register") \ + ENUM_ENTRY(TYPE_XMM, "16-byte") \ + ENUM_ENTRY(TYPE_YMM, "32-byte") \ + ENUM_ENTRY(TYPE_ZMM, "64-byte") \ + ENUM_ENTRY(TYPE_VK, "mask register") \ + ENUM_ENTRY(TYPE_VK_PAIR, "mask register pair") \ + ENUM_ENTRY(TYPE_TMM, "tile") \ + ENUM_ENTRY(TYPE_SEGMENTREG, "Segment register operand") \ + ENUM_ENTRY(TYPE_DEBUGREG, "Debug register operand") \ + ENUM_ENTRY(TYPE_CONTROLREG, "Control register operand") \ + ENUM_ENTRY(TYPE_BNDR, "MPX bounds register") \ + \ + ENUM_ENTRY(TYPE_Rv, "Register operand of operand size") \ + ENUM_ENTRY(TYPE_RELv, "Immediate address of operand size") \ + ENUM_ENTRY(TYPE_DUP0, "Duplicate of operand 0") \ + ENUM_ENTRY(TYPE_DUP1, "operand 1") \ + ENUM_ENTRY(TYPE_DUP2, "operand 2") \ + ENUM_ENTRY(TYPE_DUP3, "operand 3") \ + ENUM_ENTRY(TYPE_DUP4, "operand 4") \ + +#define ENUM_ENTRY(n, d) n, +enum OperandType { + TYPES + TYPE_max +}; +#undef ENUM_ENTRY + +/// The specification for how to extract and interpret one operand. +struct OperandSpecifier { + uint8_t encoding; + uint8_t type; +}; + +static const unsigned X86_MAX_OPERANDS = 6; + +/// Decoding mode for the Intel disassembler. 16-bit, 32-bit, and 64-bit mode +/// are supported, and represent real mode, IA-32e, and IA-32e in 64-bit mode, +/// respectively. +enum DisassemblerMode { + MODE_16BIT, + MODE_32BIT, + MODE_64BIT +}; + +} // namespace X86Disassembler +} // namespace llvm + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/X86TargetParser.h b/contrib/libs/llvm16/include/llvm/Support/X86TargetParser.h new file mode 100644 index 00000000000..9901d0d5c20 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/X86TargetParser.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/X86TargetParser.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This header is deprecated in favour of +/// `llvm/TargetParser/X86TargetParser.h`. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/X86TargetParser.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/YAMLParser.h b/contrib/libs/llvm16/include/llvm/Support/YAMLParser.h new file mode 100644 index 00000000000..5c505fa07de --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/YAMLParser.h @@ -0,0 +1,639 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- YAMLParser.h - Simple YAML parser ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a YAML 1.2 parser. +// +// See http://www.yaml.org/spec/1.2/spec.html for the full standard. +// +// This currently does not implement the following: +// * Tag resolution. +// * UTF-16. +// * BOMs anywhere other than the first Unicode scalar value in the file. +// +// The most important class here is Stream. This represents a YAML stream with +// 0, 1, or many documents. +// +// SourceMgr sm; +// StringRef input = getInput(); +// yaml::Stream stream(input, sm); +// +// for (yaml::document_iterator di = stream.begin(), de = stream.end(); +// di != de; ++di) { +// yaml::Node *n = di->getRoot(); +// if (n) { +// // Do something with n... +// } else +// break; +// } +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_YAMLPARSER_H +#define LLVM_SUPPORT_YAMLPARSER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" +#include <cassert> +#include <cstddef> +#include <iterator> +#include <map> +#include <memory> +#include <optional> +#include <string> +#include <system_error> + +namespace llvm { + +class MemoryBufferRef; +class raw_ostream; +class Twine; + +namespace yaml { + +class Document; +class document_iterator; +class Node; +class Scanner; +struct Token; + +/// Dump all the tokens in this stream to OS. +/// \returns true if there was an error, false otherwise. +bool dumpTokens(StringRef Input, raw_ostream &); + +/// Scans all tokens in input without outputting anything. This is used +/// for benchmarking the tokenizer. +/// \returns true if there was an error, false otherwise. +bool scanTokens(StringRef Input); + +/// Escape \a Input for a double quoted scalar; if \p EscapePrintable +/// is true, all UTF8 sequences will be escaped, if \p EscapePrintable is +/// false, those UTF8 sequences encoding printable unicode scalars will not be +/// escaped, but emitted verbatim. +std::string escape(StringRef Input, bool EscapePrintable = true); + +/// Parse \p S as a bool according to https://yaml.org/type/bool.html. +std::optional<bool> parseBool(StringRef S); + +/// This class represents a YAML stream potentially containing multiple +/// documents. +class Stream { +public: + /// This keeps a reference to the string referenced by \p Input. + Stream(StringRef Input, SourceMgr &, bool ShowColors = true, + std::error_code *EC = nullptr); + + Stream(MemoryBufferRef InputBuffer, SourceMgr &, bool ShowColors = true, + std::error_code *EC = nullptr); + ~Stream(); + + document_iterator begin(); + document_iterator end(); + void skip(); + bool failed(); + + bool validate() { + skip(); + return !failed(); + } + + void printError(Node *N, const Twine &Msg, + SourceMgr::DiagKind Kind = SourceMgr::DK_Error); + void printError(const SMRange &Range, const Twine &Msg, + SourceMgr::DiagKind Kind = SourceMgr::DK_Error); + +private: + friend class Document; + + std::unique_ptr<Scanner> scanner; + std::unique_ptr<Document> CurrentDoc; +}; + +/// Abstract base class for all Nodes. +class Node { + virtual void anchor(); + +public: + enum NodeKind { + NK_Null, + NK_Scalar, + NK_BlockScalar, + NK_KeyValue, + NK_Mapping, + NK_Sequence, + NK_Alias + }; + + Node(unsigned int Type, std::unique_ptr<Document> &, StringRef Anchor, + StringRef Tag); + + // It's not safe to copy YAML nodes; the document is streamed and the position + // is part of the state. + Node(const Node &) = delete; + void operator=(const Node &) = delete; + + void *operator new(size_t Size, BumpPtrAllocator &Alloc, + size_t Alignment = 16) noexcept { + return Alloc.Allocate(Size, Alignment); + } + + void operator delete(void *Ptr, BumpPtrAllocator &Alloc, + size_t Size) noexcept { + Alloc.Deallocate(Ptr, Size, 0); + } + + void operator delete(void *) noexcept = delete; + + /// Get the value of the anchor attached to this node. If it does not + /// have one, getAnchor().size() will be 0. + StringRef getAnchor() const { return Anchor; } + + /// Get the tag as it was written in the document. This does not + /// perform tag resolution. + StringRef getRawTag() const { return Tag; } + + /// Get the verbatium tag for a given Node. This performs tag resoluton + /// and substitution. + std::string getVerbatimTag() const; + + SMRange getSourceRange() const { return SourceRange; } + void setSourceRange(SMRange SR) { SourceRange = SR; } + + // These functions forward to Document and Scanner. + Token &peekNext(); + Token getNext(); + Node *parseBlockNode(); + BumpPtrAllocator &getAllocator(); + void setError(const Twine &Message, Token &Location) const; + bool failed() const; + + virtual void skip() {} + + unsigned int getType() const { return TypeID; } + +protected: + std::unique_ptr<Document> &Doc; + SMRange SourceRange; + + ~Node() = default; + +private: + unsigned int TypeID; + StringRef Anchor; + /// The tag as typed in the document. + StringRef Tag; +}; + +/// A null value. +/// +/// Example: +/// !!null null +class NullNode final : public Node { + void anchor() override; + +public: + NullNode(std::unique_ptr<Document> &D) + : Node(NK_Null, D, StringRef(), StringRef()) {} + + static bool classof(const Node *N) { return N->getType() == NK_Null; } +}; + +/// A scalar node is an opaque datum that can be presented as a +/// series of zero or more Unicode scalar values. +/// +/// Example: +/// Adena +class ScalarNode final : public Node { + void anchor() override; + +public: + ScalarNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, + StringRef Val) + : Node(NK_Scalar, D, Anchor, Tag), Value(Val) { + SMLoc Start = SMLoc::getFromPointer(Val.begin()); + SMLoc End = SMLoc::getFromPointer(Val.end()); + SourceRange = SMRange(Start, End); + } + + // Return Value without any escaping or folding or other fun YAML stuff. This + // is the exact bytes that are contained in the file (after conversion to + // utf8). + StringRef getRawValue() const { return Value; } + + /// Gets the value of this node as a StringRef. + /// + /// \param Storage is used to store the content of the returned StringRef if + /// it requires any modification from how it appeared in the source. + /// This happens with escaped characters and multi-line literals. + StringRef getValue(SmallVectorImpl<char> &Storage) const; + + static bool classof(const Node *N) { + return N->getType() == NK_Scalar; + } + +private: + StringRef Value; + + StringRef unescapeDoubleQuoted(StringRef UnquotedValue, + StringRef::size_type Start, + SmallVectorImpl<char> &Storage) const; +}; + +/// A block scalar node is an opaque datum that can be presented as a +/// series of zero or more Unicode scalar values. +/// +/// Example: +/// | +/// Hello +/// World +class BlockScalarNode final : public Node { + void anchor() override; + +public: + BlockScalarNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, + StringRef Value, StringRef RawVal) + : Node(NK_BlockScalar, D, Anchor, Tag), Value(Value) { + SMLoc Start = SMLoc::getFromPointer(RawVal.begin()); + SMLoc End = SMLoc::getFromPointer(RawVal.end()); + SourceRange = SMRange(Start, End); + } + + /// Gets the value of this node as a StringRef. + StringRef getValue() const { return Value; } + + static bool classof(const Node *N) { + return N->getType() == NK_BlockScalar; + } + +private: + StringRef Value; +}; + +/// A key and value pair. While not technically a Node under the YAML +/// representation graph, it is easier to treat them this way. +/// +/// TODO: Consider making this not a child of Node. +/// +/// Example: +/// Section: .text +class KeyValueNode final : public Node { + void anchor() override; + +public: + KeyValueNode(std::unique_ptr<Document> &D) + : Node(NK_KeyValue, D, StringRef(), StringRef()) {} + + /// Parse and return the key. + /// + /// This may be called multiple times. + /// + /// \returns The key, or nullptr if failed() == true. + Node *getKey(); + + /// Parse and return the value. + /// + /// This may be called multiple times. + /// + /// \returns The value, or nullptr if failed() == true. + Node *getValue(); + + void skip() override { + if (Node *Key = getKey()) { + Key->skip(); + if (Node *Val = getValue()) + Val->skip(); + } + } + + static bool classof(const Node *N) { + return N->getType() == NK_KeyValue; + } + +private: + Node *Key = nullptr; + Node *Value = nullptr; +}; + +/// This is an iterator abstraction over YAML collections shared by both +/// sequences and maps. +/// +/// BaseT must have a ValueT* member named CurrentEntry and a member function +/// increment() which must set CurrentEntry to 0 to create an end iterator. +template <class BaseT, class ValueT> class basic_collection_iterator { +public: + using iterator_category = std::input_iterator_tag; + using value_type = ValueT; + using difference_type = std::ptrdiff_t; + using pointer = value_type *; + using reference = value_type &; + + basic_collection_iterator() = default; + basic_collection_iterator(BaseT *B) : Base(B) {} + + ValueT *operator->() const { + assert(Base && Base->CurrentEntry && "Attempted to access end iterator!"); + return Base->CurrentEntry; + } + + ValueT &operator*() const { + assert(Base && Base->CurrentEntry && + "Attempted to dereference end iterator!"); + return *Base->CurrentEntry; + } + + operator ValueT *() const { + assert(Base && Base->CurrentEntry && "Attempted to access end iterator!"); + return Base->CurrentEntry; + } + + /// Note on EqualityComparable: + /// + /// The iterator is not re-entrant, + /// it is meant to be used for parsing YAML on-demand + /// Once iteration started - it can point only to one entry at a time + /// hence Base.CurrentEntry and Other.Base.CurrentEntry are equal + /// iff Base and Other.Base are equal. + bool operator==(const basic_collection_iterator &Other) const { + if (Base && (Base == Other.Base)) { + assert((Base->CurrentEntry == Other.Base->CurrentEntry) + && "Equal Bases expected to point to equal Entries"); + } + + return Base == Other.Base; + } + + bool operator!=(const basic_collection_iterator &Other) const { + return !(Base == Other.Base); + } + + basic_collection_iterator &operator++() { + assert(Base && "Attempted to advance iterator past end!"); + Base->increment(); + // Create an end iterator. + if (!Base->CurrentEntry) + Base = nullptr; + return *this; + } + +private: + BaseT *Base = nullptr; +}; + +// The following two templates are used for both MappingNode and Sequence Node. +template <class CollectionType> +typename CollectionType::iterator begin(CollectionType &C) { + assert(C.IsAtBeginning && "You may only iterate over a collection once!"); + C.IsAtBeginning = false; + typename CollectionType::iterator ret(&C); + ++ret; + return ret; +} + +template <class CollectionType> void skip(CollectionType &C) { + // TODO: support skipping from the middle of a parsed collection ;/ + assert((C.IsAtBeginning || C.IsAtEnd) && "Cannot skip mid parse!"); + if (C.IsAtBeginning) + for (typename CollectionType::iterator i = begin(C), e = C.end(); i != e; + ++i) + i->skip(); +} + +/// Represents a YAML map created from either a block map for a flow map. +/// +/// This parses the YAML stream as increment() is called. +/// +/// Example: +/// Name: _main +/// Scope: Global +class MappingNode final : public Node { + void anchor() override; + +public: + enum MappingType { + MT_Block, + MT_Flow, + MT_Inline ///< An inline mapping node is used for "[key: value]". + }; + + MappingNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, + MappingType MT) + : Node(NK_Mapping, D, Anchor, Tag), Type(MT) {} + + friend class basic_collection_iterator<MappingNode, KeyValueNode>; + + using iterator = basic_collection_iterator<MappingNode, KeyValueNode>; + + template <class T> friend typename T::iterator yaml::begin(T &); + template <class T> friend void yaml::skip(T &); + + iterator begin() { return yaml::begin(*this); } + + iterator end() { return iterator(); } + + void skip() override { yaml::skip(*this); } + + static bool classof(const Node *N) { + return N->getType() == NK_Mapping; + } + +private: + MappingType Type; + bool IsAtBeginning = true; + bool IsAtEnd = false; + KeyValueNode *CurrentEntry = nullptr; + + void increment(); +}; + +/// Represents a YAML sequence created from either a block sequence for a +/// flow sequence. +/// +/// This parses the YAML stream as increment() is called. +/// +/// Example: +/// - Hello +/// - World +class SequenceNode final : public Node { + void anchor() override; + +public: + enum SequenceType { + ST_Block, + ST_Flow, + // Use for: + // + // key: + // - val1 + // - val2 + // + // As a BlockMappingEntry and BlockEnd are not created in this case. + ST_Indentless + }; + + SequenceNode(std::unique_ptr<Document> &D, StringRef Anchor, StringRef Tag, + SequenceType ST) + : Node(NK_Sequence, D, Anchor, Tag), SeqType(ST) {} + + friend class basic_collection_iterator<SequenceNode, Node>; + + using iterator = basic_collection_iterator<SequenceNode, Node>; + + template <class T> friend typename T::iterator yaml::begin(T &); + template <class T> friend void yaml::skip(T &); + + void increment(); + + iterator begin() { return yaml::begin(*this); } + + iterator end() { return iterator(); } + + void skip() override { yaml::skip(*this); } + + static bool classof(const Node *N) { + return N->getType() == NK_Sequence; + } + +private: + SequenceType SeqType; + bool IsAtBeginning = true; + bool IsAtEnd = false; + bool WasPreviousTokenFlowEntry = true; // Start with an imaginary ','. + Node *CurrentEntry = nullptr; +}; + +/// Represents an alias to a Node with an anchor. +/// +/// Example: +/// *AnchorName +class AliasNode final : public Node { + void anchor() override; + +public: + AliasNode(std::unique_ptr<Document> &D, StringRef Val) + : Node(NK_Alias, D, StringRef(), StringRef()), Name(Val) {} + + StringRef getName() const { return Name; } + + static bool classof(const Node *N) { return N->getType() == NK_Alias; } + +private: + StringRef Name; +}; + +/// A YAML Stream is a sequence of Documents. A document contains a root +/// node. +class Document { +public: + Document(Stream &ParentStream); + + /// Root for parsing a node. Returns a single node. + Node *parseBlockNode(); + + /// Finish parsing the current document and return true if there are + /// more. Return false otherwise. + bool skip(); + + /// Parse and return the root level node. + Node *getRoot() { + if (Root) + return Root; + return Root = parseBlockNode(); + } + + const std::map<StringRef, StringRef> &getTagMap() const { return TagMap; } + +private: + friend class Node; + friend class document_iterator; + + /// Stream to read tokens from. + Stream &stream; + + /// Used to allocate nodes to. All are destroyed without calling their + /// destructor when the document is destroyed. + BumpPtrAllocator NodeAllocator; + + /// The root node. Used to support skipping a partially parsed + /// document. + Node *Root; + + /// Maps tag prefixes to their expansion. + std::map<StringRef, StringRef> TagMap; + + Token &peekNext(); + Token getNext(); + void setError(const Twine &Message, Token &Location) const; + bool failed() const; + + /// Parse %BLAH directives and return true if any were encountered. + bool parseDirectives(); + + /// Parse %YAML + void parseYAMLDirective(); + + /// Parse %TAG + void parseTAGDirective(); + + /// Consume the next token and error if it is not \a TK. + bool expectToken(int TK); +}; + +/// Iterator abstraction for Documents over a Stream. +class document_iterator { +public: + document_iterator() = default; + document_iterator(std::unique_ptr<Document> &D) : Doc(&D) {} + + 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) const { + return !(*this == Other); + } + + document_iterator operator++() { + assert(Doc && "incrementing iterator past the end."); + if (!(*Doc)->skip()) { + Doc->reset(nullptr); + } else { + Stream &S = (*Doc)->stream; + Doc->reset(new Document(S)); + } + return *this; + } + + Document &operator*() { return **Doc; } + + std::unique_ptr<Document> &operator->() { return *Doc; } + +private: + bool isAtEnd() const { return !Doc || !*Doc; } + + std::unique_ptr<Document> *Doc = nullptr; +}; + +} // end namespace yaml + +} // end namespace llvm + +#endif // LLVM_SUPPORT_YAMLPARSER_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/YAMLTraits.h b/contrib/libs/llvm16/include/llvm/Support/YAMLTraits.h new file mode 100644 index 00000000000..80cecb20573 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/YAMLTraits.h @@ -0,0 +1,2149 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/YAMLTraits.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_YAMLTRAITS_H +#define LLVM_SUPPORT_YAMLTRAITS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.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" +#include "llvm/Support/AlignOf.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <map> +#include <memory> +#include <new> +#include <optional> +#include <string> +#include <system_error> +#include <type_traits> +#include <vector> + +namespace llvm { + +class VersionTuple; + +namespace yaml { + +enum class NodeKind : uint8_t { + Scalar, + Map, + Sequence, +}; + +struct EmptyContext {}; + +/// This class should be specialized by any type that needs to be converted +/// to/from a YAML mapping. For example: +/// +/// struct MappingTraits<MyStruct> { +/// static void mapping(IO &io, MyStruct &s) { +/// io.mapRequired("name", s.name); +/// io.mapRequired("size", s.size); +/// io.mapOptional("age", s.age); +/// } +/// }; +template<class T> +struct MappingTraits { + // Must provide: + // static void mapping(IO &io, T &fields); + // Optionally may provide: + // static std::string validate(IO &io, T &fields); + // static void enumInput(IO &io, T &value); + // + // The optional flow flag will cause generated YAML to use a flow mapping + // (e.g. { a: 0, b: 1 }): + // static const bool flow = true; +}; + +/// This class is similar to MappingTraits<T> but allows you to pass in +/// additional context for each map operation. For example: +/// +/// struct MappingContextTraits<MyStruct, MyContext> { +/// static void mapping(IO &io, MyStruct &s, MyContext &c) { +/// io.mapRequired("name", s.name); +/// io.mapRequired("size", s.size); +/// io.mapOptional("age", s.age); +/// ++c.TimesMapped; +/// } +/// }; +template <class T, class Context> struct MappingContextTraits { + // Must provide: + // static void mapping(IO &io, T &fields, Context &Ctx); + // Optionally may provide: + // static std::string validate(IO &io, T &fields, Context &Ctx); + // + // The optional flow flag will cause generated YAML to use a flow mapping + // (e.g. { a: 0, b: 1 }): + // static const bool flow = true; +}; + +/// This class should be specialized by any integral type that converts +/// to/from a YAML scalar where there is a one-to-one mapping between +/// in-memory values and a string in YAML. For example: +/// +/// struct ScalarEnumerationTraits<Colors> { +/// static void enumeration(IO &io, Colors &value) { +/// io.enumCase(value, "red", cRed); +/// io.enumCase(value, "blue", cBlue); +/// io.enumCase(value, "green", cGreen); +/// } +/// }; +template <typename T, typename Enable = void> struct ScalarEnumerationTraits { + // Must provide: + // static void enumeration(IO &io, T &value); +}; + +/// This class should be specialized by any integer type that is a union +/// of bit values and the YAML representation is a flow sequence of +/// strings. For example: +/// +/// struct ScalarBitSetTraits<MyFlags> { +/// static void bitset(IO &io, MyFlags &value) { +/// io.bitSetCase(value, "big", flagBig); +/// io.bitSetCase(value, "flat", flagFlat); +/// io.bitSetCase(value, "round", flagRound); +/// } +/// }; +template <typename T, typename Enable = void> struct ScalarBitSetTraits { + // Must provide: + // 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: +/// +/// template<> +/// struct ScalarTraits<MyType> { +/// static void output(const MyType &val, void*, llvm::raw_ostream &out) { +/// // stream out custom formatting +/// out << llvm::format("%x", val); +/// } +/// static StringRef input(StringRef scalar, void*, MyType &value) { +/// // parse scalar and set `value` +/// // return empty string on success, or error string +/// return StringRef(); +/// } +/// static QuotingType mustQuote(StringRef) { return QuotingType::Single; } +/// }; +template <typename T, typename Enable = void> struct ScalarTraits { + // Must provide: + // + // Function to write the value as a string: + // static void output(const T &value, void *ctxt, llvm::raw_ostream &out); + // + // Function to convert a string to a value. Returns the empty + // StringRef on success or an error string if string is malformed: + // static StringRef input(StringRef scalar, void *ctxt, T &value); + // + // Function to determine if the value should be quoted. + // static QuotingType mustQuote(StringRef); +}; + +/// This class should be specialized by type that requires custom conversion +/// to/from a YAML literal block scalar. For example: +/// +/// template <> +/// struct BlockScalarTraits<MyType> { +/// static void output(const MyType &Value, void*, llvm::raw_ostream &Out) +/// { +/// // stream out custom formatting +/// Out << Value; +/// } +/// static StringRef input(StringRef Scalar, void*, MyType &Value) { +/// // parse scalar and set `value` +/// // return empty string on success, or error string +/// return StringRef(); +/// } +/// }; +template <typename T> +struct BlockScalarTraits { + // Must provide: + // + // Function to write the value as a string: + // static void output(const T &Value, void *ctx, llvm::raw_ostream &Out); + // + // Function to convert a string to a value. Returns the empty + // StringRef on success or an error string if string is malformed: + // static StringRef input(StringRef Scalar, void *ctxt, T &Value); + // + // Optional: + // static StringRef inputTag(T &Val, std::string Tag) + // static void outputTag(const T &Val, raw_ostream &Out) +}; + +/// This class should be specialized by type that requires custom conversion +/// to/from a YAML scalar with optional tags. For example: +/// +/// template <> +/// struct TaggedScalarTraits<MyType> { +/// static void output(const MyType &Value, void*, llvm::raw_ostream +/// &ScalarOut, llvm::raw_ostream &TagOut) +/// { +/// // stream out custom formatting including optional Tag +/// Out << Value; +/// } +/// static StringRef input(StringRef Scalar, StringRef Tag, void*, MyType +/// &Value) { +/// // parse scalar and set `value` +/// // return empty string on success, or error string +/// return StringRef(); +/// } +/// static QuotingType mustQuote(const MyType &Value, StringRef) { +/// return QuotingType::Single; +/// } +/// }; +template <typename T> struct TaggedScalarTraits { + // Must provide: + // + // Function to write the value and tag as strings: + // static void output(const T &Value, void *ctx, llvm::raw_ostream &ScalarOut, + // llvm::raw_ostream &TagOut); + // + // Function to convert a string to a value. Returns the empty + // StringRef on success or an error string if string is malformed: + // static StringRef input(StringRef Scalar, StringRef Tag, void *ctxt, T + // &Value); + // + // Function to determine if the value should be quoted. + // static QuotingType mustQuote(const T &Value, StringRef Scalar); +}; + +/// This class should be specialized by any type that needs to be converted +/// to/from a YAML sequence. For example: +/// +/// template<> +/// struct SequenceTraits<MyContainer> { +/// static size_t size(IO &io, MyContainer &seq) { +/// return seq.size(); +/// } +/// static MyType& element(IO &, MyContainer &seq, size_t index) { +/// if ( index >= seq.size() ) +/// seq.resize(index+1); +/// return seq[index]; +/// } +/// }; +template<typename T, typename EnableIf = void> +struct SequenceTraits { + // Must provide: + // static size_t size(IO &io, T &seq); + // static T::value_type& element(IO &io, T &seq, size_t index); + // + // The following is option and will cause generated YAML to use + // a flow sequence (e.g. [a,b,c]). + // static const bool flow = true; +}; + +/// This class should be specialized by any type for which vectors of that +/// type need to be converted to/from a YAML sequence. +template<typename T, typename EnableIf = void> +struct SequenceElementTraits { + // Must provide: + // static const bool flow; +}; + +/// This class should be specialized by any type that needs to be converted +/// to/from a list of YAML documents. +template<typename T> +struct DocumentListTraits { + // Must provide: + // static size_t size(IO &io, T &seq); + // static T::value_type& element(IO &io, T &seq, size_t index); +}; + +/// This class should be specialized by any type that needs to be converted +/// to/from a YAML mapping in the case where the names of the keys are not known +/// in advance, e.g. a string map. +template <typename T> +struct CustomMappingTraits { + // static void inputOne(IO &io, StringRef key, T &elem); + // static void output(IO &io, T &elem); +}; + +/// This class should be specialized by any type that can be represented as +/// a scalar, map, or sequence, decided dynamically. For example: +/// +/// typedef std::unique_ptr<MyBase> MyPoly; +/// +/// template<> +/// struct PolymorphicTraits<MyPoly> { +/// static NodeKind getKind(const MyPoly &poly) { +/// return poly->getKind(); +/// } +/// static MyScalar& getAsScalar(MyPoly &poly) { +/// if (!poly || !isa<MyScalar>(poly)) +/// poly.reset(new MyScalar()); +/// return *cast<MyScalar>(poly.get()); +/// } +/// // ... +/// }; +template <typename T> struct PolymorphicTraits { + // Must provide: + // static NodeKind getKind(const T &poly); + // static scalar_type &getAsScalar(T &poly); + // static map_type &getAsMap(T &poly); + // static sequence_type &getAsSequence(T &poly); +}; + +// Only used for better diagnostics of missing traits +template <typename T> +struct MissingTrait; + +// Test if ScalarEnumerationTraits<T> is defined on type T. +template <class T> +struct has_ScalarEnumerationTraits +{ + using Signature_enumeration = void (*)(class IO&, T&); + + template <typename U> + static char test(SameType<Signature_enumeration, &U::enumeration>*); + + template <typename U> + static double test(...); + + static bool const value = + (sizeof(test<ScalarEnumerationTraits<T>>(nullptr)) == 1); +}; + +// Test if ScalarBitSetTraits<T> is defined on type T. +template <class T> +struct has_ScalarBitSetTraits +{ + using Signature_bitset = void (*)(class IO&, T&); + + template <typename U> + static char test(SameType<Signature_bitset, &U::bitset>*); + + template <typename U> + static double test(...); + + static bool const value = (sizeof(test<ScalarBitSetTraits<T>>(nullptr)) == 1); +}; + +// Test if ScalarTraits<T> is defined on type T. +template <class T> +struct has_ScalarTraits +{ + using Signature_input = StringRef (*)(StringRef, void*, T&); + using Signature_output = void (*)(const T&, void*, raw_ostream&); + using Signature_mustQuote = QuotingType (*)(StringRef); + + template <typename U> + static char test(SameType<Signature_input, &U::input> *, + SameType<Signature_output, &U::output> *, + SameType<Signature_mustQuote, &U::mustQuote> *); + + template <typename U> + static double test(...); + + static bool const value = + (sizeof(test<ScalarTraits<T>>(nullptr, nullptr, nullptr)) == 1); +}; + +// Test if BlockScalarTraits<T> is defined on type T. +template <class T> +struct has_BlockScalarTraits +{ + using Signature_input = StringRef (*)(StringRef, void *, T &); + using Signature_output = void (*)(const T &, void *, raw_ostream &); + + template <typename U> + static char test(SameType<Signature_input, &U::input> *, + SameType<Signature_output, &U::output> *); + + template <typename U> + static double test(...); + + static bool const value = + (sizeof(test<BlockScalarTraits<T>>(nullptr, nullptr)) == 1); +}; + +// Test if TaggedScalarTraits<T> is defined on type T. +template <class T> struct has_TaggedScalarTraits { + using Signature_input = StringRef (*)(StringRef, StringRef, void *, T &); + using Signature_output = void (*)(const T &, void *, raw_ostream &, + raw_ostream &); + using Signature_mustQuote = QuotingType (*)(const T &, StringRef); + + template <typename U> + static char test(SameType<Signature_input, &U::input> *, + SameType<Signature_output, &U::output> *, + SameType<Signature_mustQuote, &U::mustQuote> *); + + template <typename U> static double test(...); + + static bool const value = + (sizeof(test<TaggedScalarTraits<T>>(nullptr, nullptr, nullptr)) == 1); +}; + +// Test if MappingContextTraits<T> is defined on type T. +template <class T, class Context> struct has_MappingTraits { + using Signature_mapping = void (*)(class IO &, T &, Context &); + + template <typename U> + static char test(SameType<Signature_mapping, &U::mapping>*); + + template <typename U> + static double test(...); + + static bool const value = + (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1); +}; + +// Test if MappingTraits<T> is defined on type T. +template <class T> struct has_MappingTraits<T, EmptyContext> { + using Signature_mapping = void (*)(class IO &, T &); + + template <typename U> + static char test(SameType<Signature_mapping, &U::mapping> *); + + template <typename U> static double test(...); + + static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1); +}; + +// Test if MappingContextTraits<T>::validate() is defined on type T. +template <class T, class Context> struct has_MappingValidateTraits { + using Signature_validate = std::string (*)(class IO &, T &, Context &); + + template <typename U> + static char test(SameType<Signature_validate, &U::validate>*); + + template <typename U> + static double test(...); + + static bool const value = + (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1); +}; + +// Test if MappingTraits<T>::validate() is defined on type T. +template <class T> struct has_MappingValidateTraits<T, EmptyContext> { + using Signature_validate = std::string (*)(class IO &, T &); + + template <typename U> + static char test(SameType<Signature_validate, &U::validate> *); + + template <typename U> static double test(...); + + static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1); +}; + +// Test if MappingContextTraits<T>::enumInput() is defined on type T. +template <class T, class Context> struct has_MappingEnumInputTraits { + using Signature_validate = void (*)(class IO &, T &); + + template <typename U> + static char test(SameType<Signature_validate, &U::enumInput> *); + + template <typename U> static double test(...); + + static bool const value = + (sizeof(test<MappingContextTraits<T, Context>>(nullptr)) == 1); +}; + +// Test if MappingTraits<T>::enumInput() is defined on type T. +template <class T> struct has_MappingEnumInputTraits<T, EmptyContext> { + using Signature_validate = void (*)(class IO &, T &); + + template <typename U> + static char test(SameType<Signature_validate, &U::enumInput> *); + + template <typename U> static double test(...); + + static bool const value = (sizeof(test<MappingTraits<T>>(nullptr)) == 1); +}; + +// Test if SequenceTraits<T> is defined on type T. +template <class T> +struct has_SequenceMethodTraits +{ + using Signature_size = size_t (*)(class IO&, T&); + + template <typename U> + static char test(SameType<Signature_size, &U::size>*); + + template <typename U> + static double test(...); + + static bool const value = (sizeof(test<SequenceTraits<T>>(nullptr)) == 1); +}; + +// Test if CustomMappingTraits<T> is defined on type T. +template <class T> +struct has_CustomMappingTraits +{ + using Signature_input = void (*)(IO &io, StringRef key, T &v); + + template <typename U> + static char test(SameType<Signature_input, &U::inputOne>*); + + template <typename U> + static double test(...); + + static bool const value = + (sizeof(test<CustomMappingTraits<T>>(nullptr)) == 1); +}; + +// has_FlowTraits<int> will cause an error with some compilers because +// it subclasses int. Using this wrapper only instantiates the +// real has_FlowTraits only if the template type is a class. +template <typename T, bool Enabled = std::is_class<T>::value> +class has_FlowTraits +{ +public: + static const bool value = false; +}; + +// Some older gcc compilers don't support straight forward tests +// for members, so test for ambiguity cause by the base and derived +// classes both defining the member. +template <class T> +struct has_FlowTraits<T, true> +{ + struct Fallback { bool flow; }; + struct Derived : T, Fallback { }; + + template<typename C> + static char (&f(SameType<bool Fallback::*, &C::flow>*))[1]; + + template<typename C> + static char (&f(...))[2]; + + static bool const value = sizeof(f<Derived>(nullptr)) == 2; +}; + +// Test if SequenceTraits<T> is defined on type T +template<typename T> +struct has_SequenceTraits : public std::integral_constant<bool, + has_SequenceMethodTraits<T>::value > { }; + +// Test if DocumentListTraits<T> is defined on type T +template <class T> +struct has_DocumentListTraits +{ + using Signature_size = size_t (*)(class IO &, T &); + + template <typename U> + static char test(SameType<Signature_size, &U::size>*); + + template <typename U> + static double test(...); + + static bool const value = (sizeof(test<DocumentListTraits<T>>(nullptr))==1); +}; + +template <class T> struct has_PolymorphicTraits { + using Signature_getKind = NodeKind (*)(const T &); + + template <typename U> + static char test(SameType<Signature_getKind, &U::getKind> *); + + template <typename U> static double test(...); + + static bool const value = (sizeof(test<PolymorphicTraits<T>>(nullptr)) == 1); +}; + +inline bool isNumeric(StringRef S) { + const auto skipDigits = [](StringRef Input) { + return Input.ltrim("0123456789"); + }; + + // Make S.front() and S.drop_front().front() (if S.front() is [+-]) calls + // safe. + if (S.empty() || S.equals("+") || S.equals("-")) + return false; + + if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN")) + return true; + + // Infinity and decimal numbers can be prefixed with sign. + StringRef Tail = (S.front() == '-' || S.front() == '+') ? S.drop_front() : S; + + // Check for infinity first, because checking for hex and oct numbers is more + // expensive. + if (Tail.equals(".inf") || Tail.equals(".Inf") || Tail.equals(".INF")) + return true; + + // Section 10.3.2 Tag Resolution + // YAML 1.2 Specification prohibits Base 8 and Base 16 numbers prefixed with + // [-+], so S should be used instead of Tail. + if (S.startswith("0o")) + return S.size() > 2 && + S.drop_front(2).find_first_not_of("01234567") == StringRef::npos; + + if (S.startswith("0x")) + return S.size() > 2 && S.drop_front(2).find_first_not_of( + "0123456789abcdefABCDEF") == StringRef::npos; + + // Parse float: [-+]? (\. [0-9]+ | [0-9]+ (\. [0-9]* )?) ([eE] [-+]? [0-9]+)? + S = Tail; + + // Handle cases when the number starts with '.' and hence needs at least one + // digit after dot (as opposed by number which has digits before the dot), but + // doesn't have one. + if (S.startswith(".") && + (S.equals(".") || + (S.size() > 1 && std::strchr("0123456789", S[1]) == nullptr))) + return false; + + if (S.startswith("E") || S.startswith("e")) + return false; + + enum ParseState { + Default, + FoundDot, + FoundExponent, + }; + ParseState State = Default; + + S = skipDigits(S); + + // Accept decimal integer. + if (S.empty()) + return true; + + if (S.front() == '.') { + State = FoundDot; + S = S.drop_front(); + } else if (S.front() == 'e' || S.front() == 'E') { + State = FoundExponent; + S = S.drop_front(); + } else { + return false; + } + + if (State == FoundDot) { + S = skipDigits(S); + if (S.empty()) + return true; + + if (S.front() == 'e' || S.front() == 'E') { + State = FoundExponent; + S = S.drop_front(); + } else { + return false; + } + } + + assert(State == FoundExponent && "Should have found exponent at this point."); + if (S.empty()) + return false; + + if (S.front() == '+' || S.front() == '-') { + S = S.drop_front(); + if (S.empty()) + return false; + } + + return skipDigits(S).empty(); +} + +inline bool isNull(StringRef S) { + return S.equals("null") || S.equals("Null") || S.equals("NULL") || + S.equals("~"); +} + +inline bool isBool(StringRef S) { + // FIXME: using parseBool is causing multiple tests to fail. + return S.equals("true") || S.equals("True") || S.equals("TRUE") || + S.equals("false") || S.equals("False") || S.equals("FALSE"); +} + +// 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 QuotingType::Single; + + QuotingType MaxQuotingNeeded = QuotingType::None; + if (isSpace(static_cast<unsigned char>(S.front())) || + isSpace(static_cast<unsigned char>(S.back()))) + MaxQuotingNeeded = QuotingType::Single; + if (isNull(S)) + MaxQuotingNeeded = QuotingType::Single; + if (isBool(S)) + MaxQuotingNeeded = QuotingType::Single; + if (isNumeric(S)) + MaxQuotingNeeded = QuotingType::Single; + + // 7.3.3 Plain Style + // Plain scalars must not begin with most indicators, as this would cause + // ambiguity with other YAML constructs. + if (std::strchr(R"(-?:\,[]{}#&*!|>'"%@`)", S[0]) != nullptr) + MaxQuotingNeeded = QuotingType::Single; + + for (unsigned char C : S) { + // Alphanum is safe. + if (isAlnum(C)) + continue; + + switch (C) { + // Safe scalar characters. + case '_': + case '-': + case '^': + case '.': + case ',': + case ' ': + // TAB (0x9) is allowed in unquoted strings. + case 0x9: + continue; + // LF(0xA) and CR(0xD) may delimit values and so require at least single + // quotes. LLVM YAML parser cannot handle single quoted multiline so use + // double quoting to produce valid YAML. + case 0xA: + case 0xD: + return QuotingType::Double; + // DEL (0x7F) are excluded from the allowed character range. + case 0x7F: + return QuotingType::Double; + // Forward slash is allowed to be unquoted, but we quote it anyway. We have + // many tests that use FileCheck against YAML output, and this output often + // contains paths. If we quote backslashes but not forward slashes then + // paths will come out either quoted or unquoted depending on which platform + // the test is run on, making FileCheck comparisons difficult. + case '/': + default: { + // C0 control block (0x0 - 0x1F) is excluded from the allowed character + // range. + if (C <= 0x1F) + return QuotingType::Double; + + // Always double quote UTF-8. + if ((C & 0x80) != 0) + return QuotingType::Double; + + // The character is not safe, at least simple quoting needed. + MaxQuotingNeeded = QuotingType::Single; + } + } + } + + return MaxQuotingNeeded; +} + +template <typename T, typename Context> +struct missingTraits + : public std::integral_constant<bool, + !has_ScalarEnumerationTraits<T>::value && + !has_ScalarBitSetTraits<T>::value && + !has_ScalarTraits<T>::value && + !has_BlockScalarTraits<T>::value && + !has_TaggedScalarTraits<T>::value && + !has_MappingTraits<T, Context>::value && + !has_SequenceTraits<T>::value && + !has_CustomMappingTraits<T>::value && + !has_DocumentListTraits<T>::value && + !has_PolymorphicTraits<T>::value> {}; + +template <typename T, typename Context> +struct validatedMappingTraits + : public std::integral_constant< + bool, has_MappingTraits<T, Context>::value && + has_MappingValidateTraits<T, Context>::value> {}; + +template <typename T, typename Context> +struct unvalidatedMappingTraits + : public std::integral_constant< + bool, has_MappingTraits<T, Context>::value && + !has_MappingValidateTraits<T, Context>::value> {}; + +// Base class for Input and Output. +class IO { +public: + IO(void *Ctxt = nullptr); + virtual ~IO(); + + virtual bool outputting() const = 0; + + virtual unsigned beginSequence() = 0; + virtual bool preflightElement(unsigned, void *&) = 0; + virtual void postflightElement(void*) = 0; + virtual void endSequence() = 0; + virtual bool canElideEmptySequence() = 0; + + virtual unsigned beginFlowSequence() = 0; + virtual bool preflightFlowElement(unsigned, void *&) = 0; + virtual void postflightFlowElement(void*) = 0; + virtual void endFlowSequence() = 0; + + virtual bool mapTag(StringRef Tag, bool Default=false) = 0; + virtual void beginMapping() = 0; + virtual void endMapping() = 0; + virtual bool preflightKey(const char*, bool, bool, bool &, void *&) = 0; + virtual void postflightKey(void*) = 0; + virtual std::vector<StringRef> keys() = 0; + + virtual void beginFlowMapping() = 0; + virtual void endFlowMapping() = 0; + + virtual void beginEnumScalar() = 0; + virtual bool matchEnumScalar(const char*, bool) = 0; + virtual bool matchEnumFallback() = 0; + virtual void endEnumScalar() = 0; + + virtual bool beginBitSetScalar(bool &) = 0; + virtual bool bitSetMatch(const char*, bool) = 0; + virtual void endBitSetScalar() = 0; + + virtual void scalarString(StringRef &, QuotingType) = 0; + virtual void blockScalarString(StringRef &) = 0; + virtual void scalarTag(std::string &) = 0; + + virtual NodeKind getNodeKind() = 0; + + virtual void setError(const Twine &) = 0; + virtual void setAllowUnknownKeys(bool Allow); + + template <typename T> + void enumCase(T &Val, const char* Str, const T ConstVal) { + if ( matchEnumScalar(Str, outputting() && Val == ConstVal) ) { + Val = ConstVal; + } + } + + // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF + template <typename T> + void enumCase(T &Val, const char* Str, const uint32_t ConstVal) { + if ( matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal)) ) { + Val = ConstVal; + } + } + + template <typename FBT, typename T> + void enumFallback(T &Val) { + if (matchEnumFallback()) { + EmptyContext Context; + // FIXME: Force integral conversion to allow strong typedefs to convert. + FBT Res = static_cast<typename FBT::BaseType>(Val); + yamlize(*this, Res, true, Context); + Val = static_cast<T>(static_cast<typename FBT::BaseType>(Res)); + } + } + + template <typename T> + void bitSetCase(T &Val, const char* Str, const T ConstVal) { + if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { + Val = static_cast<T>(Val | ConstVal); + } + } + + // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF + template <typename T> + void bitSetCase(T &Val, const char* Str, const uint32_t ConstVal) { + if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) { + Val = static_cast<T>(Val | ConstVal); + } + } + + template <typename T> + void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) { + if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) + Val = Val | ConstVal; + } + + template <typename T> + void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal, + uint32_t Mask) { + if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) + Val = Val | ConstVal; + } + + void *getContext() const; + void setContext(void *); + + template <typename T> void mapRequired(const char *Key, T &Val) { + EmptyContext Ctx; + this->processKey(Key, Val, true, Ctx); + } + + template <typename T, typename Context> + void mapRequired(const char *Key, T &Val, Context &Ctx) { + this->processKey(Key, Val, true, Ctx); + } + + template <typename T> void mapOptional(const char *Key, T &Val) { + EmptyContext Ctx; + mapOptionalWithContext(Key, Val, Ctx); + } + + template <typename T, typename DefaultT> + void mapOptional(const char *Key, T &Val, const DefaultT &Default) { + EmptyContext Ctx; + mapOptionalWithContext(Key, Val, Default, Ctx); + } + + template <typename T, typename Context> + std::enable_if_t<has_SequenceTraits<T>::value, void> + mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { + // omit key/value instead of outputting empty sequence + if (this->canElideEmptySequence() && !(Val.begin() != Val.end())) + return; + this->processKey(Key, Val, false, Ctx); + } + + template <typename T, typename Context> + void mapOptionalWithContext(const char *Key, std::optional<T> &Val, + Context &Ctx) { + this->processKeyWithDefault(Key, Val, std::optional<T>(), + /*Required=*/false, Ctx); + } + + template <typename T, typename Context> + std::enable_if_t<!has_SequenceTraits<T>::value, void> + mapOptionalWithContext(const char *Key, T &Val, Context &Ctx) { + this->processKey(Key, Val, false, Ctx); + } + + template <typename T, typename Context, typename DefaultT> + void mapOptionalWithContext(const char *Key, T &Val, const DefaultT &Default, + Context &Ctx) { + static_assert(std::is_convertible<DefaultT, T>::value, + "Default type must be implicitly convertible to value type!"); + this->processKeyWithDefault(Key, Val, static_cast<const T &>(Default), + false, Ctx); + } + +private: + template <typename T, typename Context> + void processKeyWithDefault(const char *Key, std::optional<T> &Val, + const std::optional<T> &DefaultValue, + bool Required, Context &Ctx); + + template <typename T, typename Context> + void processKeyWithDefault(const char *Key, T &Val, const T &DefaultValue, + bool Required, Context &Ctx) { + void *SaveInfo; + bool UseDefault; + const bool sameAsDefault = outputting() && Val == DefaultValue; + if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault, + SaveInfo) ) { + yamlize(*this, Val, Required, Ctx); + this->postflightKey(SaveInfo); + } + else { + if ( UseDefault ) + Val = DefaultValue; + } + } + + template <typename T, typename Context> + void processKey(const char *Key, T &Val, bool Required, Context &Ctx) { + void *SaveInfo; + bool UseDefault; + if ( this->preflightKey(Key, Required, false, UseDefault, SaveInfo) ) { + yamlize(*this, Val, Required, Ctx); + this->postflightKey(SaveInfo); + } + } + +private: + void *Ctxt; +}; + +namespace detail { + +template <typename T, typename Context> +void doMapping(IO &io, T &Val, Context &Ctx) { + MappingContextTraits<T, Context>::mapping(io, Val, Ctx); +} + +template <typename T> void doMapping(IO &io, T &Val, EmptyContext &Ctx) { + MappingTraits<T>::mapping(io, Val); +} + +} // end namespace detail + +template <typename T> +std::enable_if_t<has_ScalarEnumerationTraits<T>::value, void> +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + io.beginEnumScalar(); + ScalarEnumerationTraits<T>::enumeration(io, Val); + io.endEnumScalar(); +} + +template <typename T> +std::enable_if_t<has_ScalarBitSetTraits<T>::value, void> +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + bool DoClear; + if ( io.beginBitSetScalar(DoClear) ) { + if ( DoClear ) + Val = T(); + ScalarBitSetTraits<T>::bitset(io, Val); + io.endBitSetScalar(); + } +} + +template <typename T> +std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool, + EmptyContext &Ctx) { + if ( io.outputting() ) { + SmallString<128> Storage; + raw_svector_ostream Buffer(Storage); + ScalarTraits<T>::output(Val, io.getContext(), Buffer); + StringRef Str = Buffer.str(); + io.scalarString(Str, ScalarTraits<T>::mustQuote(Str)); + } + else { + StringRef Str; + io.scalarString(Str, ScalarTraits<T>::mustQuote(Str)); + StringRef Result = ScalarTraits<T>::input(Str, io.getContext(), Val); + if ( !Result.empty() ) { + io.setError(Twine(Result)); + } + } +} + +template <typename T> +std::enable_if_t<has_BlockScalarTraits<T>::value, void> +yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { + if (YamlIO.outputting()) { + std::string Storage; + raw_string_ostream Buffer(Storage); + BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer); + StringRef Str = Buffer.str(); + YamlIO.blockScalarString(Str); + } else { + StringRef Str; + YamlIO.blockScalarString(Str); + StringRef Result = + BlockScalarTraits<T>::input(Str, YamlIO.getContext(), Val); + if (!Result.empty()) + YamlIO.setError(Twine(Result)); + } +} + +template <typename T> +std::enable_if_t<has_TaggedScalarTraits<T>::value, void> +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + if (io.outputting()) { + std::string ScalarStorage, TagStorage; + raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage); + TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer, + TagBuffer); + io.scalarTag(TagBuffer.str()); + StringRef ScalarStr = ScalarBuffer.str(); + io.scalarString(ScalarStr, + TaggedScalarTraits<T>::mustQuote(Val, ScalarStr)); + } else { + std::string Tag; + io.scalarTag(Tag); + StringRef Str; + io.scalarString(Str, QuotingType::None); + StringRef Result = + TaggedScalarTraits<T>::input(Str, Tag, io.getContext(), Val); + if (!Result.empty()) { + io.setError(Twine(Result)); + } + } +} + +template <typename T, typename Context> +std::enable_if_t<validatedMappingTraits<T, Context>::value, void> +yamlize(IO &io, T &Val, bool, Context &Ctx) { + if (has_FlowTraits<MappingTraits<T>>::value) + io.beginFlowMapping(); + else + io.beginMapping(); + if (io.outputting()) { + std::string Err = MappingTraits<T>::validate(io, Val); + if (!Err.empty()) { + errs() << Err << "\n"; + assert(Err.empty() && "invalid struct trying to be written as yaml"); + } + } + detail::doMapping(io, Val, Ctx); + if (!io.outputting()) { + std::string Err = MappingTraits<T>::validate(io, Val); + if (!Err.empty()) + io.setError(Err); + } + if (has_FlowTraits<MappingTraits<T>>::value) + io.endFlowMapping(); + else + io.endMapping(); +} + +template <typename T, typename Context> +std::enable_if_t<!has_MappingEnumInputTraits<T, Context>::value, bool> +yamlizeMappingEnumInput(IO &io, T &Val) { + return false; +} + +template <typename T, typename Context> +std::enable_if_t<has_MappingEnumInputTraits<T, Context>::value, bool> +yamlizeMappingEnumInput(IO &io, T &Val) { + if (io.outputting()) + return false; + + io.beginEnumScalar(); + MappingTraits<T>::enumInput(io, Val); + bool Matched = !io.matchEnumFallback(); + io.endEnumScalar(); + return Matched; +} + +template <typename T, typename Context> +std::enable_if_t<unvalidatedMappingTraits<T, Context>::value, void> +yamlize(IO &io, T &Val, bool, Context &Ctx) { + if (yamlizeMappingEnumInput<T, Context>(io, Val)) + return; + if (has_FlowTraits<MappingTraits<T>>::value) { + io.beginFlowMapping(); + detail::doMapping(io, Val, Ctx); + io.endFlowMapping(); + } else { + io.beginMapping(); + detail::doMapping(io, Val, Ctx); + io.endMapping(); + } +} + +template <typename T> +std::enable_if_t<has_CustomMappingTraits<T>::value, void> +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + if ( io.outputting() ) { + io.beginMapping(); + CustomMappingTraits<T>::output(io, Val); + io.endMapping(); + } else { + io.beginMapping(); + for (StringRef key : io.keys()) + CustomMappingTraits<T>::inputOne(io, key, Val); + io.endMapping(); + } +} + +template <typename T> +std::enable_if_t<has_PolymorphicTraits<T>::value, void> +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val) + : io.getNodeKind()) { + case NodeKind::Scalar: + return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx); + case NodeKind::Map: + return yamlize(io, PolymorphicTraits<T>::getAsMap(Val), true, Ctx); + case NodeKind::Sequence: + return yamlize(io, PolymorphicTraits<T>::getAsSequence(Val), true, Ctx); + } +} + +template <typename T> +std::enable_if_t<missingTraits<T, EmptyContext>::value, void> +yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { + char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; +} + +template <typename T, typename Context> +std::enable_if_t<has_SequenceTraits<T>::value, void> +yamlize(IO &io, T &Seq, bool, Context &Ctx) { + if ( has_FlowTraits< SequenceTraits<T>>::value ) { + unsigned incnt = io.beginFlowSequence(); + unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt; + for(unsigned i=0; i < count; ++i) { + void *SaveInfo; + if ( io.preflightFlowElement(i, SaveInfo) ) { + yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx); + io.postflightFlowElement(SaveInfo); + } + } + io.endFlowSequence(); + } + else { + unsigned incnt = io.beginSequence(); + unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt; + for(unsigned i=0; i < count; ++i) { + void *SaveInfo; + if ( io.preflightElement(i, SaveInfo) ) { + yamlize(io, SequenceTraits<T>::element(io, Seq, i), true, Ctx); + io.postflightElement(SaveInfo); + } + } + io.endSequence(); + } +} + +template<> +struct ScalarTraits<bool> { + static void output(const bool &, void* , raw_ostream &); + static StringRef input(StringRef, void *, bool &); + 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 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 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 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 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 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 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 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 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 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 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 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 QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +// For endian types, we use existing scalar Traits class for the underlying +// type. This way endian aware types are supported whenever the traits are +// defined for the underlying type. +template <typename value_type, support::endianness endian, size_t alignment> +struct ScalarTraits<support::detail::packed_endian_specific_integral< + value_type, endian, alignment>, + std::enable_if_t<has_ScalarTraits<value_type>::value>> { + using endian_type = + support::detail::packed_endian_specific_integral<value_type, endian, + alignment>; + + static void output(const endian_type &E, void *Ctx, raw_ostream &Stream) { + ScalarTraits<value_type>::output(static_cast<value_type>(E), Ctx, Stream); + } + + static StringRef input(StringRef Str, void *Ctx, endian_type &E) { + value_type V; + auto R = ScalarTraits<value_type>::input(Str, Ctx, V); + E = static_cast<endian_type>(V); + return R; + } + + static QuotingType mustQuote(StringRef Str) { + return ScalarTraits<value_type>::mustQuote(Str); + } +}; + +template <typename value_type, support::endianness endian, size_t alignment> +struct ScalarEnumerationTraits< + support::detail::packed_endian_specific_integral<value_type, endian, + alignment>, + std::enable_if_t<has_ScalarEnumerationTraits<value_type>::value>> { + using endian_type = + support::detail::packed_endian_specific_integral<value_type, endian, + alignment>; + + static void enumeration(IO &io, endian_type &E) { + value_type V = E; + ScalarEnumerationTraits<value_type>::enumeration(io, V); + E = V; + } +}; + +template <typename value_type, support::endianness endian, size_t alignment> +struct ScalarBitSetTraits< + support::detail::packed_endian_specific_integral<value_type, endian, + alignment>, + std::enable_if_t<has_ScalarBitSetTraits<value_type>::value>> { + using endian_type = + support::detail::packed_endian_specific_integral<value_type, endian, + alignment>; + static void bitset(IO &io, endian_type &E) { + value_type V = E; + ScalarBitSetTraits<value_type>::bitset(io, V); + E = V; + } +}; + +// Utility for use within MappingTraits<>::mapping() method +// to [de]normalize an object for use with YAML conversion. +template <typename TNorm, typename TFinal> +struct MappingNormalization { + MappingNormalization(IO &i_o, TFinal &Obj) + : io(i_o), BufPtr(nullptr), Result(Obj) { + if ( io.outputting() ) { + BufPtr = new (&Buffer) TNorm(io, Obj); + } + else { + BufPtr = new (&Buffer) TNorm(io); + } + } + + ~MappingNormalization() { + if ( ! io.outputting() ) { + Result = BufPtr->denormalize(io); + } + BufPtr->~TNorm(); + } + + TNorm* operator->() { return BufPtr; } + +private: + using Storage = AlignedCharArrayUnion<TNorm>; + + Storage Buffer; + IO &io; + TNorm *BufPtr; + TFinal &Result; +}; + +// Utility for use within MappingTraits<>::mapping() method +// to [de]normalize an object for use with YAML conversion. +template <typename TNorm, typename TFinal> +struct MappingNormalizationHeap { + MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator) + : io(i_o), Result(Obj) { + if ( io.outputting() ) { + BufPtr = new (&Buffer) TNorm(io, Obj); + } + else if (allocator) { + BufPtr = allocator->Allocate<TNorm>(); + new (BufPtr) TNorm(io); + } else { + BufPtr = new TNorm(io); + } + } + + ~MappingNormalizationHeap() { + if ( io.outputting() ) { + BufPtr->~TNorm(); + } + else { + Result = BufPtr->denormalize(io); + } + } + + TNorm* operator->() { return BufPtr; } + +private: + using Storage = AlignedCharArrayUnion<TNorm>; + + Storage Buffer; + IO &io; + TNorm *BufPtr = nullptr; + TFinal &Result; +}; + +/// +/// The Input class is used to parse a yaml document into in-memory structs +/// and vectors. +/// +/// It works by using YAMLParser to do a syntax parse of the entire yaml +/// document, then the Input class builds a graph of HNodes which wraps +/// each yaml Node. The extra layer is buffering. The low level yaml +/// parser only lets you look at each node once. The buffering layer lets +/// you search and interate multiple times. This is necessary because +/// the mapRequired() method calls may not be in the same order +/// as the keys in the document. +/// +class Input : public IO { +public: + // Construct a yaml Input object from a StringRef and optional + // user-data. The DiagHandler can be specified to provide + // alternative error reporting. + Input(StringRef InputContent, + void *Ctxt = nullptr, + SourceMgr::DiagHandlerTy DiagHandler = nullptr, + void *DiagHandlerCtxt = nullptr); + Input(MemoryBufferRef Input, + void *Ctxt = nullptr, + SourceMgr::DiagHandlerTy DiagHandler = nullptr, + void *DiagHandlerCtxt = nullptr); + ~Input() override; + + // Check if there was an syntax or semantic error during parsing. + std::error_code error(); + +private: + bool outputting() const override; + bool mapTag(StringRef, bool) override; + void beginMapping() override; + void endMapping() override; + bool preflightKey(const char *, bool, bool, bool &, void *&) override; + void postflightKey(void *) override; + std::vector<StringRef> keys() override; + void beginFlowMapping() override; + void endFlowMapping() override; + unsigned beginSequence() override; + void endSequence() override; + bool preflightElement(unsigned index, void *&) override; + void postflightElement(void *) override; + unsigned beginFlowSequence() override; + bool preflightFlowElement(unsigned , void *&) override; + void postflightFlowElement(void *) override; + void endFlowSequence() override; + void beginEnumScalar() override; + bool matchEnumScalar(const char*, bool) override; + bool matchEnumFallback() override; + void endEnumScalar() override; + bool beginBitSetScalar(bool &) override; + bool bitSetMatch(const char *, bool ) override; + void endBitSetScalar() override; + void scalarString(StringRef &, QuotingType) override; + void blockScalarString(StringRef &) override; + void scalarTag(std::string &) override; + NodeKind getNodeKind() override; + void setError(const Twine &message) override; + bool canElideEmptySequence() override; + + class HNode { + virtual void anchor(); + + public: + HNode(Node *n) : _node(n) { } + virtual ~HNode() = default; + + static bool classof(const HNode *) { return true; } + + Node *_node; + }; + + class EmptyHNode : public HNode { + void anchor() override; + + public: + EmptyHNode(Node *n) : HNode(n) { } + + static bool classof(const HNode *n) { return NullNode::classof(n->_node); } + + static bool classof(const EmptyHNode *) { return true; } + }; + + class ScalarHNode : public HNode { + void anchor() override; + + public: + ScalarHNode(Node *n, StringRef s) : HNode(n), _value(s) { } + + StringRef value() const { return _value; } + + static bool classof(const HNode *n) { + return ScalarNode::classof(n->_node) || + BlockScalarNode::classof(n->_node); + } + + static bool classof(const ScalarHNode *) { return true; } + + protected: + StringRef _value; + }; + + class MapHNode : public HNode { + void anchor() override; + + public: + MapHNode(Node *n) : HNode(n) { } + + static bool classof(const HNode *n) { + return MappingNode::classof(n->_node); + } + + static bool classof(const MapHNode *) { return true; } + + using NameToNodeAndLoc = + StringMap<std::pair<std::unique_ptr<HNode>, SMRange>>; + + NameToNodeAndLoc Mapping; + SmallVector<std::string, 6> ValidKeys; + }; + + class SequenceHNode : public HNode { + void anchor() override; + + public: + SequenceHNode(Node *n) : HNode(n) { } + + static bool classof(const HNode *n) { + return SequenceNode::classof(n->_node); + } + + static bool classof(const SequenceHNode *) { return true; } + + std::vector<std::unique_ptr<HNode>> Entries; + }; + + std::unique_ptr<Input::HNode> createHNodes(Node *node); + void setError(HNode *hnode, const Twine &message); + void setError(Node *node, const Twine &message); + void setError(const SMRange &Range, const Twine &message); + + void reportWarning(HNode *hnode, const Twine &message); + void reportWarning(Node *hnode, const Twine &message); + void reportWarning(const SMRange &Range, const Twine &message); + +public: + // These are only used by operator>>. They could be private + // if those templated things could be made friends. + bool setCurrentDocument(); + bool nextDocument(); + + /// Returns the current node that's being parsed by the YAML Parser. + const Node *getCurrentNode() const; + + void setAllowUnknownKeys(bool Allow) override; + +private: + SourceMgr SrcMgr; // must be before Strm + std::unique_ptr<llvm::yaml::Stream> Strm; + std::unique_ptr<HNode> TopNode; + std::error_code EC; + BumpPtrAllocator StringAllocator; + document_iterator DocIterator; + llvm::BitVector BitValuesUsed; + HNode *CurrentNode = nullptr; + bool ScalarMatchFound = false; + bool AllowUnknownKeys = false; +}; + +/// +/// The Output class is used to generate a yaml document from in-memory structs +/// and vectors. +/// +class Output : public IO { +public: + Output(raw_ostream &, void *Ctxt = nullptr, int WrapColumn = 70); + ~Output() override; + + /// Set whether or not to output optional values which are equal + /// to the default value. By default, when outputting if you attempt + /// to write a value that is equal to the default, the value gets ignored. + /// Sometimes, it is useful to be able to see these in the resulting YAML + /// anyway. + void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; } + + bool outputting() const override; + bool mapTag(StringRef, bool) override; + void beginMapping() override; + void endMapping() override; + bool preflightKey(const char *key, bool, bool, bool &, void *&) override; + void postflightKey(void *) override; + std::vector<StringRef> keys() override; + void beginFlowMapping() override; + void endFlowMapping() override; + unsigned beginSequence() override; + void endSequence() override; + bool preflightElement(unsigned, void *&) override; + void postflightElement(void *) override; + unsigned beginFlowSequence() override; + bool preflightFlowElement(unsigned, void *&) override; + void postflightFlowElement(void *) override; + void endFlowSequence() override; + void beginEnumScalar() override; + bool matchEnumScalar(const char*, bool) override; + bool matchEnumFallback() override; + void endEnumScalar() override; + bool beginBitSetScalar(bool &) override; + bool bitSetMatch(const char *, bool ) override; + void endBitSetScalar() override; + void scalarString(StringRef &, QuotingType) override; + void blockScalarString(StringRef &) override; + void scalarTag(std::string &) override; + NodeKind getNodeKind() override; + void setError(const Twine &message) override; + bool canElideEmptySequence() override; + + // These are only used by operator<<. They could be private + // if that templated operator could be made a friend. + void beginDocuments(); + bool preflightDocument(unsigned); + void postflightDocument(); + void endDocuments(); + +private: + void output(StringRef s); + void outputUpToEndOfLine(StringRef s); + void newLineCheck(bool EmptySequence = false); + void outputNewLine(); + void paddedKey(StringRef key); + void flowKey(StringRef Key); + + enum InState { + inSeqFirstElement, + inSeqOtherElement, + inFlowSeqFirstElement, + inFlowSeqOtherElement, + inMapFirstKey, + inMapOtherKey, + inFlowMapFirstKey, + inFlowMapOtherKey + }; + + static bool inSeqAnyElement(InState State); + static bool inFlowSeqAnyElement(InState State); + static bool inMapAnyKey(InState State); + static bool inFlowMapAnyKey(InState State); + + raw_ostream &Out; + int WrapColumn; + SmallVector<InState, 8> StateStack; + int Column = 0; + int ColumnAtFlowStart = 0; + int ColumnAtMapFlowStart = 0; + bool NeedBitValueComma = false; + bool NeedFlowSequenceComma = false; + bool EnumerationMatchFound = false; + bool WriteDefaultValues = false; + StringRef Padding; + StringRef PaddingBeforeContainer; +}; + +template <typename T, typename Context> +void IO::processKeyWithDefault(const char *Key, std::optional<T> &Val, + const std::optional<T> &DefaultValue, + bool Required, Context &Ctx) { + assert(!DefaultValue && "std::optional<T> shouldn't have a value!"); + void *SaveInfo; + bool UseDefault = true; + const bool sameAsDefault = outputting() && !Val; + if (!outputting() && !Val) + Val = T(); + if (Val && + this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { + + // When reading an std::optional<X> key from a YAML description, we allow + // the special "<none>" value, which can be used to specify that no value + // was requested, i.e. the DefaultValue will be assigned. The DefaultValue + // is usually None. + bool IsNone = false; + if (!outputting()) + if (const auto *Node = + dyn_cast<ScalarNode>(((Input *)this)->getCurrentNode())) + // We use rtrim to ignore possible white spaces that might exist when a + // comment is present on the same line. + IsNone = Node->getRawValue().rtrim(' ') == "<none>"; + + if (IsNone) + Val = DefaultValue; + else + yamlize(*this, *Val, Required, Ctx); + this->postflightKey(SaveInfo); + } else { + if (UseDefault) + Val = DefaultValue; + } +} + +/// YAML I/O does conversion based on types. But often native data types +/// are just a typedef of built in intergral types (e.g. int). But the C++ +/// type matching system sees through the typedef and all the typedefed types +/// look like a built in type. This will cause the generic YAML I/O conversion +/// to be used. To provide better control over the YAML conversion, you can +/// use this macro instead of typedef. It will create a class with one field +/// and automatic conversion operators to and from the base type. +/// Based on BOOST_STRONG_TYPEDEF +#define LLVM_YAML_STRONG_TYPEDEF(_base, _type) \ + struct _type { \ + _type() = default; \ + _type(const _base v) : value(v) {} \ + _type(const _type &v) = default; \ + _type &operator=(const _type &rhs) = default; \ + _type &operator=(const _base &rhs) { value = rhs; return *this; } \ + operator const _base & () const { return value; } \ + bool operator==(const _type &rhs) const { return value == rhs.value; } \ + bool operator==(const _base &rhs) const { return value == rhs; } \ + bool operator<(const _type &rhs) const { return value < rhs.value; } \ + _base value; \ + using BaseType = _base; \ + }; + +/// +/// Use these types instead of uintXX_t in any mapping to have +/// its yaml output formatted as hexadecimal. +/// +LLVM_YAML_STRONG_TYPEDEF(uint8_t, Hex8) +LLVM_YAML_STRONG_TYPEDEF(uint16_t, Hex16) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, Hex32) +LLVM_YAML_STRONG_TYPEDEF(uint64_t, Hex64) + +template<> +struct ScalarTraits<Hex8> { + static void output(const Hex8 &, void *, raw_ostream &); + static StringRef input(StringRef, void *, Hex8 &); + 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 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 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 QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +template <> struct ScalarTraits<VersionTuple> { + static void output(const VersionTuple &Value, void *, llvm::raw_ostream &Out); + static StringRef input(StringRef, void *, VersionTuple &); + static QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +// Define non-member operator>> so that Input can stream in a document list. +template <typename T> +inline std::enable_if_t<has_DocumentListTraits<T>::value, Input &> +operator>>(Input &yin, T &docList) { + int i = 0; + EmptyContext Ctx; + while ( yin.setCurrentDocument() ) { + yamlize(yin, DocumentListTraits<T>::element(yin, docList, i), true, Ctx); + if ( yin.error() ) + return yin; + yin.nextDocument(); + ++i; + } + return yin; +} + +// Define non-member operator>> so that Input can stream in a map as a document. +template <typename T> +inline std::enable_if_t<has_MappingTraits<T, EmptyContext>::value, Input &> +operator>>(Input &yin, T &docMap) { + EmptyContext Ctx; + yin.setCurrentDocument(); + yamlize(yin, docMap, true, Ctx); + return yin; +} + +// Define non-member operator>> so that Input can stream in a sequence as +// a document. +template <typename T> +inline std::enable_if_t<has_SequenceTraits<T>::value, Input &> +operator>>(Input &yin, T &docSeq) { + EmptyContext Ctx; + if (yin.setCurrentDocument()) + yamlize(yin, docSeq, true, Ctx); + return yin; +} + +// Define non-member operator>> so that Input can stream in a block scalar. +template <typename T> +inline std::enable_if_t<has_BlockScalarTraits<T>::value, Input &> +operator>>(Input &In, T &Val) { + EmptyContext Ctx; + if (In.setCurrentDocument()) + yamlize(In, Val, true, Ctx); + return In; +} + +// Define non-member operator>> so that Input can stream in a string map. +template <typename T> +inline std::enable_if_t<has_CustomMappingTraits<T>::value, Input &> +operator>>(Input &In, T &Val) { + EmptyContext Ctx; + if (In.setCurrentDocument()) + yamlize(In, Val, true, Ctx); + return In; +} + +// Define non-member operator>> so that Input can stream in a polymorphic type. +template <typename T> +inline std::enable_if_t<has_PolymorphicTraits<T>::value, Input &> +operator>>(Input &In, T &Val) { + EmptyContext Ctx; + if (In.setCurrentDocument()) + yamlize(In, Val, true, Ctx); + return In; +} + +// Provide better error message about types missing a trait specialization +template <typename T> +inline std::enable_if_t<missingTraits<T, EmptyContext>::value, Input &> +operator>>(Input &yin, T &docSeq) { + char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; + return yin; +} + +// Define non-member operator<< so that Output can stream out document list. +template <typename T> +inline std::enable_if_t<has_DocumentListTraits<T>::value, Output &> +operator<<(Output &yout, T &docList) { + EmptyContext Ctx; + yout.beginDocuments(); + const size_t count = DocumentListTraits<T>::size(yout, docList); + for(size_t i=0; i < count; ++i) { + if ( yout.preflightDocument(i) ) { + yamlize(yout, DocumentListTraits<T>::element(yout, docList, i), true, + Ctx); + yout.postflightDocument(); + } + } + yout.endDocuments(); + return yout; +} + +// Define non-member operator<< so that Output can stream out a map. +template <typename T> +inline std::enable_if_t<has_MappingTraits<T, EmptyContext>::value, Output &> +operator<<(Output &yout, T &map) { + EmptyContext Ctx; + yout.beginDocuments(); + if ( yout.preflightDocument(0) ) { + yamlize(yout, map, true, Ctx); + yout.postflightDocument(); + } + yout.endDocuments(); + return yout; +} + +// Define non-member operator<< so that Output can stream out a sequence. +template <typename T> +inline std::enable_if_t<has_SequenceTraits<T>::value, Output &> +operator<<(Output &yout, T &seq) { + EmptyContext Ctx; + yout.beginDocuments(); + if ( yout.preflightDocument(0) ) { + yamlize(yout, seq, true, Ctx); + yout.postflightDocument(); + } + yout.endDocuments(); + return yout; +} + +// Define non-member operator<< so that Output can stream out a block scalar. +template <typename T> +inline std::enable_if_t<has_BlockScalarTraits<T>::value, Output &> +operator<<(Output &Out, T &Val) { + EmptyContext Ctx; + Out.beginDocuments(); + if (Out.preflightDocument(0)) { + yamlize(Out, Val, true, Ctx); + Out.postflightDocument(); + } + Out.endDocuments(); + return Out; +} + +// Define non-member operator<< so that Output can stream out a string map. +template <typename T> +inline std::enable_if_t<has_CustomMappingTraits<T>::value, Output &> +operator<<(Output &Out, T &Val) { + EmptyContext Ctx; + Out.beginDocuments(); + if (Out.preflightDocument(0)) { + yamlize(Out, Val, true, Ctx); + Out.postflightDocument(); + } + Out.endDocuments(); + return Out; +} + +// Define non-member operator<< so that Output can stream out a polymorphic +// type. +template <typename T> +inline std::enable_if_t<has_PolymorphicTraits<T>::value, Output &> +operator<<(Output &Out, T &Val) { + EmptyContext Ctx; + Out.beginDocuments(); + if (Out.preflightDocument(0)) { + // FIXME: The parser does not support explicit documents terminated with a + // plain scalar; the end-marker is included as part of the scalar token. + assert(PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar && "plain scalar documents are not supported"); + yamlize(Out, Val, true, Ctx); + Out.postflightDocument(); + } + Out.endDocuments(); + return Out; +} + +// Provide better error message about types missing a trait specialization +template <typename T> +inline std::enable_if_t<missingTraits<T, EmptyContext>::value, Output &> +operator<<(Output &yout, T &seq) { + char missing_yaml_trait_for_type[sizeof(MissingTrait<T>)]; + return yout; +} + +template <bool B> struct IsFlowSequenceBase {}; +template <> struct IsFlowSequenceBase<true> { static const bool flow = true; }; + +template <typename T, typename U = void> +struct IsResizable : std::false_type {}; + +template <typename T> +struct IsResizable<T, std::void_t<decltype(std::declval<T>().resize(0))>> + : public std::true_type {}; + +template <typename T, bool B> struct IsResizableBase { + using type = typename T::value_type; + + static type &element(IO &io, T &seq, size_t index) { + if (index >= seq.size()) + seq.resize(index + 1); + return seq[index]; + } +}; + +template <typename T> struct IsResizableBase<T, false> { + using type = typename T::value_type; + + static type &element(IO &io, T &seq, size_t index) { + if (index >= seq.size()) { + io.setError(Twine("value sequence extends beyond static size (") + + Twine(seq.size()) + ")"); + return seq[0]; + } + return seq[index]; + } +}; + +template <typename T, bool Flow> +struct SequenceTraitsImpl + : IsFlowSequenceBase<Flow>, IsResizableBase<T, IsResizable<T>::value> { + static size_t size(IO &io, T &seq) { return seq.size(); } +}; + +// Simple helper to check an expression can be used as a bool-valued template +// argument. +template <bool> struct CheckIsBool { static const bool value = true; }; + +// If T has SequenceElementTraits, then vector<T> and SmallVector<T, N> have +// SequenceTraits that do the obvious thing. +template <typename T> +struct SequenceTraits< + std::vector<T>, + std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> + : SequenceTraitsImpl<std::vector<T>, SequenceElementTraits<T>::flow> {}; +template <typename T, unsigned N> +struct SequenceTraits< + SmallVector<T, N>, + std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> + : SequenceTraitsImpl<SmallVector<T, N>, SequenceElementTraits<T>::flow> {}; +template <typename T> +struct SequenceTraits< + SmallVectorImpl<T>, + std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> + : SequenceTraitsImpl<SmallVectorImpl<T>, SequenceElementTraits<T>::flow> {}; +template <typename T> +struct SequenceTraits< + MutableArrayRef<T>, + std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>> + : SequenceTraitsImpl<MutableArrayRef<T>, SequenceElementTraits<T>::flow> {}; + +// Sequences of fundamental types use flow formatting. +template <typename T> +struct SequenceElementTraits<T, + std::enable_if_t<std::is_fundamental<T>::value>> { + static const bool flow = true; +}; + +// Sequences of strings use block formatting. +template<> struct SequenceElementTraits<std::string> { + static const bool flow = false; +}; +template<> struct SequenceElementTraits<StringRef> { + static const bool flow = false; +}; +template<> struct SequenceElementTraits<std::pair<std::string, std::string>> { + static const bool flow = false; +}; + +/// Implementation of CustomMappingTraits for std::map<std::string, T>. +template <typename T> struct StdMapStringCustomMappingTraitsImpl { + using map_type = std::map<std::string, T>; + + static void inputOne(IO &io, StringRef key, map_type &v) { + io.mapRequired(key.str().c_str(), v[std::string(key)]); + } + + static void output(IO &io, map_type &v) { + for (auto &p : v) + io.mapRequired(p.first.c_str(), p.second); + } +}; + +} // end namespace yaml +} // end namespace llvm + +#define LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(TYPE, FLOW) \ + namespace llvm { \ + namespace yaml { \ + static_assert( \ + !std::is_fundamental_v<TYPE> && !std::is_same_v<TYPE, std::string> && \ + !std::is_same_v<TYPE, llvm::StringRef>, \ + "only use LLVM_YAML_IS_SEQUENCE_VECTOR for types you control"); \ + template <> struct SequenceElementTraits<TYPE> { \ + static const bool flow = FLOW; \ + }; \ + } \ + } + +/// Utility for declaring that a std::vector of a particular type +/// should be considered a YAML sequence. +#define LLVM_YAML_IS_SEQUENCE_VECTOR(type) \ + LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, false) + +/// Utility for declaring that a std::vector of a particular type +/// should be considered a YAML flow sequence. +#define LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(type) \ + LLVM_YAML_IS_SEQUENCE_VECTOR_IMPL(type, true) + +#define LLVM_YAML_DECLARE_MAPPING_TRAITS(Type) \ + namespace llvm { \ + namespace yaml { \ + template <> struct MappingTraits<Type> { \ + static void mapping(IO &IO, Type &Obj); \ + }; \ + } \ + } + +#define LLVM_YAML_DECLARE_ENUM_TRAITS(Type) \ + namespace llvm { \ + namespace yaml { \ + template <> struct ScalarEnumerationTraits<Type> { \ + static void enumeration(IO &io, Type &Value); \ + }; \ + } \ + } + +#define LLVM_YAML_DECLARE_BITSET_TRAITS(Type) \ + namespace llvm { \ + namespace yaml { \ + template <> struct ScalarBitSetTraits<Type> { \ + static void bitset(IO &IO, Type &Options); \ + }; \ + } \ + } + +#define LLVM_YAML_DECLARE_SCALAR_TRAITS(Type, MustQuote) \ + namespace llvm { \ + namespace yaml { \ + 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 QuotingType mustQuote(StringRef) { return MustQuote; } \ + }; \ + } \ + } + +/// Utility for declaring that a std::vector of a particular type +/// should be considered a YAML document list. +#define LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(_type) \ + namespace llvm { \ + namespace yaml { \ + template <unsigned N> \ + struct DocumentListTraits<SmallVector<_type, N>> \ + : public SequenceTraitsImpl<SmallVector<_type, N>, false> {}; \ + template <> \ + struct DocumentListTraits<std::vector<_type>> \ + : public SequenceTraitsImpl<std::vector<_type>, false> {}; \ + } \ + } + +/// Utility for declaring that std::map<std::string, _type> should be considered +/// a YAML map. +#define LLVM_YAML_IS_STRING_MAP(_type) \ + namespace llvm { \ + namespace yaml { \ + template <> \ + struct CustomMappingTraits<std::map<std::string, _type>> \ + : public StdMapStringCustomMappingTraitsImpl<_type> {}; \ + } \ + } + +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex64) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex32) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex16) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::Hex8) + +#endif // LLVM_SUPPORT_YAMLTRAITS_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/circular_raw_ostream.h b/contrib/libs/llvm16/include/llvm/Support/circular_raw_ostream.h new file mode 100644 index 00000000000..533e4196265 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/circular_raw_ostream.h @@ -0,0 +1,169 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/circular_raw_ostream.h - Buffered streams --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains raw_ostream implementations for streams to do circular +// buffering of their output. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CIRCULAR_RAW_OSTREAM_H +#define LLVM_SUPPORT_CIRCULAR_RAW_OSTREAM_H + +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + /// circular_raw_ostream - A raw_ostream which *can* save its data + /// to a circular buffer, or can pass it through directly to an + /// underlying stream if specified with a buffer of zero. + /// + class circular_raw_ostream : public raw_ostream { + public: + /// TAKE_OWNERSHIP - Tell this stream that it owns the underlying + /// stream and is responsible for cleanup, memory management + /// issues, etc. + /// + static constexpr bool TAKE_OWNERSHIP = true; + + /// REFERENCE_ONLY - Tell this stream it should not manage the + /// held stream. + /// + static constexpr bool REFERENCE_ONLY = false; + + private: + /// TheStream - The real stream we output to. We set it to be + /// unbuffered, since we're already doing our own buffering. + /// + raw_ostream *TheStream = nullptr; + + /// OwnsStream - Are we responsible for managing the underlying + /// stream? + /// + bool OwnsStream; + + /// BufferSize - The size of the buffer in bytes. + /// + size_t BufferSize; + + /// BufferArray - The actual buffer storage. + /// + char *BufferArray = nullptr; + + /// Cur - Pointer to the current output point in BufferArray. + /// + char *Cur; + + /// Filled - Indicate whether the buffer has been completely + /// filled. This helps avoid garbage output. + /// + bool Filled = false; + + /// Banner - A pointer to a banner to print before dumping the + /// log. + /// + const char *Banner; + + /// flushBuffer - Dump the contents of the buffer to Stream. + /// + void flushBuffer() { + if (Filled) + // Write the older portion of the buffer. + TheStream->write(Cur, BufferArray + BufferSize - Cur); + // Write the newer portion of the buffer. + TheStream->write(BufferArray, Cur - BufferArray); + Cur = BufferArray; + Filled = false; + } + + void write_impl(const char *Ptr, size_t Size) override; + + /// current_pos - Return the current position within the stream, + /// not counting the bytes currently in the buffer. + /// + uint64_t current_pos() const override { + // This has the same effect as calling TheStream.current_pos(), + // but that interface is private. + return TheStream->tell() - TheStream->GetNumBytesInBuffer(); + } + + public: + /// circular_raw_ostream - Construct an optionally + /// circular-buffered stream, handing it an underlying stream to + /// do the "real" output. + /// + /// As a side effect, if BuffSize is nonzero, the given Stream is + /// set to be Unbuffered. This is because circular_raw_ostream + /// does its own buffering, so it doesn't want another layer of + /// buffering to be happening underneath it. + /// + /// "Owns" tells the circular_raw_ostream whether it is + /// responsible for managing the held stream, doing memory + /// management of it, etc. + /// + circular_raw_ostream(raw_ostream &Stream, const char *Header, + size_t BuffSize = 0, bool Owns = REFERENCE_ONLY) + : raw_ostream(/*unbuffered*/ true), OwnsStream(Owns), + BufferSize(BuffSize), Banner(Header) { + if (BufferSize != 0) + BufferArray = new char[BufferSize]; + Cur = BufferArray; + setStream(Stream, Owns); + } + + ~circular_raw_ostream() override { + flush(); + flushBufferWithBanner(); + releaseStream(); + delete[] BufferArray; + } + + bool is_displayed() const override { + return TheStream->is_displayed(); + } + + /// setStream - Tell the circular_raw_ostream to output a + /// different stream. "Owns" tells circular_raw_ostream whether + /// it should take responsibility for managing the underlying + /// stream. + /// + void setStream(raw_ostream &Stream, bool Owns = REFERENCE_ONLY) { + releaseStream(); + TheStream = &Stream; + OwnsStream = Owns; + } + + /// flushBufferWithBanner - Force output of the buffer along with + /// a small header. + /// + void flushBufferWithBanner(); + + private: + /// releaseStream - Delete the held stream if needed. Otherwise, + /// transfer the buffer settings from this circular_raw_ostream + /// back to the underlying stream. + /// + void releaseStream() { + if (!TheStream) + return; + if (OwnsStream) + delete TheStream; + } + }; +} // end llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/raw_os_ostream.h b/contrib/libs/llvm16/include/llvm/Support/raw_os_ostream.h new file mode 100644 index 00000000000..f1ecb4949b5 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/raw_os_ostream.h @@ -0,0 +1,52 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- raw_os_ostream.h - std::ostream adaptor for raw_ostream --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the raw_os_ostream class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RAW_OS_OSTREAM_H +#define LLVM_SUPPORT_RAW_OS_OSTREAM_H + +#include "llvm/Support/raw_ostream.h" +#include <iosfwd> + +namespace llvm { + +/// raw_os_ostream - A raw_ostream that writes to an std::ostream. This is a +/// simple adaptor class. It does not check for output errors; clients should +/// use the underlying stream to detect errors. +class raw_os_ostream : public raw_ostream { + std::ostream &OS; + + /// write_impl - See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + + /// current_pos - Return the current position within the stream, not + /// counting the bytes currently in the buffer. + uint64_t current_pos() const override; + +public: + raw_os_ostream(std::ostream &O) : OS(O) {} + ~raw_os_ostream() override; +}; + +} // end llvm namespace + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/raw_ostream.h b/contrib/libs/llvm16/include/llvm/Support/raw_ostream.h new file mode 100644 index 00000000000..22a8cc140c7 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/raw_ostream.h @@ -0,0 +1,782 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===--- raw_ostream.h - Raw output stream ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the raw_ostream class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RAW_OSTREAM_H +#define LLVM_SUPPORT_RAW_OSTREAM_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <optional> +#include <string> +#include <string_view> +#include <system_error> +#include <type_traits> + +namespace llvm { + +class Duration; +class formatv_object_base; +class format_object_base; +class FormattedString; +class FormattedNumber; +class FormattedBytes; +template <class T> class [[nodiscard]] Expected; + +namespace sys { +namespace fs { +enum FileAccess : unsigned; +enum OpenFlags : unsigned; +enum CreationDisposition : unsigned; +class FileLocker; +} // end namespace fs +} // end namespace sys + +/// This class implements an extremely fast bulk output stream that can *only* +/// output to a stream. It does not support seeking, reopening, rewinding, line +/// buffered disciplines etc. It is a simple buffer that outputs +/// a chunk at a time. +class raw_ostream { +public: + // Class kinds to support LLVM-style RTTI. + enum class OStreamKind { + OK_OStream, + OK_FDStream, + }; + +private: + OStreamKind Kind; + + /// The buffer is handled in such a way that the buffer is + /// uninitialized, unbuffered, or out of space when OutBufCur >= + /// OutBufEnd. Thus a single comparison suffices to determine if we + /// need to take the slow path to write a single character. + /// + /// The buffer is in one of three states: + /// 1. Unbuffered (BufferMode == Unbuffered) + /// 1. Uninitialized (BufferMode != Unbuffered && OutBufStart == 0). + /// 2. Buffered (BufferMode != Unbuffered && OutBufStart != 0 && + /// OutBufEnd - OutBufStart >= 1). + /// + /// If buffered, then the raw_ostream owns the buffer if (BufferMode == + /// InternalBuffer); otherwise the buffer has been set via SetBuffer and is + /// managed by the subclass. + /// + /// If a subclass installs an external buffer using SetBuffer then it can wait + /// for a \see write_impl() call to handle the data which has been put into + /// this buffer. + char *OutBufStart, *OutBufEnd, *OutBufCur; + bool ColorEnabled = false; + + /// Optional stream this stream is tied to. If this stream is written to, the + /// tied-to stream will be flushed first. + raw_ostream *TiedStream = nullptr; + + enum class BufferKind { + Unbuffered = 0, + InternalBuffer, + ExternalBuffer + } BufferMode; + +public: + // color order matches ANSI escape sequence, don't change + enum class Colors { + BLACK = 0, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + WHITE, + SAVEDCOLOR, + RESET, + }; + + static constexpr Colors BLACK = Colors::BLACK; + static constexpr Colors RED = Colors::RED; + static constexpr Colors GREEN = Colors::GREEN; + static constexpr Colors YELLOW = Colors::YELLOW; + static constexpr Colors BLUE = Colors::BLUE; + static constexpr Colors MAGENTA = Colors::MAGENTA; + static constexpr Colors CYAN = Colors::CYAN; + static constexpr Colors WHITE = Colors::WHITE; + static constexpr Colors SAVEDCOLOR = Colors::SAVEDCOLOR; + static constexpr Colors RESET = Colors::RESET; + + explicit raw_ostream(bool unbuffered = false, + OStreamKind K = OStreamKind::OK_OStream) + : Kind(K), BufferMode(unbuffered ? BufferKind::Unbuffered + : BufferKind::InternalBuffer) { + // Start out ready to flush. + OutBufStart = OutBufEnd = OutBufCur = nullptr; + } + + raw_ostream(const raw_ostream &) = delete; + void operator=(const raw_ostream &) = delete; + + virtual ~raw_ostream(); + + /// tell - Return the current offset with the file. + uint64_t tell() const { return current_pos() + GetNumBytesInBuffer(); } + + OStreamKind get_kind() const { return Kind; } + + //===--------------------------------------------------------------------===// + // Configuration Interface + //===--------------------------------------------------------------------===// + + /// If possible, pre-allocate \p ExtraSize bytes for stream data. + /// i.e. it extends internal buffers to keep additional ExtraSize bytes. + /// So that the stream could keep at least tell() + ExtraSize bytes + /// without re-allocations. reserveExtraSpace() does not change + /// the size/data of the stream. + virtual void reserveExtraSpace(uint64_t ExtraSize) {} + + /// Set the stream to be buffered, with an automatically determined buffer + /// size. + void SetBuffered(); + + /// Set the stream to be buffered, using the specified buffer size. + void SetBufferSize(size_t Size) { + flush(); + SetBufferAndMode(new char[Size], Size, BufferKind::InternalBuffer); + } + + size_t GetBufferSize() const { + // If we're supposed to be buffered but haven't actually gotten around + // to allocating the buffer yet, return the value that would be used. + if (BufferMode != BufferKind::Unbuffered && OutBufStart == nullptr) + return preferred_buffer_size(); + + // Otherwise just return the size of the allocated buffer. + return OutBufEnd - OutBufStart; + } + + /// Set the stream to be unbuffered. When unbuffered, the stream will flush + /// after every write. This routine will also flush the buffer immediately + /// when the stream is being set to unbuffered. + void SetUnbuffered() { + flush(); + SetBufferAndMode(nullptr, 0, BufferKind::Unbuffered); + } + + size_t GetNumBytesInBuffer() const { + return OutBufCur - OutBufStart; + } + + //===--------------------------------------------------------------------===// + // Data Output Interface + //===--------------------------------------------------------------------===// + + void flush() { + if (OutBufCur != OutBufStart) + flush_nonempty(); + } + + raw_ostream &operator<<(char C) { + if (OutBufCur >= OutBufEnd) + return write(C); + *OutBufCur++ = C; + return *this; + } + + raw_ostream &operator<<(unsigned char C) { + if (OutBufCur >= OutBufEnd) + return write(C); + *OutBufCur++ = C; + return *this; + } + + raw_ostream &operator<<(signed char C) { + if (OutBufCur >= OutBufEnd) + return write(C); + *OutBufCur++ = C; + return *this; + } + + raw_ostream &operator<<(StringRef Str) { + // Inline fast path, particularly for strings with a known length. + size_t Size = Str.size(); + + // Make sure we can use the fast path. + if (Size > (size_t)(OutBufEnd - OutBufCur)) + return write(Str.data(), Size); + + if (Size) { + memcpy(OutBufCur, Str.data(), Size); + OutBufCur += Size; + } + return *this; + } + +#if defined(__cpp_char8_t) + // When using `char8_t *` integers or pointers are written to the ostream + // instead of UTF-8 code as one might expect. This might lead to unexpected + // behavior, especially as `u8""` literals are of type `char8_t*` instead of + // type `char_t*` from C++20 onwards. Thus we disallow using them with + // raw_ostreams. + // If you have u8"" literals to stream, you can rewrite them as ordinary + // literals with escape sequences + // e.g. replace `u8"\u00a0"` by `"\xc2\xa0"` + // or use `reinterpret_cast`: + // e.g. replace `u8"\u00a0"` by `reinterpret_cast<const char *>(u8"\u00a0")` + raw_ostream &operator<<(const char8_t *Str) = delete; +#endif + + raw_ostream &operator<<(const char *Str) { + // Inline fast path, particularly for constant strings where a sufficiently + // smart compiler will simplify strlen. + + return this->operator<<(StringRef(Str)); + } + + raw_ostream &operator<<(const std::string &Str) { + // Avoid the fast path, it would only increase code size for a marginal win. + return write(Str.data(), Str.length()); + } + + raw_ostream &operator<<(const std::string_view &Str) { + return write(Str.data(), Str.length()); + } + + raw_ostream &operator<<(const SmallVectorImpl<char> &Str) { + return write(Str.data(), Str.size()); + } + + raw_ostream &operator<<(unsigned long N); + raw_ostream &operator<<(long N); + raw_ostream &operator<<(unsigned long long N); + raw_ostream &operator<<(long long N); + raw_ostream &operator<<(const void *P); + + raw_ostream &operator<<(unsigned int N) { + return this->operator<<(static_cast<unsigned long>(N)); + } + + raw_ostream &operator<<(int N) { + return this->operator<<(static_cast<long>(N)); + } + + raw_ostream &operator<<(double N); + + /// Output \p N in hexadecimal, without any prefix or padding. + raw_ostream &write_hex(unsigned long long N); + + // Change the foreground color of text. + raw_ostream &operator<<(Colors C); + + /// 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 llvm::isPrint into an escape sequence. + raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false); + + raw_ostream &write(unsigned char C); + raw_ostream &write(const char *Ptr, size_t Size); + + // Formatted output, see the format() function in Support/Format.h. + raw_ostream &operator<<(const format_object_base &Fmt); + + // Formatted output, see the leftJustify() function in Support/Format.h. + raw_ostream &operator<<(const FormattedString &); + + // Formatted output, see the formatHex() function in Support/Format.h. + raw_ostream &operator<<(const FormattedNumber &); + + // Formatted output, see the formatv() function in Support/FormatVariadic.h. + raw_ostream &operator<<(const formatv_object_base &); + + // Formatted output, see the format_bytes() function in Support/Format.h. + raw_ostream &operator<<(const FormattedBytes &); + + /// indent - Insert 'NumSpaces' spaces. + raw_ostream &indent(unsigned NumSpaces); + + /// write_zeros - Insert 'NumZeros' nulls. + raw_ostream &write_zeros(unsigned NumZeros); + + /// Changes the foreground color of text that will be output from this point + /// forward. + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold bold/brighter text, default false + /// @param BG if true change the background, default: change foreground + /// @returns itself so it can be used within << invocations + virtual raw_ostream &changeColor(enum Colors Color, bool Bold = false, + bool BG = false); + + /// Resets the colors to terminal defaults. Call this when you are done + /// outputting colored text, or before program exit. + virtual raw_ostream &resetColor(); + + /// Reverses the foreground and background colors. + virtual raw_ostream &reverseColor(); + + /// This function determines if this stream is connected to a "tty" or + /// "console" window. That is, the output would be displayed to the user + /// rather than being put on a pipe or stored in a file. + virtual bool is_displayed() const { return false; } + + /// This function determines if this stream is displayed and supports colors. + /// The result is unaffected by calls to enable_color(). + virtual bool has_colors() const { return is_displayed(); } + + // Enable or disable colors. Once enable_colors(false) is called, + // changeColor() has no effect until enable_colors(true) is called. + virtual void enable_colors(bool enable) { ColorEnabled = enable; } + + bool colors_enabled() const { return ColorEnabled; } + + /// Tie this stream to the specified stream. Replaces any existing tied-to + /// stream. Specifying a nullptr unties the stream. + void tie(raw_ostream *TieTo) { TiedStream = TieTo; } + + //===--------------------------------------------------------------------===// + // Subclass Interface + //===--------------------------------------------------------------------===// + +private: + /// The is the piece of the class that is implemented by subclasses. This + /// writes the \p Size bytes starting at + /// \p Ptr to the underlying stream. + /// + /// This function is guaranteed to only be called at a point at which it is + /// safe for the subclass to install a new buffer via SetBuffer. + /// + /// \param Ptr The start of the data to be written. For buffered streams this + /// is guaranteed to be the start of the buffer. + /// + /// \param Size The number of bytes to be written. + /// + /// \invariant { Size > 0 } + virtual void write_impl(const char *Ptr, size_t Size) = 0; + + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + virtual uint64_t current_pos() const = 0; + +protected: + /// Use the provided buffer as the raw_ostream buffer. This is intended for + /// use only by subclasses which can arrange for the output to go directly + /// into the desired output buffer, instead of being copied on each flush. + void SetBuffer(char *BufferStart, size_t Size) { + SetBufferAndMode(BufferStart, Size, BufferKind::ExternalBuffer); + } + + /// Return an efficient buffer size for the underlying output mechanism. + virtual size_t preferred_buffer_size() const; + + /// Return the beginning of the current stream buffer, or 0 if the stream is + /// unbuffered. + const char *getBufferStart() const { return OutBufStart; } + + //===--------------------------------------------------------------------===// + // Private Interface + //===--------------------------------------------------------------------===// +private: + /// Install the given buffer and mode. + void SetBufferAndMode(char *BufferStart, size_t Size, BufferKind Mode); + + /// Flush the current buffer, which is known to be non-empty. This outputs the + /// currently buffered data and resets the buffer to empty. + void flush_nonempty(); + + /// Copy data into the buffer. Size must not be greater than the number of + /// unused bytes in the buffer. + void copy_to_buffer(const char *Ptr, size_t Size); + + /// Compute whether colors should be used and do the necessary work such as + /// flushing. The result is affected by calls to enable_color(). + bool prepare_colors(); + + /// Flush the tied-to stream (if present) and then write the required data. + void flush_tied_then_write(const char *Ptr, size_t Size); + + virtual void anchor(); +}; + +/// Call the appropriate insertion operator, given an rvalue reference to a +/// raw_ostream object and return a stream of the same type as the argument. +template <typename OStream, typename T> +std::enable_if_t<!std::is_reference<OStream>::value && + std::is_base_of<raw_ostream, OStream>::value, + OStream &&> +operator<<(OStream &&OS, const T &Value) { + OS << Value; + return std::move(OS); +} + +/// An abstract base class for streams implementations that also support a +/// pwrite operation. This is useful for code that can mostly stream out data, +/// but needs to patch in a header that needs to know the output size. +class raw_pwrite_stream : public raw_ostream { + virtual void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) = 0; + void anchor() override; + +public: + explicit raw_pwrite_stream(bool Unbuffered = false, + OStreamKind K = OStreamKind::OK_OStream) + : raw_ostream(Unbuffered, K) {} + void pwrite(const char *Ptr, size_t Size, uint64_t Offset) { +#ifndef NDEBUG + uint64_t Pos = tell(); + // /dev/null always reports a pos of 0, so we cannot perform this check + // in that case. + if (Pos) + assert(Size + Offset <= Pos && "We don't support extending the stream"); +#endif + pwrite_impl(Ptr, Size, Offset); + } +}; + +//===----------------------------------------------------------------------===// +// File Output Streams +//===----------------------------------------------------------------------===// + +/// A raw_ostream that writes to a file descriptor. +/// +class raw_fd_ostream : public raw_pwrite_stream { + int FD; + bool ShouldClose; + bool SupportsSeeking = false; + bool IsRegularFile = false; + mutable std::optional<bool> HasColors; + +#ifdef _WIN32 + /// True if this fd refers to a Windows console device. Mintty and other + /// terminal emulators are TTYs, but they are not consoles. + bool IsWindowsConsole = false; +#endif + + std::error_code EC; + + uint64_t pos = 0; + + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + + void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; + + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + uint64_t current_pos() const override { return pos; } + + /// Determine an efficient buffer size. + size_t preferred_buffer_size() const override; + + void anchor() override; + +protected: + /// Set the flag indicating that an output error has been encountered. + void error_detected(std::error_code EC) { this->EC = EC; } + + /// Return the file descriptor. + int get_fd() const { return FD; } + + // Update the file position by increasing \p Delta. + void inc_pos(uint64_t Delta) { pos += Delta; } + +public: + /// Open the specified file for writing. If an error occurs, information + /// about the error is put into EC, and the stream should be immediately + /// destroyed; + /// \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. This will not close the stdout + /// descriptor. + raw_fd_ostream(StringRef Filename, std::error_code &EC); + raw_fd_ostream(StringRef Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp); + raw_fd_ostream(StringRef Filename, std::error_code &EC, + sys::fs::FileAccess Access); + raw_fd_ostream(StringRef Filename, std::error_code &EC, + sys::fs::OpenFlags Flags); + raw_fd_ostream(StringRef Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access, + 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. If FD is for stdout or + /// stderr, it will not be closed. + raw_fd_ostream(int fd, bool shouldClose, bool unbuffered = false, + OStreamKind K = OStreamKind::OK_OStream); + + ~raw_fd_ostream() override; + + /// Manually flush the stream and close the file. Note that this does not call + /// fsync. + void close(); + + bool supportsSeeking() const { return SupportsSeeking; } + + bool isRegularFile() const { return IsRegularFile; } + + /// Flushes the stream and repositions the underlying file descriptor position + /// to the offset specified from the beginning of the file. + uint64_t seek(uint64_t off); + + bool is_displayed() const override; + + 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 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 + /// called to report the error. Use clear_error() after handling the error to + /// avoid this behavior. + /// + /// "Errors should never pass silently. + /// Unless explicitly silenced." + /// - from The Zen of Python, by Tim Peters + /// + void clear_error() { EC = std::error_code(); } + + /// Locks the underlying file. + /// + /// @returns RAII object that releases the lock upon leaving the scope, if the + /// locking was successful. Otherwise returns corresponding + /// error code. + /// + /// The function blocks the current thread until the lock become available or + /// error occurs. + /// + /// Possible use of this function may be as follows: + /// + /// @code{.cpp} + /// if (auto L = stream.lock()) { + /// // ... do action that require file to be locked. + /// } else { + /// handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) { + /// // ... handle lock error. + /// }); + /// } + /// @endcode + [[nodiscard]] Expected<sys::fs::FileLocker> lock(); + + /// Tries to lock the underlying file within the specified period. + /// + /// @returns RAII object that releases the lock upon leaving the scope, if the + /// locking was successful. Otherwise returns corresponding + /// error code. + /// + /// It is used as @ref lock. + [[nodiscard]] Expected<sys::fs::FileLocker> + tryLockFor(Duration const &Timeout); +}; + +/// This returns a reference to a raw_fd_ostream for standard output. Use it +/// like: outs() << "foo" << "bar"; +raw_fd_ostream &outs(); + +/// This returns a reference to a raw_ostream for standard error. +/// Use it like: errs() << "foo" << "bar"; +/// By default, the stream is tied to stdout to ensure stdout is flushed before +/// stderr is written, to ensure the error messages are written in their +/// expected place. +raw_fd_ostream &errs(); + +/// This returns a reference to a raw_ostream which simply discards output. +raw_ostream &nulls(); + +//===----------------------------------------------------------------------===// +// File Streams +//===----------------------------------------------------------------------===// + +/// A raw_ostream of a file for reading/writing/seeking. +/// +class raw_fd_stream : public raw_fd_ostream { +public: + /// Open the specified file for reading/writing/seeking. If an error occurs, + /// information about the error is put into EC, and the stream should be + /// immediately destroyed. + raw_fd_stream(StringRef Filename, std::error_code &EC); + + /// This reads the \p Size bytes into a buffer pointed by \p Ptr. + /// + /// \param Ptr The start of the buffer to hold data to be read. + /// + /// \param Size The number of bytes to be read. + /// + /// On success, the number of bytes read is returned, and the file position is + /// advanced by this number. On error, -1 is returned, use error() to get the + /// error code. + ssize_t read(char *Ptr, size_t Size); + + /// Check if \p OS is a pointer of type raw_fd_stream*. + static bool classof(const raw_ostream *OS); +}; + +//===----------------------------------------------------------------------===// +// Output Stream Adaptors +//===----------------------------------------------------------------------===// + +/// A raw_ostream that writes to an std::string. This is a simple adaptor +/// class. This class does not encounter output errors. +/// raw_string_ostream operates without a buffer, delegating all memory +/// management to the std::string. Thus the std::string is always up-to-date, +/// may be used directly and there is no need to call flush(). +class raw_string_ostream : public raw_ostream { + std::string &OS; + + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + uint64_t current_pos() const override { return OS.size(); } + +public: + explicit raw_string_ostream(std::string &O) : OS(O) { + SetUnbuffered(); + } + + /// Returns the string's reference. In most cases it is better to simply use + /// the underlying std::string directly. + /// TODO: Consider removing this API. + std::string &str() { return OS; } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserve(tell() + ExtraSize); + } +}; + +/// A raw_ostream that writes to an SmallVector or SmallString. This is a +/// simple adaptor class. This class does not encounter output errors. +/// raw_svector_ostream operates without a buffer, delegating all memory +/// management to the SmallString. Thus the SmallString is always up-to-date, +/// may be used directly and there is no need to call flush(). +class raw_svector_ostream : public raw_pwrite_stream { + SmallVectorImpl<char> &OS; + + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override; + + void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; + + /// Return the current position within the stream. + uint64_t current_pos() const override; + +public: + /// Construct a new raw_svector_ostream. + /// + /// \param O The vector to write to; this should generally have at least 128 + /// bytes free to avoid any extraneous memory overhead. + explicit raw_svector_ostream(SmallVectorImpl<char> &O) : OS(O) { + SetUnbuffered(); + } + + ~raw_svector_ostream() override = default; + + void flush() = delete; + + /// Return a StringRef for the vector contents. + StringRef str() const { return StringRef(OS.data(), OS.size()); } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserve(tell() + ExtraSize); + } +}; + +/// A raw_ostream that discards all output. +class raw_null_ostream : public raw_pwrite_stream { + /// See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t size) override; + void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; + + /// Return the current position within the stream, not counting the bytes + /// currently in the buffer. + uint64_t current_pos() const override; + +public: + explicit raw_null_ostream() = default; + ~raw_null_ostream() override; +}; + +class buffer_ostream : public raw_svector_ostream { + raw_ostream &OS; + SmallVector<char, 0> Buffer; + + void anchor() override; + +public: + buffer_ostream(raw_ostream &OS) : raw_svector_ostream(Buffer), OS(OS) {} + ~buffer_ostream() override { OS << str(); } +}; + +class buffer_unique_ostream : public raw_svector_ostream { + std::unique_ptr<raw_ostream> OS; + SmallVector<char, 0> Buffer; + + void anchor() override; + +public: + buffer_unique_ostream(std::unique_ptr<raw_ostream> OS) + : raw_svector_ostream(Buffer), OS(std::move(OS)) { + // Turn off buffering on OS, which we now own, to avoid allocating a buffer + // when the destructor writes only to be immediately flushed again. + this->OS->SetUnbuffered(); + } + ~buffer_unique_ostream() override { *OS << str(); } +}; + +class Error; + +/// This helper creates an output stream and then passes it to \p Write. +/// The stream created is based on the specified \p OutputFileName: +/// llvm::outs for "-", raw_null_ostream for "/dev/null", and raw_fd_ostream +/// for other names. For raw_fd_ostream instances, the stream writes to +/// a temporary file. The final output file is atomically replaced with the +/// temporary file after the \p Write function is finished. +Error writeToOutput(StringRef OutputFileName, + std::function<Error(raw_ostream &)> Write); + +raw_ostream &operator<<(raw_ostream &OS, std::nullopt_t); + +template <typename T, typename = decltype(std::declval<raw_ostream &>() + << std::declval<const T &>())> +raw_ostream &operator<<(raw_ostream &OS, const std::optional<T> &O) { + if (O) + OS << *O; + else + OS << std::nullopt; + return OS; +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_RAW_OSTREAM_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/thread.h b/contrib/libs/llvm16/include/llvm/Support/thread.h new file mode 100644 index 00000000000..8302a69512d --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/thread.h @@ -0,0 +1,255 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===-- llvm/Support/thread.h - Wrapper for <thread> ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header is a wrapper for <thread> that works around problems with the +// MSVC headers when exceptions are disabled. It also provides llvm::thread, +// which is either a typedef of std::thread or a replacement that calls the +// function synchronously depending on the value of LLVM_ENABLE_THREADS. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_THREAD_H +#define LLVM_SUPPORT_THREAD_H + +#include "llvm/Config/llvm-config.h" +#include <optional> + +#ifdef _WIN32 +typedef unsigned long DWORD; +typedef void *PVOID; +typedef PVOID HANDLE; +#endif + +#if LLVM_ENABLE_THREADS + +#include <thread> + +namespace llvm { + +#if LLVM_ON_UNIX || _WIN32 + +/// LLVM thread following std::thread interface with added constructor to +/// specify stack size. +class thread { + template <typename CalleeTuple> static void GenericThreadProxy(void *Ptr) { + std::unique_ptr<CalleeTuple> Callee(static_cast<CalleeTuple *>(Ptr)); + std::apply( + [](auto &&F, auto &&...Args) { + std::forward<decltype(F)>(F)(std::forward<decltype(Args)>(Args)...); + }, + *Callee); + } + +public: +#if LLVM_ON_UNIX + using native_handle_type = pthread_t; + using id = pthread_t; + using start_routine_type = void *(*)(void *); + + template <typename CalleeTuple> static void *ThreadProxy(void *Ptr) { + GenericThreadProxy<CalleeTuple>(Ptr); + return nullptr; + } +#elif _WIN32 + using native_handle_type = HANDLE; + using id = DWORD; + using start_routine_type = unsigned(__stdcall *)(void *); + + template <typename CalleeTuple> + static unsigned __stdcall ThreadProxy(void *Ptr) { + GenericThreadProxy<CalleeTuple>(Ptr); + return 0; + } +#endif + + static const std::optional<unsigned> DefaultStackSize; + + thread() : Thread(native_handle_type()) {} + thread(thread &&Other) noexcept + : Thread(std::exchange(Other.Thread, native_handle_type())) {} + + template <class Function, class... Args> + explicit thread(Function &&f, Args &&...args) + : thread(DefaultStackSize, f, args...) {} + + template <class Function, class... Args> + explicit thread(std::optional<unsigned> StackSizeInBytes, Function &&f, + Args &&...args); + thread(const thread &) = delete; + + ~thread() { + if (joinable()) + std::terminate(); + } + + thread &operator=(thread &&Other) noexcept { + if (joinable()) + std::terminate(); + Thread = std::exchange(Other.Thread, native_handle_type()); + return *this; + } + + bool joinable() const noexcept { return Thread != native_handle_type(); } + + inline id get_id() const noexcept; + + native_handle_type native_handle() const noexcept { return Thread; } + + static unsigned hardware_concurrency() { + return std::thread::hardware_concurrency(); + }; + + inline void join(); + inline void detach(); + + void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); } + +private: + native_handle_type Thread; +}; + +thread::native_handle_type +llvm_execute_on_thread_impl(thread::start_routine_type ThreadFunc, void *Arg, + std::optional<unsigned> StackSizeInBytes); +void llvm_thread_join_impl(thread::native_handle_type Thread); +void llvm_thread_detach_impl(thread::native_handle_type Thread); +thread::id llvm_thread_get_id_impl(thread::native_handle_type Thread); +thread::id llvm_thread_get_current_id_impl(); + +template <class Function, class... Args> +thread::thread(std::optional<unsigned> StackSizeInBytes, Function &&f, + Args &&...args) { + typedef std::tuple<std::decay_t<Function>, std::decay_t<Args>...> CalleeTuple; + std::unique_ptr<CalleeTuple> Callee( + new CalleeTuple(std::forward<Function>(f), std::forward<Args>(args)...)); + + Thread = llvm_execute_on_thread_impl(ThreadProxy<CalleeTuple>, Callee.get(), + StackSizeInBytes); + if (Thread != native_handle_type()) + Callee.release(); +} + +thread::id thread::get_id() const noexcept { + return llvm_thread_get_id_impl(Thread); +} + +void thread::join() { + llvm_thread_join_impl(Thread); + Thread = native_handle_type(); +} + +void thread::detach() { + llvm_thread_detach_impl(Thread); + Thread = native_handle_type(); +} + +namespace this_thread { +inline thread::id get_id() { return llvm_thread_get_current_id_impl(); } +} // namespace this_thread + +#else // !LLVM_ON_UNIX && !_WIN32 + +/// std::thread backed implementation of llvm::thread interface that ignores the +/// stack size request. +class thread { +public: + using native_handle_type = std::thread::native_handle_type; + using id = std::thread::id; + + thread() : Thread(std::thread()) {} + thread(thread &&Other) noexcept + : Thread(std::exchange(Other.Thread, std::thread())) {} + + template <class Function, class... Args> + explicit thread(std::optional<unsigned> StackSizeInBytes, Function &&f, + Args &&...args) + : Thread(std::forward<Function>(f), std::forward<Args>(args)...) {} + + template <class Function, class... Args> + explicit thread(Function &&f, Args &&...args) : Thread(f, args...) {} + + thread(const thread &) = delete; + + ~thread() {} + + thread &operator=(thread &&Other) noexcept { + Thread = std::exchange(Other.Thread, std::thread()); + return *this; + } + + bool joinable() const noexcept { return Thread.joinable(); } + + id get_id() const noexcept { return Thread.get_id(); } + + native_handle_type native_handle() noexcept { return Thread.native_handle(); } + + static unsigned hardware_concurrency() { + return std::thread::hardware_concurrency(); + }; + + inline void join() { Thread.join(); } + inline void detach() { Thread.detach(); } + + void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); } + +private: + std::thread Thread; +}; + +namespace this_thread { + inline thread::id get_id() { return std::this_thread::get_id(); } +} + +#endif // LLVM_ON_UNIX || _WIN32 + +} // namespace llvm + +#else // !LLVM_ENABLE_THREADS + +#include <utility> + +namespace llvm { + +struct thread { + thread() {} + thread(thread &&other) {} + template <class Function, class... Args> + explicit thread(std::optional<unsigned> StackSizeInBytes, Function &&f, + Args &&...args) { + f(std::forward<Args>(args)...); + } + template <class Function, class... Args> + explicit thread(Function &&f, Args &&...args) { + f(std::forward<Args>(args)...); + } + thread(const thread &) = delete; + + void detach() { + report_fatal_error("Detaching from a thread does not make sense with no " + "threading support"); + } + void join() {} + static unsigned hardware_concurrency() { return 1; }; +}; + +} // namespace llvm + +#endif // LLVM_ENABLE_THREADS + +#endif // LLVM_SUPPORT_THREAD_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/type_traits.h b/contrib/libs/llvm16/include/llvm/Support/type_traits.h new file mode 100644 index 00000000000..5c80f0b158e --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/type_traits.h @@ -0,0 +1,147 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +//===- llvm/Support/type_traits.h - Simplfied type traits -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides useful additions to the standard type_traits library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_TYPE_TRAITS_H +#define LLVM_SUPPORT_TYPE_TRAITS_H + +#include "llvm/Support/Compiler.h" +#include <type_traits> +#include <utility> + +namespace llvm { + + +/// Metafunction that determines whether the given type is either an +/// integral type or an enumeration type, including enum classes. +/// +/// Note that this accepts potentially more integral types than is_integral +/// because it is based on being implicitly convertible to an integral type. +/// Also note that enum classes aren't implicitly convertible to integral types, +/// the value may therefore need to be explicitly converted before being used. +template <typename T> class is_integral_or_enum { + using UnderlyingT = std::remove_reference_t<T>; + +public: + static const bool value = + !std::is_class<UnderlyingT>::value && // Filter conversion operators. + !std::is_pointer<UnderlyingT>::value && + !std::is_floating_point<UnderlyingT>::value && + (std::is_enum<UnderlyingT>::value || + std::is_convertible<UnderlyingT, unsigned long long>::value); +}; + +/// If T is a pointer, just return it. If it is not, return T&. +template<typename T, typename Enable = void> +struct add_lvalue_reference_if_not_pointer { using type = T &; }; + +template <typename T> +struct add_lvalue_reference_if_not_pointer< + T, std::enable_if_t<std::is_pointer<T>::value>> { + using type = T; +}; + +/// If T is a pointer to X, return a pointer to const X. If it is not, +/// return const T. +template<typename T, typename Enable = void> +struct add_const_past_pointer { using type = const T; }; + +template <typename T> +struct add_const_past_pointer<T, std::enable_if_t<std::is_pointer<T>::value>> { + using type = const std::remove_pointer_t<T> *; +}; + +template <typename T, typename Enable = void> +struct const_pointer_or_const_ref { + using type = const T &; +}; +template <typename T> +struct const_pointer_or_const_ref<T, + std::enable_if_t<std::is_pointer<T>::value>> { + using type = typename add_const_past_pointer<T>::type; +}; + +namespace detail { +/// Internal utility to detect trivial copy construction. +template<typename T> union copy_construction_triviality_helper { + T t; + copy_construction_triviality_helper() = default; + copy_construction_triviality_helper(const copy_construction_triviality_helper&) = default; + ~copy_construction_triviality_helper() = default; +}; +/// Internal utility to detect trivial move construction. +template<typename T> union move_construction_triviality_helper { + T t; + move_construction_triviality_helper() = default; + move_construction_triviality_helper(move_construction_triviality_helper&&) = default; + ~move_construction_triviality_helper() = default; +}; + +template<class T> +union trivial_helper { + T t; +}; + +} // end namespace detail + +/// An implementation of `std::is_trivially_copy_constructible` since we have +/// users with STLs that don't yet include it. +template <typename T> +struct is_trivially_copy_constructible + : std::is_copy_constructible< + ::llvm::detail::copy_construction_triviality_helper<T>> {}; +template <typename T> +struct is_trivially_copy_constructible<T &> : std::true_type {}; +template <typename T> +struct is_trivially_copy_constructible<T &&> : std::false_type {}; + +/// An implementation of `std::is_trivially_move_constructible` since we have +/// users with STLs that don't yet include it. +template <typename T> +struct is_trivially_move_constructible + : std::is_move_constructible< + ::llvm::detail::move_construction_triviality_helper<T>> {}; +template <typename T> +struct is_trivially_move_constructible<T &> : std::true_type {}; +template <typename T> +struct is_trivially_move_constructible<T &&> : std::true_type {}; + + +template <typename T> +struct is_copy_assignable { + template<class F> + static auto get(F*) -> decltype(std::declval<F &>() = std::declval<const F &>(), std::true_type{}); + static std::false_type get(...); + static constexpr bool value = decltype(get((T*)nullptr))::value; +}; + +template <typename T> +struct is_move_assignable { + template<class F> + static auto get(F*) -> decltype(std::declval<F &>() = std::declval<F &&>(), std::true_type{}); + static std::false_type get(...); + static constexpr bool value = decltype(get((T*)nullptr))::value; +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_TYPE_TRAITS_H + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/contrib/libs/llvm16/include/llvm/Support/xxhash.h b/contrib/libs/llvm16/include/llvm/Support/xxhash.h new file mode 100644 index 00000000000..94811149284 --- /dev/null +++ b/contrib/libs/llvm16/include/llvm/Support/xxhash.h @@ -0,0 +1,60 @@ +#pragma once + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* based on revision d2df04efcbef7d7f6886d345861e5dfda4edacc1 Removed + * everything but a simple interface for computing XXh64. */ + +#ifndef LLVM_SUPPORT_XXHASH_H +#define LLVM_SUPPORT_XXHASH_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { +uint64_t xxHash64(llvm::StringRef Data); +uint64_t xxHash64(llvm::ArrayRef<uint8_t> Data); +} + +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif |