aboutsummaryrefslogtreecommitdiff
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/docs/ClangLinkerWrapper.rst8
-rw-r--r--clang/docs/ClangOffloadPackager.rst193
-rw-r--r--clang/docs/OpenMPSupport.rst2
-rw-r--r--clang/docs/ReleaseNotes.rst2
-rw-r--r--clang/docs/index.rst1
-rw-r--r--clang/include/clang/AST/HLSLResource.h5
-rw-r--r--clang/include/clang/Analysis/Analyses/LifetimeSafety.h24
-rw-r--r--clang/include/clang/Basic/SanitizerSpecialCaseList.h6
-rw-r--r--clang/include/clang/CIR/Dialect/IR/CIRAttrs.td57
-rw-r--r--clang/include/clang/CIR/Dialect/IR/CIROps.td156
-rw-r--r--clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td22
-rw-r--r--clang/include/clang/Sema/Overload.h10
-rw-r--r--clang/lib/AST/ByteCode/InterpBuiltin.cpp12
-rw-r--r--clang/lib/AST/DeclPrinter.cpp14
-rw-r--r--clang/lib/Analysis/LifetimeSafety.cpp505
-rw-r--r--clang/lib/Analysis/LifetimeSafety.md230
-rw-r--r--clang/lib/Basic/Diagnostic.cpp35
-rw-r--r--clang/lib/Basic/SanitizerSpecialCaseList.cpp11
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp18
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCXXABI.h16
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCleanup.h9
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenException.cpp35
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp132
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp247
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenFunction.h7
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp54
-rw-r--r--clang/lib/CIR/CodeGen/EHScopeStack.h4
-rw-r--r--clang/lib/CIR/Dialect/IR/CIRAttrs.cpp43
-rw-r--r--clang/lib/CIR/Dialect/IR/CIRDialect.cpp4
-rw-r--r--clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp65
-rw-r--r--clang/lib/CodeGen/CGOpenMPRuntimeGPU.h6
-rw-r--r--clang/lib/Driver/Action.cpp2
-rw-r--r--clang/lib/Driver/ToolChains/Arch/AArch64.cpp7
-rw-r--r--clang/lib/Driver/ToolChains/Clang.h2
-rw-r--r--clang/lib/Headers/avx512fp16intrin.h8
-rw-r--r--clang/lib/Headers/avx512vlfp16intrin.h6
-rw-r--r--clang/lib/Headers/opencl-c-base.h10
-rw-r--r--clang/lib/Sema/SemaChecking.cpp4
-rw-r--r--clang/lib/Sema/SemaConcept.cpp26
-rw-r--r--clang/lib/Sema/SemaHLSL.cpp35
-rw-r--r--clang/lib/Sema/SemaOverload.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp6
-rw-r--r--clang/lib/Testing/CommandLineArgs.cpp3
-rw-r--r--clang/test/AST/ByteCode/builtin-object-size.cpp3
-rw-r--r--clang/test/AST/HLSL/resource_binding_attr.hlsl3
-rw-r--r--clang/test/AST/HLSL/vk_binding_attr.hlsl27
-rw-r--r--clang/test/AST/ast-print-record-decl.c7
-rw-r--r--clang/test/Analysis/Checkers/WebKit/objc-mock-types.h16
-rw-r--r--clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm9
-rw-r--r--clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h18
-rw-r--r--clang/test/Analysis/LifetimeSafety/benchmark.py82
-rw-r--r--clang/test/Analysis/NewDeleteLeaks.cpp33
-rw-r--r--clang/test/CIR/CodeGen/complex.cpp46
-rw-r--r--clang/test/CIR/CodeGen/constant-inits.cpp88
-rw-r--r--clang/test/CIR/CodeGen/new.cpp53
-rw-r--r--clang/test/CIR/CodeGen/throws.cpp44
-rw-r--r--clang/test/CIR/IR/dynamic-cast.cir59
-rw-r--r--clang/test/CIR/IR/invalid-dyn-cast.cir43
-rw-r--r--clang/test/CMakeLists.txt2
-rw-r--r--clang/test/CodeGen/X86/avx512fp16-builtins.c4
-rw-r--r--clang/test/CodeGen/X86/avx512vlfp16-builtins.c2
-rw-r--r--clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c172
-rw-r--r--clang/test/CodeGenCXX/builtin-invoke.cpp2
-rw-r--r--clang/test/Driver/aarch64-cpu-defaults-appleos26.c10
-rw-r--r--clang/test/Driver/amdgpu-openmp-sanitize-options.c2
-rw-r--r--clang/test/Driver/amdgpu-openmp-toolchain.c4
-rw-r--r--clang/test/Driver/cuda-phases.cu4
-rw-r--r--clang/test/Driver/darwin-maccatalyst-error.c6
-rw-r--r--clang/test/Driver/darwin-maccatalyst.c5
-rw-r--r--clang/test/Driver/env.c4
-rw-r--r--clang/test/Driver/hip-phases.hip4
-rw-r--r--clang/test/Driver/hip-toolchain-no-rdc.hip4
-rw-r--r--clang/test/Driver/linker-wrapper-image.c8
-rw-r--r--clang/test/Driver/linker-wrapper.c48
-rw-r--r--clang/test/Driver/offload-packager.c20
-rw-r--r--clang/test/Driver/openmp-offload-gpu.c6
-rw-r--r--clang/test/Driver/openmp-offload-jit.c2
-rw-r--r--clang/test/Driver/openmp-offload.c6
-rw-r--r--clang/test/Driver/print-supported-extensions-riscv.c2
-rw-r--r--clang/test/Driver/riscv-arch.c4
-rw-r--r--clang/test/Driver/spirv-openmp-toolchain.c2
-rw-r--r--clang/test/Driver/sycl-offload-jit.cpp6
-rw-r--r--clang/test/Preprocessor/riscv-target-features.c6
-rw-r--r--clang/test/Sema/format-strings-signedness.cpp30
-rw-r--r--clang/test/Sema/warn-lifetime-safety.cpp138
-rw-r--r--clang/test/SemaCXX/GH161671.cpp339
-rw-r--r--clang/test/SemaOpenCL/features.cl9
-rw-r--r--clang/test/SemaTemplate/concepts.cpp41
-rw-r--r--clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp2
-rw-r--r--clang/test/lit.cfg.py2
-rw-r--r--clang/tools/CMakeLists.txt1
-rw-r--r--clang/tools/clang-offload-packager/CMakeLists.txt17
-rw-r--r--clang/tools/clang-offload-packager/ClangOffloadPackager.cpp259
-rw-r--r--clang/unittests/AST/DeclPrinterTest.cpp104
-rw-r--r--clang/unittests/Analysis/LifetimeSafetyTest.cpp501
-rw-r--r--clang/unittests/Basic/DiagnosticTest.cpp36
-rw-r--r--clang/unittests/Driver/ToolChainTest.cpp2
98 files changed, 2977 insertions, 1425 deletions
diff --git a/clang/docs/ClangLinkerWrapper.rst b/clang/docs/ClangLinkerWrapper.rst
index eb38d2b..28f48fc 100644
--- a/clang/docs/ClangLinkerWrapper.rst
+++ b/clang/docs/ClangLinkerWrapper.rst
@@ -14,10 +14,10 @@ This tool works as a wrapper of the normal host linking job. This tool is used
to create linked device images for offloading and the necessary runtime calls to
register them. It works by first scanning the linker's input for embedded device
offloading data stored at the ``.llvm.offloading`` section. This section
-contains binary data created by the :doc:`ClangOffloadPackager`. The extracted
-device files will then be linked. The linked modules will then be wrapped into a
-new object file containing the code necessary to register it with the offloading
-runtime.
+contains binary data created by the ``llvm-offload-binary`` utility. The
+extracted device files will then be linked. The linked modules will then be
+wrapped into a new object file containing the code necessary to register it with
+the offloading runtime.
Usage
=====
diff --git a/clang/docs/ClangOffloadPackager.rst b/clang/docs/ClangOffloadPackager.rst
deleted file mode 100644
index 481069b..0000000
--- a/clang/docs/ClangOffloadPackager.rst
+++ /dev/null
@@ -1,193 +0,0 @@
-======================
-Clang Offload Packager
-======================
-
-.. contents::
- :local:
-
-.. _clang-offload-packager:
-
-Introduction
-============
-
-This tool bundles device files into a single image containing necessary
-metadata. We use a custom binary format for bundling all the device images
-together. The image format is a small header wrapping around a string map. This
-tool creates bundled binaries so that they can be embedded into the host to
-create a fat-binary.
-
-Binary Format
-=============
-
-The binary format is marked by the ``0x10FF10AD`` magic bytes, followed by a
-version. Each created binary contains its own magic bytes. This allows us to
-locate all the embedded offloading sections even after they may have been merged
-by the linker, such as when using relocatable linking. Conceptually, this binary
-format is a serialization of a string map and an image buffer. The binary header
-is described in the following :ref:`table<table-binary_header>`.
-
-.. table:: Offloading Binary Header
- :name: table-binary_header
-
- +----------+--------------+----------------------------------------------------+
- | Type | Identifier | Description |
- +==========+==============+====================================================+
- | uint8_t | magic | The magic bytes for the binary format (0x10FF10AD) |
- +----------+--------------+----------------------------------------------------+
- | uint32_t | version | Version of this format (currently version 1) |
- +----------+--------------+----------------------------------------------------+
- | uint64_t | size | Size of this binary in bytes |
- +----------+--------------+----------------------------------------------------+
- | uint64_t | entry offset | Absolute offset of the offload entries in bytes |
- +----------+--------------+----------------------------------------------------+
- | uint64_t | entry size | Size of the offload entries in bytes |
- +----------+--------------+----------------------------------------------------+
-
-Once identified through the magic bytes, we use the size field to take a slice
-of the binary blob containing the information for a single offloading image. We
-can then use the offset field to find the actual offloading entries containing
-the image and metadata. The offload entry contains information about the device
-image. It contains the fields shown in the following
-:ref:`table<table-binary_entry>`.
-
-.. table:: Offloading Entry Table
- :name: table-binary_entry
-
- +----------+---------------+----------------------------------------------------+
- | Type | Identifier | Description |
- +==========+===============+====================================================+
- | uint16_t | image kind | The kind of the device image (e.g. bc, cubin) |
- +----------+---------------+----------------------------------------------------+
- | uint16_t | offload kind | The producer of the image (e.g. openmp, cuda) |
- +----------+---------------+----------------------------------------------------+
- | uint32_t | flags | Generic flags for the image |
- +----------+---------------+----------------------------------------------------+
- | uint64_t | string offset | Absolute offset of the string metadata table |
- +----------+---------------+----------------------------------------------------+
- | uint64_t | num strings | Number of string entries in the table |
- +----------+---------------+----------------------------------------------------+
- | uint64_t | image offset | Absolute offset of the device image in bytes |
- +----------+---------------+----------------------------------------------------+
- | uint64_t | image size | Size of the device image in bytes |
- +----------+---------------+----------------------------------------------------+
-
-This table contains the offsets of the string table and the device image itself
-along with some other integer information. The image kind lets us easily
-identify the type of image stored here without needing to inspect the binary.
-The offloading kind is used to determine which registration code or linking
-semantics are necessary for this image. These are stored as enumerations with
-the following values for the :ref:`offload kind<table-offload_kind>` and the
-:ref:`image kind<table-image_kind>`.
-
-.. table:: Image Kind
- :name: table-image_kind
-
- +---------------+-------+---------------------------------------+
- | Name | Value | Description |
- +===============+=======+=======================================+
- | IMG_None | 0x00 | No image information provided |
- +---------------+-------+---------------------------------------+
- | IMG_Object | 0x01 | The image is a generic object file |
- +---------------+-------+---------------------------------------+
- | IMG_Bitcode | 0x02 | The image is an LLVM-IR bitcode file |
- +---------------+-------+---------------------------------------+
- | IMG_Cubin | 0x03 | The image is a CUDA object file |
- +---------------+-------+---------------------------------------+
- | IMG_Fatbinary | 0x04 | The image is a CUDA fatbinary file |
- +---------------+-------+---------------------------------------+
- | IMG_PTX | 0x05 | The image is a CUDA PTX file |
- +---------------+-------+---------------------------------------+
-
-.. table:: Offload Kind
- :name: table-offload_kind
-
- +------------+-------+---------------------------------------+
- | Name | Value | Description |
- +============+=======+=======================================+
- | OFK_None | 0x00 | No offloading information provided |
- +------------+-------+---------------------------------------+
- | OFK_OpenMP | 0x01 | The producer was OpenMP offloading |
- +------------+-------+---------------------------------------+
- | OFK_CUDA | 0x02 | The producer was CUDA |
- +------------+-------+---------------------------------------+
- | OFK_HIP | 0x03 | The producer was HIP |
- +------------+-------+---------------------------------------+
- | OFK_SYCL | 0x04 | The producer was SYCL |
- +------------+-------+---------------------------------------+
-
-The flags are used to signify certain conditions, such as the presence of
-debugging information or whether or not LTO was used. The string entry table is
-used to generically contain any arbitrary key-value pair. This is stored as an
-array of the :ref:`string entry<table-binary_string>` format.
-
-.. table:: Offloading String Entry
- :name: table-binary_string
-
- +----------+--------------+-------------------------------------------------------+
- | Type | Identifier | Description |
- +==========+==============+=======================================================+
- | uint64_t | key offset | Absolute byte offset of the key in the string table |
- +----------+--------------+-------------------------------------------------------+
- | uint64_t | value offset | Absolute byte offset of the value in the string table |
- +----------+--------------+-------------------------------------------------------+
-
-The string entries simply provide offsets to a key and value pair in the
-binary images string table. The string table is simply a collection of null
-terminated strings with defined offsets in the image. The string entry allows us
-to create a key-value pair from this string table. This is used for passing
-arbitrary arguments to the image, such as the triple and architecture.
-
-All of these structures are combined to form a single binary blob, the order
-does not matter because of the use of absolute offsets. This makes it easier to
-extend in the future. As mentioned previously, multiple offloading images are
-bundled together by simply concatenating them in this format. Because we have
-the magic bytes and size of each image, we can extract them as-needed.
-
-Usage
-=====
-
-This tool can be used with the following arguments. Generally information is
-passed as a key-value pair to the ``image=`` argument. The ``file`` and
-``triple``, arguments are considered mandatory to make a valid image.
-The ``arch`` argument is suggested.
-
-.. code-block:: console
-
- OVERVIEW: A utility for bundling several object files into a single binary.
- The output binary can then be embedded into the host section table
- to create a fatbinary containing offloading code.
-
- USAGE: clang-offload-packager [options]
-
- OPTIONS:
-
- Generic Options:
-
- --help - Display available options (--help-hidden for more)
- --help-list - Display list of available options (--help-list-hidden for more)
- --version - Display the version of this program
-
- clang-offload-packager options:
-
- --image=<<key>=<value>,...> - List of key and value arguments. Required
- keywords are 'file' and 'triple'.
- -o <file> - Write output to <file>.
-
-Example
-=======
-
-This tool simply takes many input files from the ``image`` option and creates a
-single output file with all the images combined.
-
-.. code-block:: console
-
- clang-offload-packager -o out.bin --image=file=input.o,triple=nvptx64,arch=sm_70
-
-The inverse operation can be performed instead by passing the packaged binary as
-input. In this mode the matching images will either be placed in the output
-specified by the ``file`` option. If no ``file`` argument is provided a name
-will be generated for each matching image.
-
-.. code-block:: console
-
- clang-offload-packager in.bin --image=file=output.o,triple=nvptx64,arch=sm_70
diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst
index cf89e31a..90c0186 100644
--- a/clang/docs/OpenMPSupport.rst
+++ b/clang/docs/OpenMPSupport.rst
@@ -550,7 +550,7 @@ implementation.
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| OMP_AVAILABLE_DEVICES envirable | :none:`unclaimed` | :none:`unclaimed` | |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
-| Traits for default device envirable | :none:`unclaimed` | :none:`unclaimed` | |
+| Traits for default device envirable | :part:`in progress` | :none:`unclaimed` | ro-i |
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
| Optionally omit array length expression | :good:`done` | :none:`unclaimed` | (Parse) https://github.com/llvm/llvm-project/pull/148048, |
| | | | (Sema) https://github.com/llvm/llvm-project/pull/152786 |
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 390e0fa..5e9a71e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -126,6 +126,8 @@ AST Dumping Potentially Breaking Changes
- Pretty-printing of templates with inherited (i.e. specified in a previous
redeclaration) default arguments has been fixed.
+- Default arguments of template template parameters are pretty-printed now.
+
Clang Frontend Potentially Breaking Changes
-------------------------------------------
- Members of anonymous unions/structs are now injected as ``IndirectFieldDecl``
diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index be654af..e238518 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -101,7 +101,6 @@ Using Clang Tools
ClangLinkerWrapper
ClangNVLinkWrapper
ClangOffloadBundler
- ClangOffloadPackager
ClangRepl
ClangSYCLLinker
diff --git a/clang/include/clang/AST/HLSLResource.h b/clang/include/clang/AST/HLSLResource.h
index 9cdd81b..7440050 100644
--- a/clang/include/clang/AST/HLSLResource.h
+++ b/clang/include/clang/AST/HLSLResource.h
@@ -69,6 +69,11 @@ struct ResourceBindingAttrs {
assert(hasImplicitOrderID());
return RegBinding->getImplicitBindingOrderID();
}
+
+ void setImplicitOrderID(unsigned Value) const {
+ assert(hasBinding() && !isExplicit() && !hasImplicitOrderID());
+ RegBinding->setImplicitBindingOrderID(Value);
+ }
};
} // namespace hlsl
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
index 512cb76..e54fc26 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -29,12 +29,18 @@
namespace clang::lifetimes {
/// Enum to track the confidence level of a potential error.
-enum class Confidence {
+enum class Confidence : uint8_t {
None,
Maybe, // Reported as a potential error (-Wlifetime-safety-strict)
Definite // Reported as a definite error (-Wlifetime-safety-permissive)
};
+enum class LivenessKind : uint8_t {
+ Dead, // Not alive
+ Maybe, // Live on some path but not all paths (may-be-live)
+ Must // Live on all paths (must-be-live)
+};
+
class LifetimeSafetyReporter {
public:
LifetimeSafetyReporter() = default;
@@ -55,6 +61,7 @@ class Fact;
class FactManager;
class LoanPropagationAnalysis;
class ExpiredLoansAnalysis;
+class LiveOriginAnalysis;
struct LifetimeFactory;
/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
@@ -89,6 +96,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OriginID ID) {
// TODO(opt): Consider using a bitset to represent the set of loans.
using LoanSet = llvm::ImmutableSet<LoanID>;
using OriginSet = llvm::ImmutableSet<OriginID>;
+using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific
/// `Fact`. identified by a lifetime-related event (`Fact`).
@@ -110,8 +118,16 @@ public:
/// Returns the set of loans an origin holds at a specific program point.
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;
- /// Returns the set of loans that have expired at a specific program point.
- std::vector<LoanID> getExpiredLoansAtPoint(ProgramPoint PP) const;
+ /// Returns the set of origins that are live at a specific program point,
+ /// along with the confidence level of their liveness.
+ ///
+ /// An origin is considered live if there are potential future uses of that
+ /// origin after the given program point. The confidence level indicates
+ /// whether the origin is definitely live (Definite) due to being domintated
+ /// by a set of uses or only possibly live (Maybe) only on some but not all
+ /// control flow paths.
+ std::vector<std::pair<OriginID, LivenessKind>>
+ getLiveOriginsAtPoint(ProgramPoint PP) const;
/// Finds the OriginID for a given declaration.
/// Returns a null optional if not found.
@@ -138,7 +154,7 @@ private:
std::unique_ptr<LifetimeFactory> Factory;
std::unique_ptr<FactManager> FactMgr;
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
- std::unique_ptr<ExpiredLoansAnalysis> ExpiredLoans;
+ std::unique_ptr<LiveOriginAnalysis> LiveOrigins;
};
} // namespace internal
} // namespace clang::lifetimes
diff --git a/clang/include/clang/Basic/SanitizerSpecialCaseList.h b/clang/include/clang/Basic/SanitizerSpecialCaseList.h
index cf74859..a05da4c 100644
--- a/clang/include/clang/Basic/SanitizerSpecialCaseList.h
+++ b/clang/include/clang/Basic/SanitizerSpecialCaseList.h
@@ -57,12 +57,10 @@ protected:
void createSanitizerSections();
struct SanitizerSection {
- SanitizerSection(SanitizerMask SM, SectionEntries &E, unsigned idx)
- : Mask(SM), Entries(E), FileIdx(idx) {};
+ SanitizerSection(SanitizerMask SM, const Section &S) : Mask(SM), S(S) {};
SanitizerMask Mask;
- SectionEntries &Entries;
- unsigned FileIdx;
+ const Section &S;
};
std::vector<SanitizerSection> SanitizerSections;
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 7714750..43832b7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -602,6 +602,63 @@ def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
}
//===----------------------------------------------------------------------===//
+// DynamicCastInfoAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_DynamicCastInfoAttr : CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
+ let summary = "ABI specific information about a dynamic cast";
+ let description = [{
+ Provide ABI specific information about a dynamic cast operation.
+
+ The `src_rtti` and the `dest_rtti` parameters give the RTTI of the source
+ record type and the destination record type, respectively.
+
+ The `runtime_func` parameter gives the `__dynamic_cast` function which is
+ provided by the runtime. The `bad_cast_func` parameter gives the
+ `__cxa_bad_cast` function which is also provided by the runtime.
+
+ The `offset_hint` parameter gives the hint value that should be passed to
+ the `__dynamic_cast` runtime function.
+ }];
+
+ let parameters = (ins
+ CIR_GlobalViewAttr:$src_rtti,
+ CIR_GlobalViewAttr:$dest_rtti,
+ "mlir::FlatSymbolRefAttr":$runtime_func,
+ "mlir::FlatSymbolRefAttr":$bad_cast_func,
+ CIR_IntAttr:$offset_hint
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins
+ "GlobalViewAttr":$src_rtti,
+ "GlobalViewAttr":$dest_rtti,
+ "mlir::FlatSymbolRefAttr":$runtime_func,
+ "mlir::FlatSymbolRefAttr":$bad_cast_func,
+ "IntAttr":$offset_hint), [{
+ return $_get(src_rtti.getContext(), src_rtti, dest_rtti, runtime_func,
+ bad_cast_func, offset_hint);
+ }]>,
+ ];
+
+ let genVerifyDecl = 1;
+ let assemblyFormat = [{
+ `<`
+ struct(qualified($src_rtti),
+ qualified($dest_rtti),
+ $runtime_func,
+ $bad_cast_func,
+ qualified($offset_hint))
+ `>`
+ }];
+
+ let extraClassDeclaration = [{
+ /// Get attribute alias name for this attribute.
+ std::string getAlias() const;
+ }];
+}
+
+//===----------------------------------------------------------------------===//
// TargetAddressSpaceAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d4ffcf3..27fe0cc 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -232,6 +232,100 @@ def CIR_CastOp : CIR_Op<"cast", [
}];
}
+//===----------------------------------------------------------------------===//
+// DynamicCastOp
+//===----------------------------------------------------------------------===//
+
+def CIR_DynamicCastKind : CIR_I32EnumAttr<
+ "DynamicCastKind", "dynamic cast kind", [
+ I32EnumAttrCase<"Ptr", 0, "ptr">,
+ I32EnumAttrCase<"Ref", 1, "ref">
+]>;
+
+def CIR_DynamicCastOp : CIR_Op<"dyn_cast"> {
+ let summary = "Perform dynamic cast on record pointers";
+ let description = [{
+ The `cir.dyn_cast` operation models part of the semantics of the
+ `dynamic_cast` operator in C++. It can be used to perform 3 kinds of casts
+ on record pointers:
+
+ - Down-cast, which casts a base class pointer to a derived class pointer;
+ - Side-cast, which casts a class pointer to a sibling class pointer;
+ - Cast-to-complete, which casts a class pointer to a void pointer.
+
+ The input of the operation must be a record pointer. The result of the
+ operation is either a record pointer or a void pointer.
+
+ The parameter `kind` specifies the semantics of this operation. If its value
+ is `ptr`, then the operation models dynamic casts on pointers. Otherwise, if
+ its value is `ref`, the operation models dynamic casts on references.
+ Specifically:
+
+ - When the input pointer is a null pointer value:
+ - If `kind` is `ref`, the operation will invoke undefined behavior. A
+ sanitizer check will be emitted if sanitizer is on.
+ - Otherwise, the operation will return a null pointer value as its result.
+ - When the runtime type check fails:
+ - If `kind` is `ref`, the operation will throw a `bad_cast` exception.
+ - Otherwise, the operation will return a null pointer value as its result.
+
+ The `info` argument gives detailed information about the requested dynamic
+ cast operation. It is an optional `#cir.dyn_cast_info` attribute that is
+ only present when the operation models a down-cast or a side-cast.
+
+ The `relative_layout` argument specifies whether the Itanium C++ ABI vtable
+ uses relative layout. It is only meaningful when the operation models a
+ cast-to-complete operation.
+
+ Examples:
+
+ ```mlir
+ %0 = cir.dyn_cast ptr %p : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
+ %1 = cir.dyn_cast ptr relative_layout %p : !cir.ptr<!rec_Base>
+ -> !cir.ptr<!rec_Derived>
+ %2 = cir.dyn_cast ref %r : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived>
+ #cir.dyn_cast_info<
+ srcRtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>,
+ destRtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>,
+ runtimeFunc = @__dynamic_cast,
+ badCastFunc = @__cxa_bad_cast,
+ offsetHint = #cir.int<0> : !s64i
+ >
+ ```
+ }];
+
+ let arguments = (ins
+ CIR_DynamicCastKind:$kind,
+ CIR_PtrToRecordType:$src,
+ OptionalAttr<CIR_DynamicCastInfoAttr>:$info,
+ UnitAttr:$relative_layout
+ );
+
+ let results = (outs
+ CIR_PtrToAnyOf<[CIR_VoidType, CIR_RecordType]>:$result
+ );
+
+ let assemblyFormat = [{
+ $kind (`relative_layout` $relative_layout^)? $src
+ `:` qualified(type($src)) `->` qualified(type($result))
+ (qualified($info)^)? attr-dict
+ }];
+
+ let extraClassDeclaration = [{
+ /// Determine whether this operation models reference casting in C++.
+ bool isRefCast() {
+ return getKind() == ::cir::DynamicCastKind::Ref;
+ }
+
+ /// Determine whether this operation represents a dynamic cast to a void
+ /// pointer.
+ bool isCastToVoid() {
+ return getType().isVoidPtr();
+ }
+ }];
+
+ let hasLLVMLowering = false;
+}
//===----------------------------------------------------------------------===//
// PtrStrideOp
@@ -278,22 +372,22 @@ def CIR_PtrStrideOp : CIR_Op<"ptr_stride", [
def CIR_ConstantOp : CIR_Op<"const", [
ConstantLike, Pure, AllTypesMatch<["value", "res"]>
]> {
- let summary = "Defines a CIR constant";
+ let summary = "Create a CIR constant from a literal attribute";
let description = [{
The `cir.const` operation turns a literal into an SSA value. The data is
attached to the operation as an attribute.
```mlir
- %0 = cir.const 42 : i32
- %1 = cir.const 4.2 : f32
- %2 = cir.const nullptr : !cir.ptr<i32>
+ %0 = cir.const #cir.int<4> : !u32i
+ %1 = cir.const #cir.fp<1.500000e+00> : !cir.float
+ %2 = cir.const #cir.ptr<null> : !cir.ptr<!void>
```
}];
let arguments = (ins TypedAttrInterface:$value);
let results = (outs CIR_AnyType:$res);
- let assemblyFormat = "attr-dict $value";
+ let assemblyFormat = "$value attr-dict";
let hasVerifier = 1;
@@ -3277,9 +3371,9 @@ def CIR_ComplexCreateOp : CIR_Op<"complex.create", [Pure, SameTypeOperands]> {
def CIR_ComplexRealOp : CIR_Op<"complex.real", [Pure]> {
let summary = "Extract the real part of a complex value";
let description = [{
- `cir.complex.real` operation takes an operand of `!cir.complex`, `!cir.int`
- or `!cir.float`. If the operand is `!cir.complex`, the real part of it will
- be returned, otherwise the value returned unmodified.
+ `cir.complex.real` operation takes an operand of `!cir.complex`, `cir.int`,
+ `!cir.bool` or `!cir.float`. If the operand is `!cir.complex`, the real
+ part of it will be returned, otherwise the value returned unmodified.
Example:
@@ -3289,8 +3383,8 @@ def CIR_ComplexRealOp : CIR_Op<"complex.real", [Pure]> {
```
}];
- let results = (outs CIR_AnyIntOrFloatType:$result);
- let arguments = (ins CIR_AnyComplexOrIntOrFloatType:$operand);
+ let results = (outs CIR_AnyIntOrBoolOrFloatType:$result);
+ let arguments = (ins CIR_AnyComplexOrIntOrBoolOrFloatType:$operand);
let assemblyFormat = [{
$operand `:` qualified(type($operand)) `->` qualified(type($result))
@@ -3309,8 +3403,8 @@ def CIR_ComplexImagOp : CIR_Op<"complex.imag", [Pure]> {
let summary = "Extract the imaginary part of a complex value";
let description = [{
`cir.complex.imag` operation takes an operand of `!cir.complex`, `!cir.int`
- or `!cir.float`. If the operand is `!cir.complex`, the imag part of it will
- be returned, otherwise a zero value will be returned.
+ `!cir.bool` or `!cir.float`. If the operand is `!cir.complex`, the imag
+ part of it will be returned, otherwise a zero value will be returned.
Example:
@@ -3320,8 +3414,8 @@ def CIR_ComplexImagOp : CIR_Op<"complex.imag", [Pure]> {
```
}];
- let results = (outs CIR_AnyIntOrFloatType:$result);
- let arguments = (ins CIR_AnyComplexOrIntOrFloatType:$operand);
+ let results = (outs CIR_AnyIntOrBoolOrFloatType:$result);
+ let arguments = (ins CIR_AnyComplexOrIntOrBoolOrFloatType:$operand);
let assemblyFormat = [{
$operand `:` qualified(type($operand)) `->` qualified(type($result))
@@ -4169,6 +4263,40 @@ def CIR_ThrowOp : CIR_Op<"throw"> {
}
//===----------------------------------------------------------------------===//
+// AllocExceptionOp
+//===----------------------------------------------------------------------===//
+
+def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> {
+ let summary = "Allocates an exception according to Itanium ABI";
+ let description = [{
+ Implements a slightly higher level __cxa_allocate_exception:
+
+ `void *__cxa_allocate_exception(size_t thrown_size);`
+
+ If the operation fails, the program terminates rather than throw.
+
+ Example:
+
+ ```mlir
+ // if (b == 0) {
+ // ...
+ // throw "...";
+ cir.if %10 {
+ %11 = cir.alloc_exception 8 -> !cir.ptr<!void>
+ ... // store exception content into %11
+ cir.throw %11 : !cir.ptr<!cir.ptr<!u8i>>, ...
+ ```
+ }];
+
+ let arguments = (ins I64Attr:$size);
+ let results = (outs Res<CIR_PointerType, "", [MemAlloc<DefaultResource>]>:$addr);
+
+ let assemblyFormat = [{
+ $size `->` qualified(type($addr)) attr-dict
+ }];
+}
+
+//===----------------------------------------------------------------------===//
// Atomic operations
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index da03a29..b2c146c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -159,19 +159,31 @@ def CIR_AnyIntOrFloatType : AnyTypeOf<[CIR_AnyFloatType, CIR_AnyIntType],
let cppFunctionName = "isAnyIntegerOrFloatingPointType";
}
+def CIR_AnyIntOrBoolOrFloatType
+ : AnyTypeOf<[CIR_AnyBoolType, CIR_AnyFloatType, CIR_AnyIntType],
+ "integer, boolean or floating point type"> {
+ let cppFunctionName = "isAnyIntegerOrBooleanOrFloatingPointType";
+}
+
//===----------------------------------------------------------------------===//
// Complex Type predicates
//===----------------------------------------------------------------------===//
def CIR_AnyComplexType : CIR_TypeBase<"::cir::ComplexType", "complex type">;
-def CIR_AnyComplexOrIntOrFloatType : AnyTypeOf<[
- CIR_AnyComplexType, CIR_AnyFloatType, CIR_AnyIntType
-], "complex, integer or floating point type"> {
- let cppFunctionName = "isComplexOrIntegerOrFloatingPointType";
+def CIR_AnyComplexOrIntOrBoolOrFloatType
+ : AnyTypeOf<[CIR_AnyComplexType, CIR_AnyIntOrBoolOrFloatType],
+ "complex, integer or floating point type"> {
+ let cppFunctionName = "isComplexOrIntegerOrBoolOrFloatingPointType";
}
//===----------------------------------------------------------------------===//
+// Record Type predicates
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyRecordType : CIR_TypeBase<"::cir::RecordType", "record type">;
+
+//===----------------------------------------------------------------------===//
// Array Type predicates
//===----------------------------------------------------------------------===//
@@ -228,6 +240,8 @@ def CIR_PtrToIntOrFloatType : CIR_PtrToType<CIR_AnyIntOrFloatType>;
def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
+def CIR_PtrToRecordType : CIR_PtrToType<CIR_AnyRecordType>;
+
def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index d34a414..59bbd0f 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -1202,12 +1202,12 @@ class Sema;
/// Would use of this function result in a rewrite using a different
/// operator?
- bool isRewrittenOperator(const FunctionDecl *FD) {
+ bool isRewrittenOperator(const FunctionDecl *FD) const {
return OriginalOperator &&
FD->getDeclName().getCXXOverloadedOperator() != OriginalOperator;
}
- bool isAcceptableCandidate(const FunctionDecl *FD) {
+ bool isAcceptableCandidate(const FunctionDecl *FD) const {
if (!OriginalOperator)
return true;
@@ -1234,7 +1234,7 @@ class Sema;
}
/// Determines whether this operator could be implemented by a function
/// with reversed parameter order.
- bool isReversible() {
+ bool isReversible() const {
return AllowRewrittenCandidates && OriginalOperator &&
(getRewrittenOverloadedOperator(OriginalOperator) != OO_None ||
allowsReversed(OriginalOperator));
@@ -1242,13 +1242,13 @@ class Sema;
/// Determine whether reversing parameter order is allowed for operator
/// Op.
- bool allowsReversed(OverloadedOperatorKind Op);
+ bool allowsReversed(OverloadedOperatorKind Op) const;
/// Determine whether we should add a rewritten candidate for \p FD with
/// reversed parameter order.
/// \param OriginalArgs are the original non reversed arguments.
bool shouldAddReversed(Sema &S, ArrayRef<Expr *> OriginalArgs,
- FunctionDecl *FD);
+ FunctionDecl *FD) const;
};
private:
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 6af7ef3..1eea813 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2314,10 +2314,14 @@ static bool interp__builtin_object_size(InterpState &S, CodePtr OpPC,
if (Ptr.isBaseClass())
ByteOffset = computePointerOffset(ASTCtx, Ptr.getBase()) -
computePointerOffset(ASTCtx, Ptr);
- else
- ByteOffset =
- computePointerOffset(ASTCtx, Ptr) -
- computePointerOffset(ASTCtx, Ptr.expand().atIndex(0).narrow());
+ else {
+ if (Ptr.inArray())
+ ByteOffset =
+ computePointerOffset(ASTCtx, Ptr) -
+ computePointerOffset(ASTCtx, Ptr.expand().atIndex(0).narrow());
+ else
+ ByteOffset = 0;
+ }
} else
ByteOffset = computePointerOffset(ASTCtx, Ptr);
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 7001ade..7f3dcca 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -111,6 +111,7 @@ namespace {
void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D);
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *TTP);
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP);
+ void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *);
void VisitHLSLBufferDecl(HLSLBufferDecl *D);
void VisitOpenACCDeclareDecl(OpenACCDeclareDecl *D);
@@ -1189,8 +1190,7 @@ void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
} else if (auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
VisitNonTypeTemplateParmDecl(NTTP);
} else if (auto TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
- VisitTemplateDecl(TTPD);
- // FIXME: print the default argument, if present.
+ VisitTemplateTemplateParmDecl(TTPD);
}
}
@@ -1916,6 +1916,16 @@ void DeclPrinter::VisitNonTypeTemplateParmDecl(
}
}
+void DeclPrinter::VisitTemplateTemplateParmDecl(
+ const TemplateTemplateParmDecl *TTPD) {
+ VisitTemplateDecl(TTPD);
+ if (TTPD->hasDefaultArgument() && !TTPD->defaultArgumentWasInherited()) {
+ Out << " = ";
+ TTPD->getDefaultArgument().getArgument().print(Policy, Out,
+ /*IncludeType=*/false);
+ }
+}
+
void DeclPrinter::VisitOpenACCDeclareDecl(OpenACCDeclareDecl *D) {
if (!D->isInvalidDecl()) {
Out << "#pragma acc declare";
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
index c18b8fb..6196ec3 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -19,12 +19,13 @@
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/ADT/PointerUnion.h"
-#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TimeProfiler.h"
#include <cstdint>
#include <memory>
+#include <optional>
namespace clang::lifetimes {
namespace internal {
@@ -872,22 +873,19 @@ public:
InStates[Start] = D.getInitialState();
W.enqueueBlock(Start);
- llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1);
-
while (const CFGBlock *B = W.dequeue()) {
- Lattice StateIn = getInState(B);
+ Lattice StateIn = *getInState(B);
Lattice StateOut = transferBlock(B, StateIn);
OutStates[B] = StateOut;
- Visited.set(B->getBlockID());
for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) {
if (!AdjacentB)
continue;
- Lattice OldInState = getInState(AdjacentB);
- Lattice NewInState = D.join(OldInState, StateOut);
+ std::optional<Lattice> OldInState = getInState(AdjacentB);
+ Lattice NewInState =
+ !OldInState ? StateOut : D.join(*OldInState, StateOut);
// Enqueue the adjacent block if its in-state has changed or if we have
- // never visited it.
- if (!Visited.test(AdjacentB->getBlockID()) ||
- NewInState != OldInState) {
+ // never seen it.
+ if (!OldInState || NewInState != *OldInState) {
InStates[AdjacentB] = NewInState;
W.enqueueBlock(AdjacentB);
}
@@ -898,7 +896,12 @@ public:
protected:
Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
- Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
+ std::optional<Lattice> getInState(const CFGBlock *B) const {
+ auto It = InStates.find(B);
+ if (It == InStates.end())
+ return std::nullopt;
+ return It->second;
+ }
Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
@@ -974,19 +977,21 @@ static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
return A;
}
-/// Checks if set A is a subset of set B.
-template <typename T>
-static bool isSubsetOf(const llvm::ImmutableSet<T> &A,
- const llvm::ImmutableSet<T> &B) {
- // Empty set is a subset of all sets.
- if (A.isEmpty())
- return true;
-
- for (const T &Elem : A)
- if (!B.contains(Elem))
- return false;
- return true;
-}
+/// Describes the strategy for joining two `ImmutableMap` instances, primarily
+/// differing in how they handle keys that are unique to one of the maps.
+///
+/// A `Symmetric` join is universally correct, while an `Asymmetric` join
+/// serves as a performance optimization. The latter is applicable only when the
+/// join operation possesses a left identity element, allowing for a more
+/// efficient, one-sided merge.
+enum class JoinKind {
+ /// A symmetric join applies the `JoinValues` operation to keys unique to
+ /// either map, ensuring that values from both maps contribute to the result.
+ Symmetric,
+ /// An asymmetric join preserves keys unique to the first map as-is, while
+ /// applying the `JoinValues` operation only to keys unique to the second map.
+ Asymmetric,
+};
/// Computes the key-wise union of two ImmutableMaps.
// TODO(opt): This key-wise join is a performance bottleneck. A more
@@ -994,22 +999,29 @@ static bool isSubsetOf(const llvm::ImmutableSet<T> &A,
// instead of the current AVL-tree-based ImmutableMap.
template <typename K, typename V, typename Joiner>
static llvm::ImmutableMap<K, V>
-join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
- typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues) {
+join(const llvm::ImmutableMap<K, V> &A, const llvm::ImmutableMap<K, V> &B,
+ typename llvm::ImmutableMap<K, V>::Factory &F, Joiner JoinValues,
+ JoinKind Kind) {
if (A.getHeight() < B.getHeight())
- std::swap(A, B);
+ return join(B, A, F, JoinValues, Kind);
// For each element in B, join it with the corresponding element in A
// (or with an empty value if it doesn't exist in A).
+ llvm::ImmutableMap<K, V> Res = A;
for (const auto &Entry : B) {
const K &Key = Entry.first;
const V &ValB = Entry.second;
- if (const V *ValA = A.lookup(Key))
- A = F.add(A, Key, JoinValues(*ValA, ValB));
- else
- A = F.add(A, Key, ValB);
+ Res = F.add(Res, Key, JoinValues(A.lookup(Key), &ValB));
+ }
+ if (Kind == JoinKind::Symmetric) {
+ for (const auto &Entry : A) {
+ const K &Key = Entry.first;
+ const V &ValA = Entry.second;
+ if (!B.contains(Key))
+ Res = F.add(Res, Key, JoinValues(&ValA, nullptr));
+ }
}
- return A;
+ return Res;
}
} // namespace utils
@@ -1017,19 +1029,6 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
// Loan Propagation Analysis
// ========================================================================= //
-using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
-using ExpiredLoanMap = llvm::ImmutableMap<LoanID, const ExpireFact *>;
-
-/// An object to hold the factories for immutable collections, ensuring
-/// that all created states share the same underlying memory management.
-struct LifetimeFactory {
- llvm::BumpPtrAllocator Allocator;
- OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
- LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
- ExpiredLoanMap::Factory ExpiredLoanMapFactory{Allocator,
- /*canonicalize=*/false};
-};
-
/// Represents the dataflow lattice for loan propagation.
///
/// This lattice tracks which loans each origin may hold at a given program
@@ -1073,10 +1072,10 @@ class LoanPropagationAnalysis
public:
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
- LifetimeFactory &LFactory)
- : DataflowAnalysis(C, AC, F),
- OriginLoanMapFactory(LFactory.OriginMapFactory),
- LoanSetFactory(LFactory.LoanSetFactory) {}
+ OriginLoanMap::Factory &OriginLoanMapFactory,
+ LoanSet::Factory &LoanSetFactory)
+ : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
+ LoanSetFactory(LoanSetFactory) {}
using Base::transfer;
@@ -1087,11 +1086,19 @@ public:
/// Merges two lattices by taking the union of loans for each origin.
// TODO(opt): Keep the state small by removing origins which become dead.
Lattice join(Lattice A, Lattice B) {
- OriginLoanMap JoinedOrigins =
- utils::join(A.Origins, B.Origins, OriginLoanMapFactory,
- [&](LoanSet S1, LoanSet S2) {
- return utils::join(S1, S2, LoanSetFactory);
- });
+ OriginLoanMap JoinedOrigins = utils::join(
+ A.Origins, B.Origins, OriginLoanMapFactory,
+ [&](const LoanSet *S1, const LoanSet *S2) {
+ assert((S1 || S2) && "unexpectedly merging 2 empty sets");
+ if (!S1)
+ return *S2;
+ if (!S2)
+ return *S1;
+ return utils::join(*S1, *S2, LoanSetFactory);
+ },
+ // Asymmetric join is a performance win. For origins present only on one
+ // branch, the loan set can be carried over as-is.
+ utils::JoinKind::Asymmetric);
return Lattice(JoinedOrigins);
}
@@ -1120,12 +1127,12 @@ public:
OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
}
- LoanSet getLoans(OriginID OID, ProgramPoint P) {
+ LoanSet getLoans(OriginID OID, ProgramPoint P) const {
return getLoans(getState(P), OID);
}
private:
- LoanSet getLoans(Lattice L, OriginID OID) {
+ LoanSet getLoans(Lattice L, OriginID OID) const {
if (auto *Loans = L.Origins.lookup(OID))
return *Loans;
return LoanSetFactory.getEmptySet();
@@ -1133,96 +1140,195 @@ private:
};
// ========================================================================= //
-// Expired Loans Analysis
+// Live Origins Analysis
+// ========================================================================= //
+//
+// A backward dataflow analysis that determines which origins are "live" at each
+// program point. An origin is "live" at a program point if there's a potential
+// future use of the pointer it represents. Liveness is "generated" by a read of
+// origin's loan set (e.g., a `UseFact`) and is "killed" (i.e., it stops being
+// live) when its loan set is overwritten (e.g. a OriginFlow killing the
+// destination origin).
+//
+// This information is used for detecting use-after-free errors, as it allows us
+// to check if a live origin holds a loan to an object that has already expired.
// ========================================================================= //
-/// The dataflow lattice for tracking the set of expired loans.
-struct ExpiredLattice {
- /// Map from an expired `LoanID` to the `ExpireFact` that made it expire.
- ExpiredLoanMap Expired;
+/// Information about why an origin is live at a program point.
+struct LivenessInfo {
+ /// The use that makes the origin live. If liveness is propagated from
+ /// multiple uses along different paths, this will point to the use appearing
+ /// earlier in the translation unit.
+ /// This is 'null' when the origin is not live.
+ const UseFact *CausingUseFact;
+ /// The kind of liveness of the origin.
+ /// `Must`: The origin is live on all control-flow paths from the current
+ /// point to the function's exit (i.e. the current point is dominated by a set
+ /// of uses).
+ /// `Maybe`: indicates it is live on some but not all paths.
+ ///
+ /// This determines the diagnostic's confidence level.
+ /// `Must`-be-alive at expiration implies a definite use-after-free,
+ /// while `Maybe`-be-alive suggests a potential one on some paths.
+ LivenessKind Kind;
+
+ LivenessInfo() : CausingUseFact(nullptr), Kind(LivenessKind::Dead) {}
+ LivenessInfo(const UseFact *UF, LivenessKind K)
+ : CausingUseFact(UF), Kind(K) {}
+
+ bool operator==(const LivenessInfo &Other) const {
+ return CausingUseFact == Other.CausingUseFact && Kind == Other.Kind;
+ }
+ bool operator!=(const LivenessInfo &Other) const { return !(*this == Other); }
+
+ void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
+ IDBuilder.AddPointer(CausingUseFact);
+ IDBuilder.Add(Kind);
+ }
+};
+
+using LivenessMap = llvm::ImmutableMap<OriginID, LivenessInfo>;
- ExpiredLattice() : Expired(nullptr) {};
- explicit ExpiredLattice(ExpiredLoanMap M) : Expired(M) {}
+/// The dataflow lattice for origin liveness analysis.
+/// It tracks which origins are live, why they're live (which UseFact),
+/// and the confidence level of that liveness.
+struct LivenessLattice {
+ LivenessMap LiveOrigins;
- bool operator==(const ExpiredLattice &Other) const {
- return Expired == Other.Expired;
+ LivenessLattice() : LiveOrigins(nullptr) {};
+
+ explicit LivenessLattice(LivenessMap L) : LiveOrigins(L) {}
+
+ bool operator==(const LivenessLattice &Other) const {
+ return LiveOrigins == Other.LiveOrigins;
}
- bool operator!=(const ExpiredLattice &Other) const {
+
+ bool operator!=(const LivenessLattice &Other) const {
return !(*this == Other);
}
- void dump(llvm::raw_ostream &OS) const {
- OS << "ExpiredLattice State:\n";
- if (Expired.isEmpty())
+ void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
+ if (LiveOrigins.isEmpty())
OS << " <empty>\n";
- for (const auto &[ID, _] : Expired)
- OS << " Loan " << ID << " is expired\n";
+ for (const auto &Entry : LiveOrigins) {
+ OriginID OID = Entry.first;
+ const LivenessInfo &Info = Entry.second;
+ OS << " ";
+ OM.dump(OID, OS);
+ OS << " is ";
+ switch (Info.Kind) {
+ case LivenessKind::Must:
+ OS << "definitely";
+ break;
+ case LivenessKind::Maybe:
+ OS << "maybe";
+ break;
+ case LivenessKind::Dead:
+ llvm_unreachable("liveness kind of live origins should not be dead.");
+ }
+ OS << " live at this point\n";
+ }
}
};
-/// The analysis that tracks which loans have expired.
-class ExpiredLoansAnalysis
- : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice,
- Direction::Forward> {
-
- ExpiredLoanMap::Factory &Factory;
+/// The analysis that tracks which origins are live, with granular information
+/// about the causing use fact and confidence level. This is a backward
+/// analysis.
+class LiveOriginAnalysis
+ : public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
+ Direction::Backward> {
+ FactManager &FactMgr;
+ LivenessMap::Factory &Factory;
public:
- ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
- LifetimeFactory &Factory)
- : DataflowAnalysis(C, AC, F), Factory(Factory.ExpiredLoanMapFactory) {}
-
- using Base::transfer;
+ LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+ LivenessMap::Factory &SF)
+ : DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF) {}
+ using DataflowAnalysis<LiveOriginAnalysis, Lattice,
+ Direction::Backward>::transfer;
- StringRef getAnalysisName() const { return "ExpiredLoans"; }
+ StringRef getAnalysisName() const { return "LiveOrigins"; }
Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
- /// Merges two lattices by taking the union of the two expired loans.
- Lattice join(Lattice L1, Lattice L2) {
- return Lattice(
- utils::join(L1.Expired, L2.Expired, Factory,
- // Take the last expiry fact to make this hermetic.
- [](const ExpireFact *F1, const ExpireFact *F2) {
- return F1->getExpiryLoc() > F2->getExpiryLoc() ? F1 : F2;
- }));
- }
-
- Lattice transfer(Lattice In, const ExpireFact &F) {
- return Lattice(Factory.add(In.Expired, F.getLoanID(), &F));
- }
-
- // Removes the loan from the set of expired loans.
- //
- // When a loan is re-issued (e.g., in a loop), it is no longer considered
- // expired. A loan can be in the expired set at the point of issue due to
- // the dataflow state from a previous loop iteration being propagated along
- // a backedge in the CFG.
- //
- // Note: This has a subtle false-negative though where a loan from previous
- // iteration is not overwritten by a reissue. This needs careful tracking
- // of loans "across iterations" which can be considered for future
- // enhancements.
- //
- // void foo(int safe) {
- // int* p = &safe;
- // int* q = &safe;
- // while (condition()) {
- // int x = 1;
- // p = &x; // A loan to 'x' is issued to 'p' in every iteration.
- // if (condition()) {
- // q = p;
- // }
- // (void)*p; // OK — 'p' points to 'x' from new iteration.
- // (void)*q; // UaF - 'q' still points to 'x' from previous iteration
- // // which is now destroyed.
- // }
- // }
- Lattice transfer(Lattice In, const IssueFact &F) {
- return Lattice(Factory.remove(In.Expired, F.getLoanID()));
+ /// Merges two lattices by combining liveness information.
+ /// When the same origin has different confidence levels, we take the lower
+ /// one.
+ Lattice join(Lattice L1, Lattice L2) const {
+ LivenessMap Merged = L1.LiveOrigins;
+ // Take the earliest UseFact to make the join hermetic and commutative.
+ auto CombineUseFact = [](const UseFact &A,
+ const UseFact &B) -> const UseFact * {
+ return A.getUseExpr()->getExprLoc() < B.getUseExpr()->getExprLoc() ? &A
+ : &B;
+ };
+ auto CombineLivenessKind = [](LivenessKind K1,
+ LivenessKind K2) -> LivenessKind {
+ assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
+ assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
+ // Only return "Must" if both paths are "Must", otherwise Maybe.
+ if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
+ return LivenessKind::Must;
+ return LivenessKind::Maybe;
+ };
+ auto CombineLivenessInfo = [&](const LivenessInfo *L1,
+ const LivenessInfo *L2) -> LivenessInfo {
+ assert((L1 || L2) && "unexpectedly merging 2 empty sets");
+ if (!L1)
+ return LivenessInfo(L2->CausingUseFact, LivenessKind::Maybe);
+ if (!L2)
+ return LivenessInfo(L1->CausingUseFact, LivenessKind::Maybe);
+ return LivenessInfo(
+ CombineUseFact(*L1->CausingUseFact, *L2->CausingUseFact),
+ CombineLivenessKind(L1->Kind, L2->Kind));
+ };
+ return Lattice(utils::join(
+ L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
+ // A symmetric join is required here. If an origin is live on one
+ // branch but not the other, its confidence must be demoted to `Maybe`.
+ utils::JoinKind::Symmetric));
+ }
+
+ /// A read operation makes the origin live with definite confidence, as it
+ /// dominates this program point. A write operation kills the liveness of
+ /// the origin since it overwrites the value.
+ Lattice transfer(Lattice In, const UseFact &UF) {
+ OriginID OID = UF.getUsedOrigin(FactMgr.getOriginMgr());
+ // Write kills liveness.
+ if (UF.isWritten())
+ return Lattice(Factory.remove(In.LiveOrigins, OID));
+ // Read makes origin live with definite confidence (dominates this point).
+ return Lattice(Factory.add(In.LiveOrigins, OID,
+ LivenessInfo(&UF, LivenessKind::Must)));
+ }
+
+ /// Issuing a new loan to an origin kills its liveness.
+ Lattice transfer(Lattice In, const IssueFact &IF) {
+ return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
}
- ExpiredLoanMap getExpiredLoans(ProgramPoint P) { return getState(P).Expired; }
+ /// An OriginFlow kills the liveness of the destination origin if `KillDest`
+ /// is true. Otherwise, it propagates liveness from destination to source.
+ Lattice transfer(Lattice In, const OriginFlowFact &OF) {
+ if (!OF.getKillDest())
+ return In;
+ return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
+ }
+
+ LivenessMap getLiveOrigins(ProgramPoint P) const {
+ return getState(P).LiveOrigins;
+ }
+
+ // Dump liveness values on all test points in the program.
+ void dump(llvm::raw_ostream &OS, const LifetimeSafetyAnalysis &LSA) const {
+ llvm::dbgs() << "==========================================\n";
+ llvm::dbgs() << getAnalysisName() << " results:\n";
+ llvm::dbgs() << "==========================================\n";
+ for (const auto &Entry : LSA.getTestPoints()) {
+ OS << "TestPoint: " << Entry.getKey() << "\n";
+ getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
+ }
+ }
};
// ========================================================================= //
@@ -1240,84 +1346,75 @@ class LifetimeChecker {
private:
llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;
LoanPropagationAnalysis &LoanPropagation;
- ExpiredLoansAnalysis &ExpiredLoans;
+ LiveOriginAnalysis &LiveOrigins;
FactManager &FactMgr;
AnalysisDeclContext &ADC;
LifetimeSafetyReporter *Reporter;
public:
- LifetimeChecker(LoanPropagationAnalysis &LPA, ExpiredLoansAnalysis &ELA,
+ LifetimeChecker(LoanPropagationAnalysis &LPA, LiveOriginAnalysis &LOA,
FactManager &FM, AnalysisDeclContext &ADC,
LifetimeSafetyReporter *Reporter)
- : LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC),
+ : LoanPropagation(LPA), LiveOrigins(LOA), FactMgr(FM), ADC(ADC),
Reporter(Reporter) {}
void run() {
llvm::TimeTraceScope TimeProfile("LifetimeChecker");
for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())
for (const Fact *F : FactMgr.getFacts(B))
- if (const auto *UF = F->getAs<UseFact>())
- checkUse(UF);
+ if (const auto *EF = F->getAs<ExpireFact>())
+ checkExpiry(EF);
issuePendingWarnings();
}
- /// Checks for use-after-free errors for a given use of an Origin.
+ /// Checks for use-after-free errors when a loan expires.
///
- /// This method is called for each 'UseFact' identified in the control flow
- /// graph. It determines if the loans held by the used origin have expired
- /// at the point of use.
- void checkUse(const UseFact *UF) {
- if (UF->isWritten())
- return;
- OriginID O = UF->getUsedOrigin(FactMgr.getOriginMgr());
-
- // Get the set of loans that the origin might hold at this program point.
- LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);
-
- // Get the set of all loans that have expired at this program point.
- ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF);
-
- // If the pointer holds no loans or no loans have expired, there's nothing
- // to check.
- if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty())
- return;
-
- // Identify loans that which have expired but are held by the pointer. Using
- // them is a use-after-free.
- llvm::SmallVector<LoanID> DefaultedLoans;
- // A definite UaF error occurs if all loans the origin might hold have
- // expired.
- bool IsDefiniteError = true;
- for (LoanID L : HeldLoans) {
- if (AllExpiredLoans.contains(L))
- DefaultedLoans.push_back(L);
- else
- // If at least one loan is not expired, this use is not a definite UaF.
- IsDefiniteError = false;
+ /// This method examines all live origins at the expiry point and determines
+ /// if any of them hold the expiring loan. If so, it creates a pending
+ /// warning with the appropriate confidence level based on the liveness
+ /// information. The confidence reflects whether the origin is definitely
+ /// or maybe live at this point.
+ ///
+ /// Note: This implementation considers only the confidence of origin
+ /// liveness. Future enhancements could also consider the confidence of loan
+ /// propagation (e.g., a loan may only be held on some execution paths).
+ void checkExpiry(const ExpireFact *EF) {
+ LoanID ExpiredLoan = EF->getLoanID();
+ LivenessMap Origins = LiveOrigins.getLiveOrigins(EF);
+ Confidence CurConfidence = Confidence::None;
+ const UseFact *BadUse = nullptr;
+ for (auto &[OID, LiveInfo] : Origins) {
+ LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);
+ if (!HeldLoans.contains(ExpiredLoan))
+ continue;
+ // Loan is defaulted.
+ Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);
+ if (CurConfidence < NewConfidence) {
+ CurConfidence = NewConfidence;
+ BadUse = LiveInfo.CausingUseFact;
+ }
}
- // If there are no defaulted loans, the use is safe.
- if (DefaultedLoans.empty())
+ if (!BadUse)
return;
-
- // Determine the confidence level of the error (definite or maybe).
- Confidence CurrentConfidence =
- IsDefiniteError ? Confidence::Definite : Confidence::Maybe;
-
- // For each expired loan, create a pending warning.
- for (LoanID DefaultedLoan : DefaultedLoans) {
- // If we already have a warning for this loan with a higher or equal
- // confidence, skip this one.
- if (FinalWarningsMap.count(DefaultedLoan) &&
- CurrentConfidence <= FinalWarningsMap[DefaultedLoan].ConfidenceLevel)
- continue;
-
- auto *EF = AllExpiredLoans.lookup(DefaultedLoan);
- assert(EF && "Could not find ExpireFact for an expired loan.");
-
- FinalWarningsMap[DefaultedLoan] = {/*ExpiryLoc=*/(*EF)->getExpiryLoc(),
- /*UseExpr=*/UF->getUseExpr(),
- /*ConfidenceLevel=*/CurrentConfidence};
+ // We have a use-after-free.
+ Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;
+ if (LastConf >= CurConfidence)
+ return;
+ FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),
+ /*UseExpr=*/BadUse->getUseExpr(),
+ /*ConfidenceLevel=*/CurConfidence};
+ }
+
+ static Confidence livenessKindToConfidence(LivenessKind K) {
+ switch (K) {
+ case LivenessKind::Must:
+ return Confidence::Definite;
+ case LivenessKind::Maybe:
+ return Confidence::Maybe;
+ case LivenessKind::Dead:
+ return Confidence::None;
}
+ llvm_unreachable("unknown liveness kind");
}
void issuePendingWarnings() {
@@ -1336,6 +1433,15 @@ public:
// LifetimeSafetyAnalysis Class Implementation
// ========================================================================= //
+/// An object to hold the factories for immutable collections, ensuring
+/// that all created states share the same underlying memory management.
+struct LifetimeFactory {
+ llvm::BumpPtrAllocator Allocator;
+ OriginLoanMap::Factory OriginMapFactory{Allocator, /*canonicalize=*/false};
+ LoanSet::Factory LoanSetFactory{Allocator, /*canonicalize=*/false};
+ LivenessMap::Factory LivenessMapFactory{Allocator, /*canonicalize=*/false};
+};
+
// We need this here for unique_ptr with forward declared class.
LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default;
@@ -1366,15 +1472,16 @@ void LifetimeSafetyAnalysis::run() {
/// the analysis.
/// 3. Collapse ExpireFacts belonging to same source location into a single
/// Fact.
- LoanPropagation =
- std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
+ LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
+ Cfg, AC, *FactMgr, Factory->OriginMapFactory, Factory->LoanSetFactory);
LoanPropagation->run();
- ExpiredLoans =
- std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
- ExpiredLoans->run();
+ LiveOrigins = std::make_unique<LiveOriginAnalysis>(
+ Cfg, AC, *FactMgr, Factory->LivenessMapFactory);
+ LiveOrigins->run();
+ DEBUG_WITH_TYPE("LiveOrigins", LiveOrigins->dump(llvm::dbgs(), *this));
- LifetimeChecker Checker(*LoanPropagation, *ExpiredLoans, *FactMgr, AC,
+ LifetimeChecker Checker(*LoanPropagation, *LiveOrigins, *FactMgr, AC,
Reporter);
Checker.run();
}
@@ -1385,15 +1492,6 @@ LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
return LoanPropagation->getLoans(OID, PP);
}
-std::vector<LoanID>
-LifetimeSafetyAnalysis::getExpiredLoansAtPoint(ProgramPoint PP) const {
- assert(ExpiredLoans && "ExpiredLoansAnalysis has not been run.");
- std::vector<LoanID> Result;
- for (const auto &pair : ExpiredLoans->getExpiredLoans(PP))
- Result.push_back(pair.first);
- return Result;
-}
-
std::optional<OriginID>
LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const {
assert(FactMgr && "FactManager not initialized");
@@ -1413,6 +1511,15 @@ LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const {
return Result;
}
+std::vector<std::pair<OriginID, LivenessKind>>
+LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const {
+ assert(LiveOrigins && "LiveOriginAnalysis has not been run.");
+ std::vector<std::pair<OriginID, LivenessKind>> Result;
+ for (auto &[OID, Info] : LiveOrigins->getLiveOrigins(PP))
+ Result.push_back({OID, Info.Kind});
+ return Result;
+}
+
llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
assert(FactMgr && "FactManager not initialized");
llvm::StringMap<ProgramPoint> AnnotationToPointMap;
diff --git a/clang/lib/Analysis/LifetimeSafety.md b/clang/lib/Analysis/LifetimeSafety.md
new file mode 100644
index 0000000..3f3d03d
--- /dev/null
+++ b/clang/lib/Analysis/LifetimeSafety.md
@@ -0,0 +1,230 @@
+Excellent! This is a very strong and logical structure for the white paper. It follows a clear narrative, starting from the high-level problem and progressively diving into the specifics of your solution. The sections on why a traditional borrow checker doesn't fit C++ and the open questions are particularly good, as they show a deep engagement with the problem space.
+
+Here is a draft of the white paper following your new skeleton, with the details filled in based on my analysis of your implementation and the provided reference documents. I've also incorporated some of my own suggestions to enhance the flow and clarity.
+
+***
+
+<Disclaimer: Public document. This work is licensed under the Apache License v2.0 with LLVM Exceptions. See [https://llvm.org/LICENSE.txt](https://llvm.org/LICENSE.txt) for license information.>
+
+# Lifetime Safety: An Intuitive Approach for Temporal Safety in C++
+**Author:**
+[Utkarsh Saxena](mailto:usx@google.com)
+
+**Purpose:** This document serves as a live RFC for a new lifetime safety analysis in C++, with the ultimate goal of publication as a white paper.
+
+## Intended Audience
+
+This document is intended for C++ compiler developers (especially those working on Clang), developers of other systems languages with advanced memory safety models (like Rust and Carbon), and all C++ users interested in writing safer code.
+
+## Goal
+
+* To describe a new lifetime model for C++ that aims to maximize the compile-time detection of temporal memory safety issues.
+* To explore a path toward incremental safety in C++, offering a spectrum of checks that can be adopted without requiring a full plunge into a restrictive ownership model.
+
+**Out of Scope**
+
+* **Rigorous Temporal Memory Safety:** This analysis aims to detect a large class of common errors, but it does not formally prove the absence of all temporal safety bugs.
+* **Runtime Solutions:** This paper focuses exclusively on static, compile-time analysis and does not cover runtime solutions like MTE or AddressSanitizer.
+
+# Paper: C++ Lifetimes Safety Analysis
+
+**Subtitle: A Flow-Sensitive, Alias-based Approach to Preventing Dangling Pointers**
+
+## Abstract
+
+This paper introduces a new intra-procedural, flow-sensitive lifetime analysis for C++ implemented in Clang. The analysis is designed to detect a significant class of temporal memory safety violations, such as use-after-free and use-after-return, at compile time. It is based on a model of "Loans" and "Origins," inspired by the Polonius borrow checker in Rust, but adapted for the semantics and flexibility of C++.
+
+The analysis works by translating the Clang CFG into a series of lifetime-relevant "Facts." These facts are then processed by dataflow analyses to precisely determine the validity of pointers and references at each program point. This fact-based approach, combined with a configurable strictness model, allows for both high-confidence error reporting and the detection of more subtle, potential bugs, without requiring extensive new annotations. The ultimate goal is to provide a powerful, low-overhead tool that makes C++ safer by default.
+
+## The Anatomy of a Temporal Safety Error
+
+At its core, a temporal safety error is a bug where an operation is performed on an object at a time when it is no longer valid to do so ([source](http://docs.google.com/document/d/19vbfAiV1yQu3xSMRWjyPUdzyB_LDdVUcKat_HWI1l3g?content_ref=at+its+core+a+temporal+safety+error+is+a+bug+where+an+operation+is+performed+on+an+object+at+a+time+when+it+is+no+longer+valid+to+do+so)). These bugs are notoriously difficult to debug because they often manifest as unpredictable crashes or silent data corruption far from the root cause. However, we can argue that this wide and varied class of errors—from use-after-free to iterator invalidation—all stem from a single, canonical pattern.
+
+**Conjecture: Any temporal safety issue is a form of Use-After-Free.**
+
+All sub-categories of temporal safety issues, such as returning a reference to a stack variable (`return-stack-addr`), using a variable after its scope has ended (`use-after-scope`), using heap memory after it has been deleted (`heap-use-after-free`), or using an iterator after its container has been modified (`use-after-invalidation`), can be described by a single sequence of events.
+
+In C++, an *object* is a region of storage, and pointers and references are the mechanisms we use to refer to them. A use-after-free occurs when we access an object after its lifetime has ended. But how can an object be accessed after it has been destroyed? This is only possible through an **alias**—a pointer or reference—that was created while the object was alive and that survived the object's destruction.
+
+This insight allows us to define a canonical use-after-free with four distinct events that happen in a specific order:
+
+1. **`t0`: Creation.** An object `M` is created in some region of storage (on the stack, on the heap, etc.).
+2. **`t1`: Alias Creation.** An alias `P` (a pointer or reference) is created that refers to the object `M`.
+3. **`t2`: End of Lifetime.** The lifetime of object `M` ends (e.g., it is deallocated, or it goes out of scope).
+4. **`t3`: Use of Alias.** The alias `P`, which now dangles, is used to access the memory where `M` once resided.
+
+Let's examine this with a simple piece of C++ code:
+
+```cpp
+void use_after_scope_example() {
+ int* p;
+ {
+ int s = 10; // t0: Object `s` is created on the stack.
+ p = &s; // t1: Alias `p` is made to refer to object `s`.
+ } // t2: The lifetime of `s` ends. `p` now dangles.
+ *p = 42; // t3: The dangling alias `p` is used. This is a use-after-free.
+}
+```
+
+The fundamental problem is that the alias `p` outlived the object `s` it referred to. The challenge for a static analysis is therefore clear: to prevent temporal safety errors, the compiler must be able to track aliases and understand the lifetime of the objects they refer to. It needs to know the "points-to" set for every alias at every point in the program and verify that, at the moment of use, the alias does not point to an object whose lifetime has ended.
+
+This alias-based perspective is powerful because it generalizes beautifully. The "end of lifetime" event at `t2` doesn't have to be a variable going out of scope. It could be:
+
+* A call to `delete`, which ends the lifetime of a heap object.
+* A function `return`, which ends the lifetime of all its local variables.
+* A container modification, like `std::vector::push_back()`, which may reallocate storage, ending the lifetime of the objects in the old buffer and invalidating all existing iterators (aliases).
+
+By focusing on tracking aliases and their validity, we can build a unified model to detect a wide range of temporal safety errors without imposing the heavy "aliasing XOR mutability" restrictions of a traditional borrow checker ([source](https://gist.github.com/nmsmith/cdaa94aa74e8e0611221e65db8e41f7b?content_ref=the+major+advancement+is+to+eliminate+the+aliasing+xor+mutability+restriction+amongst+references+and+replace+it+with+a+similar+restriction+applied+to+lifetime+parameters)). This provides a more intuitive and C++-idiomatic path to memory safety.
+
+## Relation with Thread safety
+
+This analysis does not address Thread Safety. Thread safety is concerned with data races that occur across multiple threads. While it is possible to create temporal safety issues in multi-threaded scenarios, this analysis is focused on the sequential lifetime of objects within a single function.
+
+## Quest for Safer Aliasing
+
+Is it possible to achieve memory safety without a restrictive model like Rust's borrow checker? We believe the answer is yes. The key is to shift our focus from *restricting aliases* to *understanding them*. Instead of forbidding programs that have aliased mutable pointers, we can build a model that understands what each pointer can point to at any given time. This approach, similar to the one proposed in P1179 for C++ and explored in modern lifetime systems like Mojo's, allows us to directly detect the root cause of the problem: using a pointer after its target has ceased to exist ([source](http://docs.google.com/document/d/19vbfAiV1yQu3xSMRWjyPUdzyB_LDdVUcKat_HWI1l3g?content_ref=this+approach+similar+to+the+one+proposed+in+p1179+for+c+and+explored+in+modern+lifetime+systems+like+mojo+s+allows+us+to+directly+detect+the+root+cause+of+the+problem+using+a+pointer+after+its+target+has+ceased+to+exist)).
+
+This paper proposes such a model for C++. Let's begin with a simple, yet illustrative, dangling pointer bug:
+
+```cpp
+// Example 1: A simple use-after-free
+void definite_simple_case() {
+ MyObj* p;
+ {
+ MyObj s;
+ p = &s; // 'p' now points to 's'
+ } // 's' is destroyed, 'p' is now dangling
+ (void)*p; // Use-after-free
+}
+```
+
+How can a compiler understand that the use of `p` is an error? It needs to answer a series of questions:
+
+1. What does `p` point to?
+2. When does the object `p` points to cease to be valid?
+3. Is `p` used after that point?
+
+Our model is designed to answer precisely these questions.
+
+## Core Concepts
+
+Our model is built on a few core concepts that allow us to formally track the relationships between pointers and the data they point to.
+
+### Access Paths
+
+An **Access Path** is a symbolic representation of a storage location in the program ([source](https://raw.githubusercontent.com/llvm/llvm-project/0e7c1732a9a7d28549fe5d690083daeb0e5de6b2/clang/lib/Analysis/LifetimeSafety.cpp?content_ref=struct+accesspath+const+clang+valuedecl+d+accesspath+const+clang+valuedecl+d+d+d)). It provides a way to uniquely identify a variable or a sub-object. For now, we will consider simple paths that refer to top-level variables, but the model can be extended to include field accesses (`a.b`), array elements (`a[i]`), and pointer indirections (`p->field`).
+
+### Loans: The Act of Borrowing
+
+A **Loan** is created whenever a reference or pointer to an object is created. It represents the act of "borrowing" that object's storage location ([source](https://raw.githubusercontent.com/llvm/llvm-project/0e7c1732a9a7d28549fe5d690083daeb0e5de6b2/clang/lib/Analysis/LifetimeSafety.cpp?content_ref=information+about+a+single+borrow+or+loan+a+loan+is+created+when+a+reference+or+pointer+is+created)). Each loan is associated with a unique ID and the `AccessPath` of the object being borrowed.
+
+In our `definite_simple_case` example, the expression `&s` creates a loan. The `AccessPath` for this loan is the variable `s`.
+
+### Origins: The Provenance of a Pointer
+
+An **Origin** is a symbolic identifier that represents the *set of possible loans* a pointer-like object could hold at any given time ([source](http://docs.google.com/document/d/1JpJ3M9yeXX-BnC4oKXBvRWzxoFrwziN1RzI4DrMrSp8?content_ref=ime+is+a+symbolic+identifier+representing+a+set+of+loans+from+which+a+pointer+or+reference+could+have+originated)). Every pointer-like variable or expression in the program is associated with an origin.
+
+* A variable declaration like `MyObj* p` introduces an origin for `p`.
+* An expression like `&s` also has an origin.
+* The complexity of origins can grow with type complexity. For example:
+ * `int* p;` has a single origin.
+ * `int** p;` has two origins: one for the outer pointer and one for the inner pointer. This allows us to distinguish between `p` itself being modified and what `*p` points to being modified.
+ * `struct S { int* p; };` also has an origin associated with the member `p`.
+
+The central goal of our analysis is to determine, for each origin at each point in the program, which loans it might contain.
+
+## Subtyping Rules and Subset Constraints
+
+The relationships between origins are established through the program's semantics, particularly assignments. When a pointer is assigned to another, as in `p = q`, the set of loans that `q` holds must be a subset of the loans that `p` can now hold. This is a fundamental subtyping rule: for `T*'a` to be a subtype of `T*'b`, the set of loans represented by `'a` must be a subset of the loans represented by `'b`.
+
+This leads to the concept of **subset constraints**. An assignment `p = q` generates a constraint `Origin(q) ⊆ Origin(p)`. The analysis doesn't solve these as a global system of equations. Instead, as we will see, it propagates the *consequences* of these constraints—the loans themselves—through the control-flow graph. This is a key departure from the Polonius model, which focuses on propagating the constraints (`'a: 'b`) themselves.
+
+## Invalidations: When Loans Expire
+
+A loan expires when the object it refers to is no longer valid. In our model, this is an **invalidation** event. The most common invalidation is deallocation, which in C++ can mean:
+* A stack variable going out of scope.
+* A `delete` call on a heap-allocated object.
+* A container modification that reallocates its internal storage.
+
+## An Event-Based Representation of the Function
+
+To analyze a function, we first transform its CFG into a sequence of atomic, lifetime-relevant **Events**, which we call **Facts**. These facts abstract away the complexities of C++ syntax and provide a clean input for our analysis. The main facts are:
+
+* `Issue(LoanID, OriginID)`: A new loan is created. For example, `&s` generates an `Issue` fact.
+* `Expire(LoanID)`: A loan expires. This is generated at the end of a variable's scope.
+* `OriginFlow(Dest, Src, Kill)`: Loans from a source origin flow to a destination origin, as in an assignment. `Kill` indicates whether the destination's old loans are cleared.
+* `Use(OriginID)`: An origin is used, such as in a pointer dereference.
+
+Let's trace our `definite_simple_case` example with these facts:
+
+```cpp
+void definite_simple_case() {
+ MyObj* p; // Origin for p is O_p
+ {
+ MyObj s;
+ // The expression `&s` generates:
+ // - IssueFact(L1, O_&s) (A new loan L1 on 's' is created)
+ // The assignment `p = &s` generates:
+ // - OriginFlowFact(O_p, O_&s, Kill=true)
+ p = &s;
+ } // The end of the scope for 's' generates:
+ // - ExpireFact(L1)
+ // The dereference `*p` generates:
+ // - UseFact(O_p)
+ (void)*p;
+}
+```
+
+## Flow-Sensitive Lifetime Policy
+
+With the program represented as a stream of facts, we can now define a flow-sensitive policy to answer our three core questions. We do this by maintaining a map from `Origin` to `Set<Loan>` at each program point. This map represents the state of our analysis.
+
+The analysis proceeds as follows:
+1. **Forward Propagation of Loans:** We perform a forward dataflow analysis.
+ * When we encounter an `Issue` fact, we add the new loan to its origin's loan set.
+ * When we see an `OriginFlow` fact, we update the destination origin's loan set with the loans from the source.
+ * At control-flow merge points, we take the *union* of the loan sets from all incoming branches.
+
+2. **Backward Propagation of Liveness:** We then perform a backward dataflow analysis, starting from `Use` facts.
+ * A `Use` of an origin marks it as "live."
+ * This liveness information is propagated backward. If an origin `O_p` is live, and it received its loans from `O_q`, then `O_q` is also considered live at that point.
+
+3. **Error Detection:** An error is flagged when the analysis determines that a **live** origin contains a loan that has **expired**.
+
+In our `definite_simple_case` example:
+* The forward analysis determines that at the point of use, `Origin(p)` contains `Loan(s)`.
+* The backward analysis determines that at the point where `s` is destroyed, `Origin(p)` is live.
+* The `ExpireFact` for `Loan(s)` occurs before the `UseFact`.
+* The combination of these three conditions triggers a use-after-free error.
+
+## Without Functions, Our Work is Done Here!
+
+The model described so far works perfectly for a single, monolithic function. However, the moment we introduce function calls, the problem becomes more complex. How do we reason about lifetimes across function boundaries, especially when we can't see the implementation of the called function?
+
+### Effects of a Function Call
+
+A function call has inputs and outputs. From a lifetime perspective, the key challenge is to understand how the lifetimes of the outputs relate to the lifetimes of the inputs.
+
+### Outlives Constraints and Placeholder Origins
+
+When analyzing a function like `const char* get_prefix(const string& s, int len)`, we don't know the specific lifetime of the `s` that will be passed by the caller. To handle this, we introduce **placeholder origins** for the input parameters. These placeholders act as variables in our analysis.
+
+If a function returns a pointer or reference, its lifetime must be tied to one of its inputs. This is an **outlives constraint**. For example, the return value of `get_prefix` must "outlive" the input `s`. In our model, this means the origin of the return value will contain the placeholder loan associated with `s`.
+
+### Opaque Functions
+
+What if a function's implementation is not visible (e.g., it's in a separate translation unit), and it has no lifetime annotations? In this case, we must be conservative. If we pass a pointer to an opaque function, we have to assume it might have been invalidated. Our model handles this by associating a special **OPAQUE loan** with the pointer after the call, signifying that its lifetime is now unknown.
+
+## Why a Borrow Checker is Not the Right Fit for C++
+
+The "aliasing XOR mutability" rule, while powerful, is fundamentally at odds with many idiomatic C++ patterns.
+* **Observer Patterns:** It's common to have multiple non-owning pointers observing a mutable object.
+* **Intrusive Data Structures:** Data structures like intrusive linked lists require objects to hold pointers to one another, creating cycles that are difficult for a traditional borrow checker to handle.
+* **Iterator Invalidation:** The core problem in C++ is often not aliasing itself, but the fact that a mutation can invalidate an alias (e.g., resizing a vector). An alias-based analysis, like the one proposed here, directly models this problem, whereas a borrow checker can feel like an indirect and overly restrictive solution.
+
+By focusing on tracking what pointers can point to, our model avoids rejecting these safe and useful patterns, making it a more natural fit for the existing C++ ecosystem.
+
+## Open Questions
+
+* **When and if to introduce the term "lifetime"?** The term "lifetime" is heavily associated with Rust's model. This paper has intentionally focused on "Origins" and "Loans" to avoid confusion. Is there a point where introducing "lifetime" would be helpful, or should we stick to the new terminology?
+* **Syntax for Annotations:** While this model is designed to work with minimal annotations, some will be necessary for complex cases. What should the syntax for these annotations look like? Can we build on existing attributes like `[[clang::lifetimebound]]`?
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index dc3778b..2b89370 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -537,33 +537,16 @@ WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
}
void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
- // Drop the default section introduced by special case list, we only support
- // exact diagnostic group names.
- // FIXME: We should make this configurable in the parser instead.
- // FIXME: C++20 can use std::erase_if(Sections, [](Section &sec) { return
- // sec.SectionStr == "*"; });
- llvm::erase_if(Sections, [](Section &sec) { return sec.SectionStr == "*"; });
- // Make sure we iterate sections by their line numbers.
- std::vector<std::pair<unsigned, const Section *>> LineAndSectionEntry;
- LineAndSectionEntry.reserve(Sections.size());
- for (const auto &Entry : Sections) {
- StringRef DiagName = Entry.SectionStr;
- // Each section has a matcher with that section's name, attached to that
- // line.
- const auto &DiagSectionMatcher = Entry.SectionMatcher;
- unsigned DiagLine = 0;
- for (const auto &Glob : DiagSectionMatcher->Globs)
- if (Glob->Name == DiagName) {
- DiagLine = Glob->LineNo;
- break;
- }
- LineAndSectionEntry.emplace_back(DiagLine, &Entry);
- }
- llvm::sort(LineAndSectionEntry);
static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
- for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+ for (const auto &SectionEntry : Sections) {
+ StringRef DiagGroup = SectionEntry.SectionStr;
+ if (DiagGroup == "*") {
+ // Drop the default section introduced by special case list, we only
+ // support exact diagnostic group names.
+ // FIXME: We should make this configurable in the parser instead.
+ continue;
+ }
SmallVector<diag::kind> GroupDiags;
- StringRef DiagGroup = SectionEntry->SectionStr;
if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
WarningFlavor, DiagGroup, GroupDiags)) {
StringRef Suggestion =
@@ -576,7 +559,7 @@ void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
for (diag::kind Diag : GroupDiags)
// We're intentionally overwriting any previous mappings here to make sure
// latest one takes precedence.
- DiagToSection[Diag] = SectionEntry;
+ DiagToSection[Diag] = &SectionEntry;
}
}
diff --git a/clang/lib/Basic/SanitizerSpecialCaseList.cpp b/clang/lib/Basic/SanitizerSpecialCaseList.cpp
index f7bc1d5..792000b 100644
--- a/clang/lib/Basic/SanitizerSpecialCaseList.cpp
+++ b/clang/lib/Basic/SanitizerSpecialCaseList.cpp
@@ -38,11 +38,11 @@ SanitizerSpecialCaseList::createOrDie(const std::vector<std::string> &Paths,
}
void SanitizerSpecialCaseList::createSanitizerSections() {
- for (auto &S : Sections) {
+ for (const auto &S : Sections) {
SanitizerMask Mask;
#define SANITIZER(NAME, ID) \
- if (S.SectionMatcher->match(NAME)) \
+ if (S.SectionMatcher.matchAny(NAME)) \
Mask |= SanitizerKind::ID;
#define SANITIZER_GROUP(NAME, ID, ALIAS) SANITIZER(NAME, ID)
@@ -50,7 +50,7 @@ void SanitizerSpecialCaseList::createSanitizerSections() {
#undef SANITIZER
#undef SANITIZER_GROUP
- SanitizerSections.emplace_back(Mask, S.Entries, S.FileIdx);
+ SanitizerSections.emplace_back(Mask, S);
}
}
@@ -66,10 +66,9 @@ SanitizerSpecialCaseList::inSectionBlame(SanitizerMask Mask, StringRef Prefix,
StringRef Category) const {
for (const auto &S : llvm::reverse(SanitizerSections)) {
if (S.Mask & Mask) {
- unsigned LineNum =
- SpecialCaseList::inSectionBlame(S.Entries, Prefix, Query, Category);
+ unsigned LineNum = S.S.getLastMatch(Prefix, Query, Category);
if (LineNum > 0)
- return {S.FileIdx, LineNum};
+ return {S.S.FileIdx, LineNum};
}
}
return NotFound;
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
index 5f1faab..df42af8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
@@ -15,6 +15,7 @@
#include "CIRGenFunction.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/GlobalDecl.h"
using namespace clang;
@@ -75,3 +76,20 @@ void CIRGenCXXABI::setCXXABIThisValue(CIRGenFunction &cgf,
assert(getThisDecl(cgf) && "no 'this' variable for function");
cgf.cxxabiThisValue = thisPtr;
}
+
+CharUnits CIRGenCXXABI::getArrayCookieSize(const CXXNewExpr *e) {
+ if (!requiresArrayCookie(e))
+ return CharUnits::Zero();
+
+ cgm.errorNYI(e->getSourceRange(), "CIRGenCXXABI::getArrayCookieSize");
+ return CharUnits::Zero();
+}
+
+bool CIRGenCXXABI::requiresArrayCookie(const CXXNewExpr *e) {
+ // If the class's usual deallocation function takes two arguments,
+ // it needs a cookie.
+ if (e->doesUsualArrayDeleteWantSize())
+ return true;
+
+ return e->getAllocatedType().isDestructedType();
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 1dee774..2465a68 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -28,6 +28,8 @@ protected:
CIRGenModule &cgm;
std::unique_ptr<clang::MangleContext> mangleContext;
+ virtual bool requiresArrayCookie(const CXXNewExpr *e);
+
public:
// TODO(cir): make this protected when target-specific CIRGenCXXABIs are
// implemented.
@@ -113,6 +115,7 @@ public:
CIRGenFunction &cgf) = 0;
virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0;
+ virtual void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) = 0;
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
QualType ty) = 0;
@@ -244,6 +247,19 @@ public:
void setStructorImplicitParamValue(CIRGenFunction &cgf, mlir::Value val) {
cgf.cxxStructorImplicitParamValue = val;
}
+
+ /**************************** Array cookies ******************************/
+
+ /// Returns the extra size required in order to store the array
+ /// cookie for the given new-expression. May return 0 to indicate that no
+ /// array cookie is required.
+ ///
+ /// Several cases are filtered out before this method is called:
+ /// - non-array allocations never need a cookie
+ /// - calls to \::operator new(size_t, void*) never need a cookie
+ ///
+ /// \param E - the new-expression being allocated.
+ virtual CharUnits getArrayCookieSize(const CXXNewExpr *e);
};
/// Creates and Itanium-family ABI
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.h b/clang/lib/CIR/CodeGen/CIRGenCleanup.h
index a4ec8cc..30f5607 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.h
@@ -104,6 +104,7 @@ public:
bool isNormalCleanup() const { return cleanupBits.isNormalCleanup; }
bool isActive() const { return cleanupBits.isActive; }
+ void setActive(bool isActive) { cleanupBits.isActive = isActive; }
size_t getCleanupSize() const { return cleanupBits.cleanupSize; }
void *getCleanupBuffer() { return this + 1; }
@@ -138,5 +139,13 @@ inline EHScopeStack::iterator EHScopeStack::begin() const {
return iterator(startOfData);
}
+inline EHScopeStack::iterator
+EHScopeStack::find(stable_iterator savePoint) const {
+ assert(savePoint.isValid() && "finding invalid savepoint");
+ assert(savePoint.size <= stable_begin().size &&
+ "finding savepoint after pop");
+ return iterator(endOfBuffer - savePoint.size);
+}
+
} // namespace clang::CIRGen
#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 7fcb39a..6453843 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -31,11 +31,36 @@ void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
if (throwType->isObjCObjectPointerType()) {
cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
return;
- } else {
- cgm.errorNYI("emitCXXThrowExpr with subExpr");
- return;
}
- } else {
- cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
+
+ cgm.getCXXABI().emitThrow(*this, e);
+ return;
}
+
+ cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
+}
+
+void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) {
+ // Make sure the exception object is cleaned up if there's an
+ // exception during initialization.
+ assert(!cir::MissingFeatures::ehCleanupScope());
+
+ // __cxa_allocate_exception returns a void*; we need to cast this
+ // to the appropriate type for the object.
+ mlir::Type ty = convertTypeForMem(e->getType());
+ Address typedAddr = addr.withElementType(builder, ty);
+
+ // From LLVM's codegen:
+ // FIXME: this isn't quite right! If there's a final unelided call
+ // to a copy constructor, then according to [except.terminate]p1 we
+ // must call std::terminate() if that constructor throws, because
+ // technically that copy occurs after the exception expression is
+ // evaluated but before the exception is caught. But the best way
+ // to handle that is to teach EmitAggExpr to do the final copy
+ // differently if it can't be elided.
+ emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
+ /*isInitializer=*/true);
+
+ // Deactivate the cleanup block.
+ assert(!cir::MissingFeatures::ehCleanupScope());
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 7989ad2..4eb8ca8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "CIRGenCXXABI.h"
+#include "CIRGenConstantEmitter.h"
#include "CIRGenFunction.h"
#include "clang/AST/DeclCXX.h"
@@ -210,6 +211,19 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorCall(
return emitCall(fnInfo, callee, returnValue, args, nullptr, loc);
}
+static CharUnits calculateCookiePadding(CIRGenFunction &cgf,
+ const CXXNewExpr *e) {
+ if (!e->isArray())
+ return CharUnits::Zero();
+
+ // No cookie is required if the operator new[] being used is the
+ // reserved placement operator new[].
+ if (e->getOperatorNew()->isReservedGlobalPlacementOperator())
+ return CharUnits::Zero();
+
+ return cgf.cgm.getCXXABI().getArrayCookieSize(e);
+}
+
static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
unsigned minElements,
mlir::Value &numElements,
@@ -224,8 +238,98 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
return sizeWithoutCookie;
}
- cgf.cgm.errorNYI(e->getSourceRange(), "emitCXXNewAllocSize: array");
- return {};
+ // The width of size_t.
+ unsigned sizeWidth = cgf.cgm.getDataLayout().getTypeSizeInBits(cgf.SizeTy);
+
+ // The number of elements can be have an arbitrary integer type;
+ // essentially, we need to multiply it by a constant factor, add a
+ // cookie size, and verify that the result is representable as a
+ // size_t. That's just a gloss, though, and it's wrong in one
+ // important way: if the count is negative, it's an error even if
+ // the cookie size would bring the total size >= 0.
+ //
+ // If the array size is constant, Sema will have prevented negative
+ // values and size overflow.
+
+ // Compute the constant factor.
+ llvm::APInt arraySizeMultiplier(sizeWidth, 1);
+ while (const ConstantArrayType *cat =
+ cgf.getContext().getAsConstantArrayType(type)) {
+ type = cat->getElementType();
+ arraySizeMultiplier *= cat->getSize();
+ }
+
+ CharUnits typeSize = cgf.getContext().getTypeSizeInChars(type);
+ llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity());
+ typeSizeMultiplier *= arraySizeMultiplier;
+
+ // Figure out the cookie size.
+ llvm::APInt cookieSize(sizeWidth,
+ calculateCookiePadding(cgf, e).getQuantity());
+
+ // This will be a size_t.
+ mlir::Value size;
+
+ // Emit the array size expression.
+ // We multiply the size of all dimensions for NumElements.
+ // e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
+ const Expr *arraySize = *e->getArraySize();
+ mlir::Attribute constNumElements =
+ ConstantEmitter(cgf.cgm, &cgf)
+ .emitAbstract(arraySize, arraySize->getType());
+ if (constNumElements) {
+ // Get an APInt from the constant
+ const llvm::APInt &count =
+ mlir::cast<cir::IntAttr>(constNumElements).getValue();
+
+ unsigned numElementsWidth = count.getBitWidth();
+
+ // The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
+ // overflow, but that should never happen. The size argument is implicitly
+ // cast to a size_t, so it can never be negative and numElementsWidth will
+ // always equal sizeWidth.
+ assert(!count.isNegative() && "Expected non-negative array size");
+ assert(numElementsWidth == sizeWidth &&
+ "Expected a size_t array size constant");
+
+ // Okay, compute a count at the right width.
+ llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth);
+
+ // Scale numElements by that. This might overflow, but we don't
+ // care because it only overflows if allocationSize does too, and
+ // if that overflows then we shouldn't use this.
+ // This emits a constant that may not be used, but we can't tell here
+ // whether it will be needed or not.
+ numElements =
+ cgf.getBuilder().getConstInt(loc, adjustedCount * arraySizeMultiplier);
+
+ // Compute the size before cookie, and track whether it overflowed.
+ bool overflow;
+ llvm::APInt allocationSize =
+ adjustedCount.umul_ov(typeSizeMultiplier, overflow);
+
+ // Sema prevents us from hitting this case
+ assert(!overflow && "Overflow in array allocation size");
+
+ // Add in the cookie, and check whether it's overflowed.
+ if (cookieSize != 0) {
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "emitCXXNewAllocSize: array cookie");
+ }
+
+ size = cgf.getBuilder().getConstInt(loc, allocationSize);
+ } else {
+ // TODO: Handle the variable size case
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "emitCXXNewAllocSize: variable array size");
+ }
+
+ if (cookieSize == 0)
+ sizeWithoutCookie = size;
+ else
+ assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?");
+
+ return size;
}
static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
@@ -254,13 +358,26 @@ static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init,
llvm_unreachable("bad evaluation kind");
}
+void CIRGenFunction::emitNewArrayInitializer(
+ const CXXNewExpr *e, QualType elementType, mlir::Type elementTy,
+ Address beginPtr, mlir::Value numElements,
+ mlir::Value allocSizeWithoutCookie) {
+ // If we have a type with trivial initialization and no initializer,
+ // there's nothing to do.
+ if (!e->hasInitializer())
+ return;
+
+ cgm.errorNYI(e->getSourceRange(), "emitNewArrayInitializer");
+}
+
static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e,
QualType elementType, mlir::Type elementTy,
Address newPtr, mlir::Value numElements,
mlir::Value allocSizeWithoutCookie) {
assert(!cir::MissingFeatures::generateDebugInfo());
if (e->isArray()) {
- cgf.cgm.errorNYI(e->getSourceRange(), "emitNewInitializer: array");
+ cgf.emitNewArrayInitializer(e, elementType, elementTy, newPtr, numElements,
+ allocSizeWithoutCookie);
} else if (const Expr *init = e->getInitializer()) {
storeAnyExprIntoOneUnit(cgf, init, e->getAllocatedType(), newPtr,
AggValueSlot::DoesNotOverlap);
@@ -536,7 +653,14 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
if (allocSize != allocSizeWithoutCookie)
cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: array with cookies");
- mlir::Type elementTy = convertTypeForMem(allocType);
+ mlir::Type elementTy;
+ if (e->isArray()) {
+ // For array new, use the allocated type to handle multidimensional arrays
+ // correctly
+ elementTy = convertTypeForMem(e->getAllocatedType());
+ } else {
+ elementTy = convertTypeForMem(allocType);
+ }
Address result = builder.createElementBitCast(getLoc(e->getSourceRange()),
allocation, elementTy);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index e20a4fc..59aa257 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -118,6 +118,9 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
/// non-packed LLVM struct will give the correct layout.
bool naturalLayout = true;
+ bool split(size_t index, CharUnits hint);
+ std::optional<size_t> splitAt(CharUnits pos);
+
static mlir::Attribute buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
CharUnits startOffset, CharUnits size,
bool naturalLayout, mlir::Type desiredTy,
@@ -137,6 +140,10 @@ public:
/// Update or overwrite the bits starting at \p offsetInBits with \p bits.
bool addBits(llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite);
+ /// Attempt to condense the value starting at \p offset to a constant of type
+ /// \p desiredTy.
+ void condense(CharUnits offset, mlir::Type desiredTy);
+
/// Produce a constant representing the entire accumulated value, ideally of
/// the specified type. If \p allowOversized, the constant might be larger
/// than implied by \p desiredTy (eg, if there is a flexible array member).
@@ -176,6 +183,195 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset,
return false;
}
+bool ConstantAggregateBuilder::addBits(llvm::APInt bits, uint64_t offsetInBits,
+ bool allowOverwrite) {
+ const ASTContext &astContext = cgm.getASTContext();
+ const uint64_t charWidth = astContext.getCharWidth();
+ mlir::Type charTy = cgm.getBuilder().getUIntNTy(charWidth);
+
+ // Offset of where we want the first bit to go within the bits of the
+ // current char.
+ unsigned offsetWithinChar = offsetInBits % charWidth;
+
+ // We split bit-fields up into individual bytes. Walk over the bytes and
+ // update them.
+ for (CharUnits offsetInChars =
+ astContext.toCharUnitsFromBits(offsetInBits - offsetWithinChar);
+ /**/; ++offsetInChars) {
+ // Number of bits we want to fill in this char.
+ unsigned wantedBits =
+ std::min((uint64_t)bits.getBitWidth(), charWidth - offsetWithinChar);
+
+ // Get a char containing the bits we want in the right places. The other
+ // bits have unspecified values.
+ llvm::APInt bitsThisChar = bits;
+ if (bitsThisChar.getBitWidth() < charWidth)
+ bitsThisChar = bitsThisChar.zext(charWidth);
+ if (cgm.getDataLayout().isBigEndian()) {
+ // Figure out how much to shift by. We may need to left-shift if we have
+ // less than one byte of Bits left.
+ int shift = bits.getBitWidth() - charWidth + offsetWithinChar;
+ if (shift > 0)
+ bitsThisChar.lshrInPlace(shift);
+ else if (shift < 0)
+ bitsThisChar = bitsThisChar.shl(-shift);
+ } else {
+ bitsThisChar = bitsThisChar.shl(offsetWithinChar);
+ }
+ if (bitsThisChar.getBitWidth() > charWidth)
+ bitsThisChar = bitsThisChar.trunc(charWidth);
+
+ if (wantedBits == charWidth) {
+ // Got a full byte: just add it directly.
+ add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
+ allowOverwrite);
+ } else {
+ // Partial byte: update the existing integer if there is one. If we
+ // can't split out a 1-CharUnit range to update, then we can't add
+ // these bits and fail the entire constant emission.
+ std::optional<size_t> firstElemToUpdate = splitAt(offsetInChars);
+ if (!firstElemToUpdate)
+ return false;
+ std::optional<size_t> lastElemToUpdate =
+ splitAt(offsetInChars + CharUnits::One());
+ if (!lastElemToUpdate)
+ return false;
+ assert(*lastElemToUpdate - *firstElemToUpdate < 2 &&
+ "should have at most one element covering one byte");
+
+ // Figure out which bits we want and discard the rest.
+ llvm::APInt updateMask(charWidth, 0);
+ if (cgm.getDataLayout().isBigEndian())
+ updateMask.setBits(charWidth - offsetWithinChar - wantedBits,
+ charWidth - offsetWithinChar);
+ else
+ updateMask.setBits(offsetWithinChar, offsetWithinChar + wantedBits);
+ bitsThisChar &= updateMask;
+ bool isNull = false;
+ if (*firstElemToUpdate < elements.size()) {
+ auto firstEltToUpdate =
+ mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
+ isNull = firstEltToUpdate && firstEltToUpdate.isNullValue();
+ }
+
+ if (*firstElemToUpdate == *lastElemToUpdate || isNull) {
+ // All existing bits are either zero or undef.
+ add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
+ /*allowOverwrite*/ true);
+ } else {
+ cir::IntAttr ci =
+ mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
+ // In order to perform a partial update, we need the existing bitwise
+ // value, which we can only extract for a constant int.
+ if (!ci)
+ return false;
+ // Because this is a 1-CharUnit range, the constant occupying it must
+ // be exactly one CharUnit wide.
+ assert(ci.getBitWidth() == charWidth && "splitAt failed");
+ assert((!(ci.getValue() & updateMask) || allowOverwrite) &&
+ "unexpectedly overwriting bitfield");
+ bitsThisChar |= (ci.getValue() & ~updateMask);
+ elements[*firstElemToUpdate].element =
+ cir::IntAttr::get(charTy, bitsThisChar);
+ }
+ }
+
+ // Stop if we've added all the bits.
+ if (wantedBits == bits.getBitWidth())
+ break;
+
+ // Remove the consumed bits from Bits.
+ if (!cgm.getDataLayout().isBigEndian())
+ bits.lshrInPlace(wantedBits);
+ bits = bits.trunc(bits.getBitWidth() - wantedBits);
+
+ // The remaining bits go at the start of the following bytes.
+ offsetWithinChar = 0;
+ }
+
+ return true;
+}
+
+/// Returns a position within elements such that all elements
+/// before the returned index end before pos and all elements at or after
+/// the returned index begin at or after pos. Splits elements as necessary
+/// to ensure this. Returns std::nullopt if we find something we can't split.
+std::optional<size_t> ConstantAggregateBuilder::splitAt(CharUnits pos) {
+ if (pos >= size)
+ return elements.size();
+
+ while (true) {
+ // Find the first element that starts after pos.
+ Element *iter =
+ llvm::upper_bound(elements, pos, [](CharUnits pos, const Element &elt) {
+ return pos < elt.offset;
+ });
+
+ if (iter == elements.begin())
+ return 0;
+
+ size_t index = iter - elements.begin() - 1;
+ const Element &elt = elements[index];
+
+ // If we already have an element starting at pos, we're done.
+ if (elt.offset == pos)
+ return index;
+
+ // Check for overlap with the element that starts before pos.
+ CharUnits eltEnd = elt.offset + getSize(elt.element);
+ if (eltEnd <= pos)
+ return index + 1;
+
+ // Try to decompose it into smaller constants.
+ if (!split(index, pos))
+ return std::nullopt;
+ }
+}
+
+/// Split the constant at index, if possible. Return true if we did.
+/// Hint indicates the location at which we'd like to split, but may be
+/// ignored.
+bool ConstantAggregateBuilder::split(size_t index, CharUnits hint) {
+ cgm.errorNYI("split constant at index");
+ return false;
+}
+
+void ConstantAggregateBuilder::condense(CharUnits offset,
+ mlir::Type desiredTy) {
+ CharUnits desiredSize = getSize(desiredTy);
+
+ std::optional<size_t> firstElemToReplace = splitAt(offset);
+ if (!firstElemToReplace)
+ return;
+ size_t first = *firstElemToReplace;
+
+ std::optional<size_t> lastElemToReplace = splitAt(offset + desiredSize);
+ if (!lastElemToReplace)
+ return;
+ size_t last = *lastElemToReplace;
+
+ size_t length = last - first;
+ if (length == 0)
+ return;
+
+ if (length == 1 && elements[first].offset == offset &&
+ getSize(elements[first].element) == desiredSize) {
+ cgm.errorNYI("re-wrapping single element records");
+ return;
+ }
+
+ // Build a new constant from the elements in the range.
+ SmallVector<Element> subElems(elements.begin() + first,
+ elements.begin() + last);
+ mlir::Attribute replacement =
+ buildFrom(cgm, subElems, offset, desiredSize,
+ /*naturalLayout=*/false, desiredTy, false);
+
+ // Replace the range with the condensed constant.
+ Element newElt(mlir::cast<mlir::TypedAttr>(replacement), offset);
+ replace(elements, first, last, {newElt});
+}
+
mlir::Attribute
ConstantAggregateBuilder::buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
CharUnits startOffset, CharUnits size,
@@ -301,6 +497,9 @@ private:
bool appendBytes(CharUnits fieldOffsetInChars, mlir::TypedAttr initCst,
bool allowOverwrite = false);
+ bool appendBitField(const FieldDecl *field, uint64_t fieldOffset,
+ cir::IntAttr ci, bool allowOverwrite = false);
+
bool build(InitListExpr *ile, bool allowOverwrite);
bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
const CXXRecordDecl *vTableClass, CharUnits baseOffset);
@@ -325,6 +524,30 @@ bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars,
return builder.add(initCst, startOffset + fieldOffsetInChars, allowOverwrite);
}
+bool ConstRecordBuilder::appendBitField(const FieldDecl *field,
+ uint64_t fieldOffset, cir::IntAttr ci,
+ bool allowOverwrite) {
+ const CIRGenRecordLayout &rl =
+ cgm.getTypes().getCIRGenRecordLayout(field->getParent());
+ const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(field);
+ llvm::APInt fieldValue = ci.getValue();
+
+ // Promote the size of FieldValue if necessary
+ // FIXME: This should never occur, but currently it can because initializer
+ // constants are cast to bool, and because clang is not enforcing bitfield
+ // width limits.
+ if (info.size > fieldValue.getBitWidth())
+ fieldValue = fieldValue.zext(info.size);
+
+ // Truncate the size of FieldValue to the bit field size.
+ if (info.size < fieldValue.getBitWidth())
+ fieldValue = fieldValue.trunc(info.size);
+
+ return builder.addBits(fieldValue,
+ cgm.getASTContext().toBits(startOffset) + fieldOffset,
+ allowOverwrite);
+}
+
bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
RecordDecl *rd = ile->getType()
->castAs<clang::RecordType>()
@@ -407,12 +630,14 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
} else {
// Otherwise we have a bitfield.
if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
- assert(!cir::MissingFeatures::bitfields());
- cgm.errorNYI(field->getSourceRange(), "bitfields");
+ if (!appendBitField(field, layout.getFieldOffset(index), constInt,
+ allowOverwrite))
+ return false;
+ } else {
+ // We are trying to initialize a bitfield with a non-trivial constant,
+ // this must require run-time code.
+ return false;
}
- // We are trying to initialize a bitfield with a non-trivial constant,
- // this must require run-time code.
- return false;
}
}
@@ -510,8 +735,16 @@ bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd,
if (field->hasAttr<NoUniqueAddressAttr>())
allowOverwrite = true;
} else {
- assert(!cir::MissingFeatures::bitfields());
- cgm.errorNYI(field->getSourceRange(), "bitfields");
+ // Otherwise we have a bitfield.
+ if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
+ if (!appendBitField(field, layout.getFieldOffset(index) + offsetBits,
+ constInt, allowOverwrite))
+ return false;
+ } else {
+ // We are trying to initialize a bitfield with a non-trivial constant,
+ // this must require run-time code.
+ return false;
+ }
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index cbc0f4a..a60efe1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1090,6 +1090,8 @@ public:
/// even if no aggregate location is provided.
RValue emitAnyExprToTemp(const clang::Expr *e);
+ void emitAnyExprToExn(const Expr *e, Address addr);
+
void emitArrayDestroy(mlir::Value begin, mlir::Value numElements,
QualType elementType, CharUnits elementAlign,
Destroyer *destroyer);
@@ -1252,6 +1254,11 @@ public:
mlir::Value emitCXXNewExpr(const CXXNewExpr *e);
+ void emitNewArrayInitializer(const CXXNewExpr *E, QualType ElementType,
+ mlir::Type ElementTy, Address BeginPtr,
+ mlir::Value NumElements,
+ mlir::Value AllocSizeWithoutCookie);
+
RValue emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e,
const CXXMethodDecl *md,
ReturnValueSlot returnValue);
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index debea8af..0418174 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -70,6 +70,7 @@ public:
QualType thisTy) override;
void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
+ void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override;
bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
CXXDtorType dt) const override {
@@ -1544,6 +1545,59 @@ void CIRGenItaniumCXXABI::emitRethrow(CIRGenFunction &cgf, bool isNoReturn) {
}
}
+void CIRGenItaniumCXXABI::emitThrow(CIRGenFunction &cgf,
+ const CXXThrowExpr *e) {
+ // This differs a bit from LLVM codegen, CIR has native operations for some
+ // cxa functions, and defers allocation size computation, always pass the dtor
+ // symbol, etc. CIRGen also does not use getAllocateExceptionFn / getThrowFn.
+
+ // Now allocate the exception object.
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ QualType clangThrowType = e->getSubExpr()->getType();
+ cir::PointerType throwTy =
+ builder.getPointerTo(cgf.convertType(clangThrowType));
+ uint64_t typeSize =
+ cgf.getContext().getTypeSizeInChars(clangThrowType).getQuantity();
+ mlir::Location subExprLoc = cgf.getLoc(e->getSubExpr()->getSourceRange());
+
+ // Defer computing allocation size to some later lowering pass.
+ mlir::TypedValue<cir::PointerType> exceptionPtr =
+ cir::AllocExceptionOp::create(builder, subExprLoc, throwTy,
+ builder.getI64IntegerAttr(typeSize))
+ .getAddr();
+
+ // Build expression and store its result into exceptionPtr.
+ CharUnits exnAlign = cgf.getContext().getExnObjectAlignment();
+ cgf.emitAnyExprToExn(e->getSubExpr(), Address(exceptionPtr, exnAlign));
+
+ // Get the RTTI symbol address.
+ auto typeInfo = mlir::cast<cir::GlobalViewAttr>(
+ cgm.getAddrOfRTTIDescriptor(subExprLoc, clangThrowType,
+ /*forEH=*/true));
+ assert(!typeInfo.getIndices() && "expected no indirection");
+
+ // The address of the destructor.
+ //
+ // Note: LLVM codegen already optimizes out the dtor if the
+ // type is a record with trivial dtor (by passing down a
+ // null dtor). In CIR, we forward this info and allow for
+ // Lowering pass to skip passing the trivial function.
+ //
+ if (const RecordType *recordTy = clangThrowType->getAs<RecordType>()) {
+ CXXRecordDecl *rec =
+ cast<CXXRecordDecl>(recordTy->getOriginalDecl()->getDefinition());
+ assert(!cir::MissingFeatures::isTrivialCtorOrDtor());
+ if (!rec->hasTrivialDestructor()) {
+ cgm.errorNYI("emitThrow: non-trivial destructor");
+ return;
+ }
+ }
+
+ // Now throw the exception.
+ mlir::Location loc = cgf.getLoc(e->getSourceRange());
+ insertThrowAndSplit(builder, loc, exceptionPtr, typeInfo.getSymbol());
+}
+
CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
switch (cgm.getASTContext().getCXXABIKind()) {
case TargetCXXABI::GenericItanium:
diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h
index c87a6ef..66c1f76 100644
--- a/clang/lib/CIR/CodeGen/EHScopeStack.h
+++ b/clang/lib/CIR/CodeGen/EHScopeStack.h
@@ -175,6 +175,10 @@ public:
return stable_iterator(endOfBuffer - startOfData);
}
+ /// Turn a stable reference to a scope depth into a unstable pointer
+ /// to the EH stack.
+ iterator find(stable_iterator savePoint) const;
+
/// Create a stable reference to the bottom of the EH stack.
static stable_iterator stable_end() { return stable_iterator(0); }
};
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 3484c59..64ac970 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -473,6 +473,49 @@ LogicalResult cir::VTableAttr::verify(
}
//===----------------------------------------------------------------------===//
+// DynamicCastInfoAtttr definitions
+//===----------------------------------------------------------------------===//
+
+std::string DynamicCastInfoAttr::getAlias() const {
+ // The alias looks like: `dyn_cast_info_<src>_<dest>`
+
+ std::string alias = "dyn_cast_info_";
+
+ alias.append(getSrcRtti().getSymbol().getValue());
+ alias.push_back('_');
+ alias.append(getDestRtti().getSymbol().getValue());
+
+ return alias;
+}
+
+LogicalResult DynamicCastInfoAttr::verify(
+ function_ref<InFlightDiagnostic()> emitError, cir::GlobalViewAttr srcRtti,
+ cir::GlobalViewAttr destRtti, mlir::FlatSymbolRefAttr runtimeFunc,
+ mlir::FlatSymbolRefAttr badCastFunc, cir::IntAttr offsetHint) {
+ auto isRttiPtr = [](mlir::Type ty) {
+ // RTTI pointers are !cir.ptr<!u8i>.
+
+ auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty);
+ if (!ptrTy)
+ return false;
+
+ auto pointeeIntTy = mlir::dyn_cast<cir::IntType>(ptrTy.getPointee());
+ if (!pointeeIntTy)
+ return false;
+
+ return pointeeIntTy.isUnsigned() && pointeeIntTy.getWidth() == 8;
+ };
+
+ if (!isRttiPtr(srcRtti.getType()))
+ return emitError() << "srcRtti must be an RTTI pointer";
+
+ if (!isRttiPtr(destRtti.getType()))
+ return emitError() << "destRtti must be an RTTI pointer";
+
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
// CIR Dialect
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index cdd4e3c..5f88590 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -71,6 +71,10 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
os << "bfi_" << bitfield.getName().str();
return AliasResult::FinalAlias;
}
+ if (auto dynCastInfoAttr = mlir::dyn_cast<cir::DynamicCastInfoAttr>(attr)) {
+ os << dynCastInfoAttr.getAlias();
+ return AliasResult::FinalAlias;
+ }
return AliasResult::NoAlias;
}
};
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3a3c631..e9649af 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2581,22 +2581,69 @@ void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter,
mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite(
cir::ThrowOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- if (op.rethrows()) {
- auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext());
- auto funcTy =
- mlir::LLVM::LLVMFunctionType::get(getContext(), voidTy, {}, false);
+ mlir::Location loc = op.getLoc();
+ auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext());
- auto mlirModule = op->getParentOfType<mlir::ModuleOp>();
- rewriter.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
+ if (op.rethrows()) {
+ auto funcTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {});
+ // Get or create `declare void @__cxa_rethrow()`
const llvm::StringRef functionName = "__cxa_rethrow";
createLLVMFuncOpIfNotExist(rewriter, op, functionName, funcTy);
- rewriter.setInsertionPointAfter(op.getOperation());
- rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
- op, mlir::TypeRange{}, functionName, mlir::ValueRange{});
+ auto cxaRethrow = mlir::LLVM::CallOp::create(
+ rewriter, loc, mlir::TypeRange{}, functionName);
+
+ rewriter.replaceOp(op, cxaRethrow);
+ return mlir::success();
}
+ auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+ auto fnTy = mlir::LLVM::LLVMFunctionType::get(
+ voidTy, {llvmPtrTy, llvmPtrTy, llvmPtrTy});
+
+ // Get or create `declare void @__cxa_throw(ptr, ptr, ptr)`
+ const llvm::StringRef fnName = "__cxa_throw";
+ createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
+
+ mlir::Value typeInfo = mlir::LLVM::AddressOfOp::create(
+ rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
+ adaptor.getTypeInfoAttr());
+
+ mlir::Value dtor;
+ if (op.getDtor()) {
+ dtor = mlir::LLVM::AddressOfOp::create(rewriter, loc, llvmPtrTy,
+ adaptor.getDtorAttr());
+ } else {
+ dtor = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
+ }
+
+ auto cxaThrowCall = mlir::LLVM::CallOp::create(
+ rewriter, loc, mlir::TypeRange{}, fnName,
+ mlir::ValueRange{adaptor.getExceptionPtr(), typeInfo, dtor});
+
+ rewriter.replaceOp(op, cxaThrowCall);
+ return mlir::success();
+}
+
+mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
+ cir::AllocExceptionOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ // Get or create `declare ptr @__cxa_allocate_exception(i64)`
+ StringRef fnName = "__cxa_allocate_exception";
+ auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+ auto int64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
+ auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {int64Ty});
+
+ createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
+ auto exceptionSize = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
+ adaptor.getSizeAttr());
+
+ auto allocaExceptionCall = mlir::LLVM::CallOp::create(
+ rewriter, op.getLoc(), mlir::TypeRange{llvmPtrTy}, fnName,
+ mlir::ValueRange{exceptionSize});
+
+ rewriter.replaceOp(op, allocaExceptionCall);
return mlir::success();
}
diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.h b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.h
index 810d6aa..3a7ee54 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.h
+++ b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.h
@@ -163,12 +163,14 @@ public:
SourceLocation Loc) override;
// Currently unsupported on the device.
+ using CGOpenMPRuntime::emitMessageClause;
llvm::Value *emitMessageClause(CodeGenFunction &CGF, const Expr *Message,
SourceLocation Loc) override;
// Currently unsupported on the device.
- virtual llvm::Value *emitSeverityClause(OpenMPSeverityClauseKind Severity,
- SourceLocation Loc) override;
+ using CGOpenMPRuntime::emitSeverityClause;
+ llvm::Value *emitSeverityClause(OpenMPSeverityClauseKind Severity,
+ SourceLocation Loc) override;
/// Emits call to void __kmpc_push_num_threads(ident_t *loc, kmp_int32
/// global_tid, kmp_int32 num_threads) to generate code for 'num_threads'
diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index e19daa9..72a42a6 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -43,7 +43,7 @@ const char *Action::getClassName(ActionClass AC) {
case OffloadUnbundlingJobClass:
return "clang-offload-unbundler";
case OffloadPackagerJobClass:
- return "clang-offload-packager";
+ return "llvm-offload-binary";
case LinkerWrapperJobClass:
return "clang-linker-wrapper";
case StaticLibJobClass:
diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
index 98f5efb..eb5d542 100644
--- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp
@@ -57,6 +57,9 @@ std::string aarch64::getAArch64TargetCPU(const ArgList &Args,
// iOS 26 only runs on apple-a12 and later CPUs.
if (!Triple.isOSVersionLT(26))
return "apple-a12";
+ // arm64 (non-e) iOS 18 only runs on apple-a10 and later CPUs.
+ if (!Triple.isOSVersionLT(18) && !Triple.isArm64e())
+ return "apple-a10";
}
if (Triple.isWatchOS()) {
@@ -64,8 +67,8 @@ std::string aarch64::getAArch64TargetCPU(const ArgList &Args,
// arm64_32/arm64e watchOS requires S4 before watchOS 26, S6 after.
if (Triple.getArch() == llvm::Triple::aarch64_32 || Triple.isArm64e())
return Triple.isOSVersionLT(26) ? "apple-s4" : "apple-s6";
- // arm64 (non-e, non-32) watchOS comes later, and requires S6 anyway.
- return "apple-s6";
+ // arm64 (non-e, non-32) watchOS comes later, and requires S9 anyway.
+ return "apple-s9";
}
if (Triple.isXROS()) {
diff --git a/clang/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h
index c227895..9adad5c 100644
--- a/clang/lib/Driver/ToolChains/Clang.h
+++ b/clang/lib/Driver/ToolChains/Clang.h
@@ -163,7 +163,7 @@ public:
class LLVM_LIBRARY_VISIBILITY OffloadPackager final : public Tool {
public:
OffloadPackager(const ToolChain &TC)
- : Tool("Offload::Packager", "clang-offload-packager", TC) {}
+ : Tool("Offload::Packager", "llvm-offload-binary", TC) {}
bool hasIntegratedCPP() const override { return false; }
void ConstructJob(Compilation &C, const JobAction &JA,
diff --git a/clang/lib/Headers/avx512fp16intrin.h b/clang/lib/Headers/avx512fp16intrin.h
index d951ba0..142cc07 100644
--- a/clang/lib/Headers/avx512fp16intrin.h
+++ b/clang/lib/Headers/avx512fp16intrin.h
@@ -112,7 +112,7 @@ static __inline__ __m512h __DEFAULT_FN_ATTRS512_CONSTEXPR _mm512_setr_ph(
e9, e8, e7, e6, e5, e4, e3, e2, e1, e0);
}
-static __inline __m512h __DEFAULT_FN_ATTRS512
+static __inline __m512h __DEFAULT_FN_ATTRS512_CONSTEXPR
_mm512_set1_pch(_Float16 _Complex __h) {
return (__m512h)_mm512_set1_ps(__builtin_bit_cast(float, __h));
}
@@ -193,17 +193,17 @@ _mm512_castsi512_ph(__m512i __a) {
return (__m512h)__a;
}
-static __inline__ __m128h __DEFAULT_FN_ATTRS256
+static __inline__ __m128h __DEFAULT_FN_ATTRS256_CONSTEXPR
_mm256_castph256_ph128(__m256h __a) {
return __builtin_shufflevector(__a, __a, 0, 1, 2, 3, 4, 5, 6, 7);
}
-static __inline__ __m128h __DEFAULT_FN_ATTRS512
+static __inline__ __m128h __DEFAULT_FN_ATTRS512_CONSTEXPR
_mm512_castph512_ph128(__m512h __a) {
return __builtin_shufflevector(__a, __a, 0, 1, 2, 3, 4, 5, 6, 7);
}
-static __inline__ __m256h __DEFAULT_FN_ATTRS512
+static __inline__ __m256h __DEFAULT_FN_ATTRS512_CONSTEXPR
_mm512_castph512_ph256(__m512h __a) {
return __builtin_shufflevector(__a, __a, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15);
diff --git a/clang/lib/Headers/avx512vlfp16intrin.h b/clang/lib/Headers/avx512vlfp16intrin.h
index c0bcc08..5b2b3f0 100644
--- a/clang/lib/Headers/avx512vlfp16intrin.h
+++ b/clang/lib/Headers/avx512vlfp16intrin.h
@@ -34,11 +34,13 @@
#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS128
#endif
-static __inline__ _Float16 __DEFAULT_FN_ATTRS128 _mm_cvtsh_h(__m128h __a) {
+static __inline__ _Float16 __DEFAULT_FN_ATTRS128_CONSTEXPR
+_mm_cvtsh_h(__m128h __a) {
return __a[0];
}
-static __inline__ _Float16 __DEFAULT_FN_ATTRS256 _mm256_cvtsh_h(__m256h __a) {
+static __inline__ _Float16 __DEFAULT_FN_ATTRS256_CONSTEXPR
+_mm256_cvtsh_h(__m256h __a) {
return __a[0];
}
diff --git a/clang/lib/Headers/opencl-c-base.h b/clang/lib/Headers/opencl-c-base.h
index 6206a34..414f10a 100644
--- a/clang/lib/Headers/opencl-c-base.h
+++ b/clang/lib/Headers/opencl-c-base.h
@@ -82,6 +82,8 @@
#define __opencl_c_read_write_images 1
#endif // defined(__SPIR__)
+#endif // (__OPENCL_CPP_VERSION__ == 202100 || __OPENCL_C_VERSION__ == 300)
+
// Undefine any feature macros that have been explicitly disabled using
// an __undef_<feature> macro.
#ifdef __undef___opencl_c_work_group_collective_functions
@@ -99,8 +101,12 @@
#ifdef __undef___opencl_c_read_write_images
#undef __opencl_c_read_write_images
#endif
-
-#endif // (__OPENCL_CPP_VERSION__ == 202100 || __OPENCL_C_VERSION__ == 300)
+#ifdef __undef___opencl_c_integer_dot_product_input_4x8bit
+#undef __opencl_c_integer_dot_product_input_4x8bit
+#endif
+#ifdef __undef___opencl_c_integer_dot_product_input_4x8bit_packed
+#undef __opencl_c_integer_dot_product_input_4x8bit_packed
+#endif
#if !defined(__opencl_c_generic_address_space)
// Internal feature macro to provide named (global, local, private) address
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 3cc61b1..063db05 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8811,8 +8811,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
case ArgType::Match:
case ArgType::MatchPromotion:
case ArgType::NoMatchPromotionTypeConfusion:
- case ArgType::NoMatchSignedness:
llvm_unreachable("expected non-matching");
+ case ArgType::NoMatchSignedness:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_signedness;
+ break;
case ArgType::NoMatchPedantic:
Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic;
break;
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 999e302c..f4df63c 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -280,6 +280,11 @@ public:
if (T->getDepth() >= TemplateArgs.getNumLevels())
return true;
+ // There might not be a corresponding template argument before substituting
+ // into the parameter mapping, e.g. a sizeof... expression.
+ if (!TemplateArgs.hasTemplateArgument(T->getDepth(), T->getIndex()))
+ return true;
+
TemplateArgument Arg = TemplateArgs(T->getDepth(), T->getIndex());
if (T->isParameterPack() && SemaRef.ArgPackSubstIndex) {
@@ -300,6 +305,12 @@ public:
if (!NTTP)
return TraverseDecl(D);
+ if (NTTP->getDepth() >= TemplateArgs.getNumLevels())
+ return true;
+
+ if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(), NTTP->getIndex()))
+ return true;
+
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) {
assert(Arg.getKind() == TemplateArgument::Pack &&
@@ -326,17 +337,25 @@ public:
return inherited::TraverseDecl(D);
}
+ bool TraverseCallExpr(CallExpr *CE) {
+ inherited::TraverseStmt(CE->getCallee());
+
+ for (Expr *Arg : CE->arguments())
+ inherited::TraverseStmt(Arg);
+
+ return true;
+ }
+
bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
// We don't care about TypeLocs. So traverse Types instead.
- return TraverseType(TL.getType(), TraverseQualifier);
+ return TraverseType(TL.getType().getCanonicalType(), TraverseQualifier);
}
bool TraverseTagType(const TagType *T, bool TraverseQualifier) {
// T's parent can be dependent while T doesn't have any template arguments.
// We should have already traversed its qualifier.
// FIXME: Add an assert to catch cases where we failed to profile the
- // concept. assert(!T->isDependentType() && "We missed a case in profiling
- // concepts!");
+ // concept.
return true;
}
@@ -701,7 +720,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
-
auto &Cached = Iter->second.Satisfaction;
Satisfaction.ContainsErrors = Cached.ContainsErrors;
Satisfaction.IsSatisfied = Cached.IsSatisfied;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a662b72..09e5d69 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -598,18 +598,17 @@ void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) {
validatePackoffset(SemaRef, BufDecl);
- // create buffer layout struct
createHostLayoutStructForBuffer(SemaRef, BufDecl);
- HLSLVkBindingAttr *VkBinding = Dcl->getAttr<HLSLVkBindingAttr>();
- HLSLResourceBindingAttr *RBA = Dcl->getAttr<HLSLResourceBindingAttr>();
- if (!VkBinding && (!RBA || !RBA->hasRegisterSlot())) {
+ // Handle implicit binding if needed.
+ ResourceBindingAttrs ResourceAttrs(Dcl);
+ if (!ResourceAttrs.isExplicit()) {
SemaRef.Diag(Dcl->getLocation(), diag::warn_hlsl_implicit_binding);
// Use HLSLResourceBindingAttr to transfer implicit binding order_ID
// to codegen. If it does not exist, create an implicit attribute.
uint32_t OrderID = getNextImplicitBindingOrderID();
- if (RBA)
- RBA->setImplicitBindingOrderID(OrderID);
+ if (ResourceAttrs.hasBinding())
+ ResourceAttrs.setImplicitOrderID(OrderID);
else
addImplicitBindingAttrToDecl(SemaRef, BufDecl,
BufDecl->isCBuffer() ? RegisterType::CBuffer
@@ -1590,10 +1589,6 @@ void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
}
void SemaHLSL::handleVkBindingAttr(Decl *D, const ParsedAttr &AL) {
- // The vk::binding attribute only applies to SPIR-V.
- if (!getASTContext().getTargetInfo().getTriple().isSPIRV())
- return;
-
uint32_t Binding = 0;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Binding))
return;
@@ -3780,17 +3775,15 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// If the resource array does not have an explicit binding attribute,
// create an implicit one. It will be used to transfer implicit binding
// order_ID to codegen.
- if (!VD->hasAttr<HLSLVkBindingAttr>()) {
- HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
- if (!RBA || !RBA->hasRegisterSlot()) {
- uint32_t OrderID = getNextImplicitBindingOrderID();
- if (RBA)
- RBA->setImplicitBindingOrderID(OrderID);
- else
- addImplicitBindingAttrToDecl(
- SemaRef, VD, getRegisterType(getResourceArrayHandleType(VD)),
- OrderID);
- }
+ ResourceBindingAttrs Binding(VD);
+ if (!Binding.isExplicit()) {
+ uint32_t OrderID = getNextImplicitBindingOrderID();
+ if (Binding.hasBinding())
+ Binding.setImplicitOrderID(OrderID);
+ else
+ addImplicitBindingAttrToDecl(
+ SemaRef, VD, getRegisterType(getResourceArrayHandleType(VD)),
+ OrderID);
}
}
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5657dfe..8d32ef6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1087,14 +1087,14 @@ static bool shouldAddReversedEqEq(Sema &S, SourceLocation OpLoc,
}
bool OverloadCandidateSet::OperatorRewriteInfo::allowsReversed(
- OverloadedOperatorKind Op) {
+ OverloadedOperatorKind Op) const {
if (!AllowRewrittenCandidates)
return false;
return Op == OO_EqualEqual || Op == OO_Spaceship;
}
bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
- Sema &S, ArrayRef<Expr *> OriginalArgs, FunctionDecl *FD) {
+ Sema &S, ArrayRef<Expr *> OriginalArgs, FunctionDecl *FD) const {
auto Op = FD->getOverloadedOperator();
if (!allowsReversed(Op))
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 83d79b43..70baab5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3812,6 +3812,15 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
return true;
}
+ // Protobuf function declared in `generated_message_util.h` that takes
+ // ownership of the second argument. As the first and third arguments are
+ // allocation arenas and won't be tracked by this checker, there is no reason
+ // to set `EscapingSymbol`. (Also, this is an implementation detail of
+ // Protobuf, so it's better to be a bit more permissive.)
+ if (FName == "GetOwnedMessageInternal") {
+ return true;
+ }
+
// Handle cases where we know a buffer's /address/ can escape.
// Note that the above checks handle some special cases where we know that
// even though the address escapes, it's still our responsibility to free the
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 00a1b8b..66cfccb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -31,9 +31,9 @@ bool tryToFindPtrOrigin(
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
if (auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {
auto QT = VD->getType();
- if (VD->hasGlobalStorage() && QT.isConstQualified()) {
+ auto IsImmortal = safeGetName(VD) == "NSApp";
+ if (VD->hasGlobalStorage() && (IsImmortal || QT.isConstQualified()))
return callback(E, true);
- }
}
}
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
@@ -208,6 +208,8 @@ bool tryToFindPtrOrigin(
continue;
}
if (auto *BoxedExpr = dyn_cast<ObjCBoxedExpr>(E)) {
+ if (StopAtFirstRefCountedObj)
+ return callback(BoxedExpr, true);
E = BoxedExpr->getSubExpr();
continue;
}
diff --git a/clang/lib/Testing/CommandLineArgs.cpp b/clang/lib/Testing/CommandLineArgs.cpp
index e9da72f..95f37e3 100644
--- a/clang/lib/Testing/CommandLineArgs.cpp
+++ b/clang/lib/Testing/CommandLineArgs.cpp
@@ -103,7 +103,8 @@ std::string getAnyTargetForTesting() {
StringRef TargetName(Target.getName());
if (TargetName == "x86-64")
TargetName = "x86_64";
- if (llvm::TargetRegistry::lookupTarget(TargetName, Error) == &Target) {
+ if (llvm::TargetRegistry::lookupTarget(llvm::Triple(TargetName), Error) ==
+ &Target) {
return std::string(TargetName);
}
}
diff --git a/clang/test/AST/ByteCode/builtin-object-size.cpp b/clang/test/AST/ByteCode/builtin-object-size.cpp
index 6f4ef54..e4433ea 100644
--- a/clang/test/AST/ByteCode/builtin-object-size.cpp
+++ b/clang/test/AST/ByteCode/builtin-object-size.cpp
@@ -17,7 +17,8 @@ static_assert(__builtin_object_size(&arrf, 0) == (sizeof(float)*2), "");
static_assert(__builtin_object_size(&arrf[1], 0) == sizeof(float), "");
static_assert(__builtin_object_size(&arrf[2], 0) == 0, "");
-
+constexpr struct { int a; int b; } F{};
+static_assert(__builtin_object_size(&F.a, 3) == sizeof(int));
struct S {
int a;
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index c6d93b9..2de0674 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -92,9 +92,8 @@ cbuffer CB3 {
StructuredBuffer<float> SB[10];
// CHECK: VarDecl {{.*}} SB2 'StructuredBuffer<float>[10]'
+// CHECK: HLSLVkBindingAttr {{.*}} 2 0
// DXIL: HLSLResourceBindingAttr {{.*}} Implicit
-// DXIL-NOT: HLSLVkBindingAttr
-// SPV: HLSLVkBindingAttr {{.*}} 2 0
// SPV-NOT: HLSLResourceBindingAttr {{.*}} Implicit
[[vk::binding(2)]]
StructuredBuffer<float> SB2[10];
diff --git a/clang/test/AST/HLSL/vk_binding_attr.hlsl b/clang/test/AST/HLSL/vk_binding_attr.hlsl
index d08165d..13e7544 100644
--- a/clang/test/AST/HLSL/vk_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/vk_binding_attr.hlsl
@@ -10,8 +10,7 @@
// SPV-NEXT: IntegerLiteral {{.*}} 'unsigned int' 102
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 0
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 0
-// SPV: HLSLVkBindingAttr {{.*}} 23 102
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 23 102
[[vk::binding(23, 102)]] StructuredBuffer<float> Buf;
// CHECK: VarDecl {{.*}} Buf2 'StructuredBuffer<float>':'hlsl::StructuredBuffer<float>'
@@ -23,8 +22,7 @@
// SPV-NEXT: IntegerLiteral {{.*}} 'unsigned int' 1
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 23
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 102
-// SPV: HLSLVkBindingAttr {{.*}} 14 1
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 14 1
// CHECK: HLSLResourceBindingAttr {{.*}} "t23" "space102"
[[vk::binding(14, 1)]] StructuredBuffer<float> Buf2 : register(t23, space102);
@@ -37,15 +35,13 @@
// SPV-NEXT: IntegerLiteral {{.*}} 'unsigned int' 0
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 23
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 102
-// SPV: HLSLVkBindingAttr {{.*}} 14 0
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 14 0
// CHECK: HLSLResourceBindingAttr {{.*}} "t23" "space102"
[[vk::binding(14)]] StructuredBuffer<float> Buf3 : register(t23, space102);
// CHECK: HLSLBufferDecl {{.*}} cbuffer CB
// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer
-// SPV-NEXT: HLSLVkBindingAttr {{.*}} 1 2
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 1 2
[[vk::binding(1, 2)]] cbuffer CB {
float a;
}
@@ -54,15 +50,14 @@
// CHECK-NEXT: CallExpr {{.*}} 'Buffer<int>':'hlsl::Buffer<int>'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'hlsl::Buffer<int> (*)(unsigned int, unsigned int, int, unsigned int, const char *)' <FunctionToPointerDecay>
// SPV-NEXT: DeclRefExpr {{.*}} 'hlsl::Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
-// SPV-NEXT-SAME: CXXMethod {{.*}} '__createFromBinding' 'Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
+// SPV-NEXT-SAME: CXXMethod {{.*}} '__createFromBinding' 'hlsl::Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
// SPV-NEXT: IntegerLiteral {{.*}} 'unsigned int' 24
// SPV-NEXT: IntegerLiteral {{.*}} 'unsigned int' 103
-// DXIL-NEXT: DeclRefExpr {{.*}} 'hlsl::Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
-// DXIL-NEXT-SAME: CXXMethod {{.*}} '__createFromImplicitBinding' 'Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
+// DXIL-NEXT: DeclRefExpr {{.*}} 'hlsl::Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
+// DXIL-NEXT-SAME: CXXMethod {{.*}} '__createFromImplicitBinding' 'hlsl::Buffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 2
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 0
-// SPV: HLSLVkBindingAttr {{.*}} 24 103
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 24 103
[[vk::binding(24, 103)]] Buffer<int> Buf4;
// CHECK: VarDecl {{.*}} Buf5 'RWBuffer<int2>':'hlsl::RWBuffer<vector<int, 2>>'
@@ -76,8 +71,7 @@
// DXIL-NEXT-SAME: CXXMethod {{.*}} '__createFromImplicitBinding' 'Buffer<int2> (unsigned int, unsigned int, int, unsigned int, const char *)'
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 3
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 0
-// SPV: HLSLVkBindingAttr {{.*}} 25 104
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 25 104
[[vk::binding(25, 104)]] RWBuffer<int2> Buf5;
// CHECK: VarDecl {{.*}} Buf6 'RWStructuredBuffer<int>':'hlsl::RWStructuredBuffer<int>'
@@ -91,6 +85,5 @@
// DXIL-NEXT-SAME: CXXMethod {{.*}} '__createFromBinding' 'hlsl::RWStructuredBuffer<int> (unsigned int, unsigned int, int, unsigned int, const char *)'
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 4
// DXIL-NEXT: IntegerLiteral {{.*}} 'unsigned int' 0
-// SPV: HLSLVkBindingAttr {{.*}} 26 105
-// DXIL-NOT: HLSLVkBindingAttr
+// CHECK: HLSLVkBindingAttr {{.*}} 26 105
[[vk::binding(26, 105)]] RWStructuredBuffer<int> Buf6;
diff --git a/clang/test/AST/ast-print-record-decl.c b/clang/test/AST/ast-print-record-decl.c
index fd81588..394f837 100644
--- a/clang/test/AST/ast-print-record-decl.c
+++ b/clang/test/AST/ast-print-record-decl.c
@@ -315,4 +315,11 @@ template <int, int = 0> KW SmearedNTTPDefArgs;
// PRINT-CXX-NEXT: template <int = 0, int> [[KW]] SmearedNTTPDefArgs;
template <int = 0, int> KW SmearedNTTPDefArgs;
+// PRINT-CXX-LABEL: Tpl
+template <int> KW Tpl;
+// PRINT-CXX-NEXT: template <template <int> class, template <int> class = Tpl> [[KW]] SmearedTplDefArgs;
+template <template <int> class, template <int> class = Tpl> KW SmearedTplDefArgs;
+// PRINT-CXX-NEXT: template <template <int> class = Tpl, template <int> class> [[KW]] SmearedTplDefArgs;
+template <template <int> class = Tpl, template <int> class> KW SmearedTplDefArgs;
+
#endif
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index dacb713..a5fc3d7 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -178,6 +178,22 @@ __attribute__((objc_root_class))
+ (NSNumber *)numberWithBool:(BOOL)value;
@end
+@interface NSResponder : NSObject
+@end
+
+@interface NSApplication : NSResponder
+
+extern NSApplication * NSApp;
+
+@property (class, readonly, strong) NSApplication *sharedApplication;
+
+- (void)finishLaunching;
+- (void)run;
+- (void)stop:(id)sender;
+- (void)terminate:(id)sender;
+
+@end
+
@interface SomeObj : NSObject
- (instancetype)_init;
- (SomeObj *)mutableCopy;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index c9d2fe8..a517dbc 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -398,12 +398,18 @@ namespace call_with_cf_constant {
void baz(const NSDictionary *);
void boo(NSNumber *);
void boo(CFTypeRef);
- void foo() {
+
+ struct Details {
+ int value;
+ };
+
+ void foo(Details* details) {
CFArrayCreateMutable(kCFAllocatorDefault, 10);
bar(@[@"hello"]);
baz(@{@"hello": @3});
boo(@YES);
boo(@NO);
+ boo(@(details->value));
}
}
@@ -582,6 +588,7 @@ struct Derived : Base {
[self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), OSObjectPtr { provide_dispatch() }.get()];
[self doWork:__null];
[self doWork:nil];
+ [NSApp run];
}
- (SomeObj *)getSomeObj {
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h b/clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h
new file mode 100644
index 0000000..cb12b55
--- /dev/null
+++ b/clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h
@@ -0,0 +1,18 @@
+// Like the compiler, the static analyzer treats some functions differently if
+// they come from a system header -- for example, it is assumed that system
+// functions do not arbitrarily free() their parameters, and that some bugs
+// found in system headers cannot be fixed by the user and should be
+// suppressed.
+#pragma clang system_header
+
+class Arena;
+class MessageLite {
+ int SomeArbitraryField;
+};
+
+// Originally declared in generated_message_util.h
+MessageLite *GetOwnedMessageInternal(Arena *, MessageLite *, Arena *);
+
+// Not a real protobuf function -- just introduced to validate that this file
+// is handled as a system header.
+void SomeOtherFunction(MessageLite *);
diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py
index d2e5f0b..cd5b3081 100644
--- a/clang/test/Analysis/LifetimeSafety/benchmark.py
+++ b/clang/test/Analysis/LifetimeSafety/benchmark.py
@@ -145,6 +145,60 @@ def generate_cpp_nested_loop_test(n: int) -> str:
return cpp_code
+def generate_cpp_switch_fan_out_test(n: int) -> str:
+ """
+ Generates C++ code with a switch statement with N branches.
+ Each branch 'i' 'uses' (reads) a single, unique pointer 'pi'.
+ This pattern creates a "fan-in" join point for the backward
+ liveness analysis, stressing the LivenessMap::join operation
+ by forcing it to merge N disjoint, single-element sets of live origins.
+ The resulting complexity for LiveOrigins should be O(n log n) or higher.
+
+ Example (n=3):
+ struct MyObj { int id; ~MyObj() {} };
+
+ void switch_fan_out_3(int condition) {
+ MyObj v1{1}; MyObj v2{1}; MyObj v3{1};
+ MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3;
+
+ switch (condition % 3) {
+ case 0:
+ p1->id = 1;
+ break;
+ case 1:
+ p2->id = 1;
+ break;
+ case 2:
+ p3->id = 1;
+ break;
+ }
+ }
+ """
+ if n <= 0:
+ return "// Number of variables must be positive."
+
+ cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n"
+ cpp_code += f"void switch_fan_out{n}(int condition) {{\n"
+ # Generate N distinct objects
+ for i in range(1, n + 1):
+ cpp_code += f" MyObj v{i}{{1}};\n"
+ cpp_code += "\n"
+ # Generate N distinct pointers, each as a separate variable
+ for i in range(1, n + 1):
+ cpp_code += f" MyObj* p{i} = &v{i};\n"
+ cpp_code += "\n"
+
+ cpp_code += f" switch (condition % {n}) {{\n"
+ for case_num in range(n):
+ cpp_code += f" case {case_num}:\n"
+ cpp_code += f" p{case_num + 1}->id = 1;\n"
+ cpp_code += " break;\n"
+
+ cpp_code += " }\n}\n"
+ cpp_code += f"\nint main() {{ switch_fan_out{n}(0); return 0; }}\n"
+ return cpp_code
+
+
def analyze_trace_file(trace_path: str) -> dict:
"""
Parses the -ftime-trace JSON output to find durations for the lifetime
@@ -156,14 +210,14 @@ def analyze_trace_file(trace_path: str) -> dict:
"total_us": 0.0,
"fact_gen_us": 0.0,
"loan_prop_us": 0.0,
- "expired_loans_us": 0.0,
+ "live_origins_us": 0.0,
}
event_name_map = {
"LifetimeSafetyAnalysis": "lifetime_us",
"ExecuteCompiler": "total_us",
"FactGenerator": "fact_gen_us",
"LoanPropagation": "loan_prop_us",
- "ExpiredLoans": "expired_loans_us",
+ "LiveOrigins": "live_origins_us",
}
try:
with open(trace_path, "r") as f:
@@ -227,7 +281,7 @@ def generate_markdown_report(results: dict) -> str:
# Table header
report.append(
- "| N (Input Size) | Total Time | Analysis Time (%) | Fact Generator (%) | Loan Propagation (%) | Expired Loans (%) |"
+ "| N (Input Size) | Total Time | Analysis Time (%) | Fact Generator (%) | Loan Propagation (%) | Live Origins (%) |"
)
report.append(
"|:---------------|-----------:|------------------:|-------------------:|---------------------:|------------------:|"
@@ -247,7 +301,7 @@ def generate_markdown_report(results: dict) -> str:
f"{data['lifetime_ms'][i] / total_t * 100:>17.2f}% |",
f"{data['fact_gen_ms'][i] / total_t * 100:>18.2f}% |",
f"{data['loan_prop_ms'][i] / total_t * 100:>20.2f}% |",
- f"{data['expired_loans_ms'][i] / total_t * 100:>17.2f}% |",
+ f"{data['live_origins_ms'][i] / total_t * 100:>17.2f}% |",
]
report.append(" ".join(row))
@@ -259,7 +313,7 @@ def generate_markdown_report(results: dict) -> str:
"Total Analysis": data["lifetime_ms"],
"FactGenerator": data["fact_gen_ms"],
"LoanPropagation": data["loan_prop_ms"],
- "ExpiredLoans": data["expired_loans_ms"],
+ "LiveOrigins": data["live_origins_ms"],
}
for phase_name, y_data in analysis_phases.items():
@@ -302,7 +356,13 @@ def run_single_test(
source_file,
]
- result = subprocess.run(clang_command, capture_output=True, text=True, timeout=60)
+ try:
+ result = subprocess.run(
+ clang_command, capture_output=True, text=True, timeout=60
+ )
+ except subprocess.TimeoutExpired:
+ print(f"Compilation timed out for N={n}!", file=sys.stderr)
+ return {}
if result.returncode != 0:
print(f"Compilation failed for N={n}!", file=sys.stderr)
@@ -354,6 +414,12 @@ if __name__ == "__main__":
"generator_func": generate_cpp_nested_loop_test,
"n_values": [50, 100, 150, 200],
},
+ {
+ "name": "switch_fan_out",
+ "title": "Switch Fan-out",
+ "generator_func": generate_cpp_switch_fan_out_test,
+ "n_values": [500, 1000, 2000, 4000],
+ },
]
results = {}
@@ -368,7 +434,7 @@ if __name__ == "__main__":
"total_ms": [],
"fact_gen_ms": [],
"loan_prop_ms": [],
- "expired_loans_ms": [],
+ "live_origins_ms": [],
}
for n in config["n_values"]:
durations_ms = run_single_test(
@@ -387,7 +453,7 @@ if __name__ == "__main__":
f" Total Analysis: {human_readable_time(durations_ms['lifetime_ms'])} | "
f"FactGen: {human_readable_time(durations_ms['fact_gen_ms'])} | "
f"LoanProp: {human_readable_time(durations_ms['loan_prop_ms'])} | "
- f"ExpiredLoans: {human_readable_time(durations_ms['expired_loans_ms'])}"
+ f"LiveOrigins: {human_readable_time(durations_ms['live_origins_ms'])}"
)
print("\n\n" + "=" * 80)
diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp
index b2bad7e..d9c4b77 100644
--- a/clang/test/Analysis/NewDeleteLeaks.cpp
+++ b/clang/test/Analysis/NewDeleteLeaks.cpp
@@ -13,6 +13,8 @@
// RUN: unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=true
#include "Inputs/system-header-simulator-for-malloc.h"
+// For the tests in namespace protobuf_leak:
+#include "Inputs/system-header-simulator-for-protobuf.h"
//===----------------------------------------------------------------------===//
// Report for which we expect NoOwnershipChangeVisitor to add a new note.
@@ -218,3 +220,34 @@ void caller() {
(void)n;
} // no-warning: No potential memory leak here, because that's been already reported.
} // namespace symbol_reaper_lifetime
+
+// Check that we do not report false positives in automatically generated
+// protobuf code that passes dynamically allocated memory to a certain function
+// named GetOwnedMessageInternal.
+namespace protobuf_leak {
+Arena *some_arena, *some_submessage_arena;
+
+MessageLite *protobuf_leak() {
+ MessageLite *p = new MessageLite(); // Real protobuf code instantiates a
+ // subclass of MessageLite, but that's
+ // not relevant for the bug.
+ MessageLite *q = GetOwnedMessageInternal(some_arena, p, some_submessage_arena);
+ return q;
+ // No leak at end of function -- the pointer escapes in GetOwnedMessageInternal.
+}
+
+void validate_system_header() {
+ // The case protobuf_leak would also pass if GetOwnedMessageInternal wasn't
+ // declared in a system header. This test verifies that another function
+ // declared in the same header behaves differently (doesn't escape memory) to
+ // demonstrate that GetOwnedMessageInternal is indeed explicitly recognized
+ // by the analyzer.
+
+ // expected-note@+1 {{Memory is allocated}}
+ MessageLite *p = new MessageLite();
+ SomeOtherFunction(p);
+ // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}}
+ // expected-note@+1 {{Potential leak of memory pointed to by 'p'}}
+}
+
+} // namespace protobuf_leak
diff --git a/clang/test/CIR/CodeGen/complex.cpp b/clang/test/CIR/CodeGen/complex.cpp
index 73c05b3..083d438 100644
--- a/clang/test/CIR/CodeGen/complex.cpp
+++ b/clang/test/CIR/CodeGen/complex.cpp
@@ -1359,3 +1359,49 @@ void complex_type_argument() {
// OGCG: store float %[[A_IMAG]], ptr %[[ARG_IMAG_PTR]], align 4
// OGCG: %[[TMP_ARG:.*]] = load <2 x float>, ptr %[[ARG_ADDR]], align 4
// OGCG: call void @_Z22complex_type_parameterCf(<2 x float> noundef %[[TMP_ARG]])
+
+void real_on_scalar_bool() {
+ bool a;
+ bool b = __real__ a;
+}
+
+// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["a"]
+// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b", init]
+// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: %[[A_REAL:.*]] = cir.complex.real %[[TMP_A]] : !cir.bool -> !cir.bool
+// CIR: cir.store{{.*}} %[[A_REAL]], %[[B_ADDR]] : !cir.bool, !cir.ptr<!cir.bool>
+
+// LLVM: %[[A_ADDR:.*]] = alloca i8, i64 1, align 1
+// LLVM: %[[B_ADDR:.*]] = alloca i8, i64 1, align 1
+// LLVM: %[[TMP_A:.*]] = load i8, ptr %[[A_ADDR]], align 1
+// LLVM: %[[TMP_A_I1:.*]] = trunc i8 %[[TMP_A]] to i1
+// LLVM: %[[TMP_A_I8:.*]] = zext i1 %[[TMP_A_I1]] to i8
+// LLVM: store i8 %[[TMP_A_I8]], ptr %[[B_ADDR]], align 1
+
+// OGCG: %[[A_ADDR:.*]] = alloca i8, align 1
+// OGCG: %[[B_ADDR:.*]] = alloca i8, align 1
+// OGCG: %[[TMP_A:.*]] = load i8, ptr %[[A_ADDR]], align 1
+// OGCG: %[[TMP_A_I1:.*]] = trunc i8 %[[TMP_A]] to i1
+// OGCG: %[[TMP_A_I8:.*]] = zext i1 %[[TMP_A_I1]] to i8
+// OGCG: store i8 %[[TMP_A_I8]], ptr %[[B_ADDR]], align 1
+
+void imag_on_scalar_bool() {
+ bool a;
+ bool b = __imag__ a;
+}
+
+// CIR: %[[A_ADDR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["a"]
+// CIR: %[[B_ADDR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b", init]
+// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: %[[A_IMAG:.*]] = cir.complex.imag %[[TMP_A]] : !cir.bool -> !cir.bool
+// CIR: cir.store{{.*}} %[[A_IMAG]], %[[B_ADDR]] : !cir.bool, !cir.ptr<!cir.bool>
+
+// LLVM: %[[A_ADDR:.*]] = alloca i8, i64 1, align 1
+// LLVM: %[[B_ADDR:.*]] = alloca i8, i64 1, align 1
+// LLVM: %[[TMP_A:.*]] = load i8, ptr %[[A_ADDR]], align 1
+// LLVM: %[[TMP_A_I1:.*]] = trunc i8 %[[TMP_A]] to i1
+// LLVM: store i8 0, ptr %[[B_ADDR]], align 1
+
+// OGCG: %[[A_ADDR:.*]] = alloca i8, align 1
+// OGCG: %[[B_ADDR:.*]] = alloca i8, align 1
+// OGCG: store i8 0, ptr %[[B_ADDR]], align 1
diff --git a/clang/test/CIR/CodeGen/constant-inits.cpp b/clang/test/CIR/CodeGen/constant-inits.cpp
index c9153c91..d5a7bb9 100644
--- a/clang/test/CIR/CodeGen/constant-inits.cpp
+++ b/clang/test/CIR/CodeGen/constant-inits.cpp
@@ -30,6 +30,41 @@ struct simple {
int a, b;
};
+// Byte-aligned bitfields
+struct byte_aligned_bitfields {
+ unsigned int a : 8;
+ unsigned int b : 8;
+ unsigned int c : 16;
+};
+
+struct signed_byte_aligned_bitfields {
+ int x : 8;
+ int y : 8;
+};
+
+struct single_byte_bitfield {
+ unsigned char a : 8;
+};
+
+// Partial bitfields (sub-byte)
+struct partial_bitfields {
+ unsigned int a : 3;
+ unsigned int b : 5;
+ unsigned int c : 8;
+};
+
+struct signed_partial_bitfields {
+ int x : 4;
+ int y : 4;
+};
+
+struct mixed_partial_bitfields {
+ unsigned char a : 1;
+ unsigned char b : 1;
+ unsigned char c : 1;
+ unsigned char d : 5;
+};
+
void function() {
constexpr static empty e;
@@ -54,8 +89,22 @@ void function() {
constexpr static simple simple_array[] {
s, {1111, 2222}, s
};
+
+ // Byte-aligned bitfield tests
+ constexpr static byte_aligned_bitfields ba_bf1 = {0xFF, 0xAA, 0x1234};
+ constexpr static signed_byte_aligned_bitfields ba_bf2 = {-1, 127};
+ constexpr static single_byte_bitfield ba_bf3 = {42};
+
+ // Partial bitfield tests
+ constexpr static partial_bitfields p_bf1 = {1, 2, 3};
+ constexpr static signed_partial_bitfields p_bf2 = {-1, 7};
+ constexpr static mixed_partial_bitfields p_bf3 = {1, 0, 1, 15};
}
+// Anonymous struct type definitions for bitfields
+// CIR-DAG: !rec_anon_struct = !cir.record<struct {!u8i, !u8i, !u8i, !u8i}>
+// CIR-DAG: !rec_anon_struct1 = !cir.record<struct {!u8i, !u8i, !cir.array<!u8i x 2>}>
+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1e = #cir.zero : !rec_empty
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1s = #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s32i}> : !rec_simple
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE2p1 = #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, #cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : !s8i]> : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
@@ -83,6 +132,33 @@ void function() {
// CIR-DAG-SAME: #cir.zero : !rec_packed_and_aligned
// CIR-DAG-SAME: ]> : !cir.array<!rec_packed_and_aligned x 2>
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE6ba_bf1 = #cir.const_record<{
+// CIR-DAG-SAME: #cir.int<255> : !u8i,
+// CIR-DAG-SAME: #cir.int<170> : !u8i,
+// CIR-DAG-SAME: #cir.int<52> : !u8i,
+// CIR-DAG-SAME: #cir.int<18> : !u8i
+// CIR-DAG-SAME: }> : !rec_anon_struct
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE6ba_bf2 = #cir.const_record<{
+// CIR-DAG-SAME: #cir.int<255> : !u8i,
+// CIR-DAG-SAME: #cir.int<127> : !u8i,
+// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>
+// CIR-DAG-SAME: }> : !rec_anon_struct1
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE6ba_bf3 = #cir.const_record<{
+// CIR-DAG-SAME: #cir.int<42> : !u8i
+// CIR-DAG-SAME: }> : !rec_single_byte_bitfield
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5p_bf1 = #cir.const_record<{
+// CIR-DAG-SAME: #cir.int<17> : !u8i,
+// CIR-DAG-SAME: #cir.int<3> : !u8i,
+// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>
+// CIR-DAG-SAME: }> : !rec_anon_struct1
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5p_bf2 = #cir.const_record<{
+// CIR-DAG-SAME: #cir.int<127> : !u8i,
+// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>
+// CIR-DAG-SAME: }> : !rec_signed_partial_bitfields
+// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5p_bf3 = #cir.const_record<{
+// CIR-DAG-SAME: #cir.int<125> : !u8i
+// CIR-DAG-SAME: }> : !rec_mixed_partial_bitfields
+
// CIR-LABEL: cir.func dso_local @_Z8functionv()
// CIR: cir.return
@@ -96,6 +172,12 @@ void function() {
// LLVM-DAG: @_ZZ8functionvE3paa = internal global %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>
// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
// LLVM-DAG: @_ZZ8functionvE9paa_array = internal global [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>, %struct.packed_and_aligned zeroinitializer]
+// LLVM-DAG: @_ZZ8functionvE6ba_bf1 = internal global { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
+// LLVM-DAG: @_ZZ8functionvE6ba_bf2 = internal global { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] zeroinitializer }
+// LLVM-DAG: @_ZZ8functionvE6ba_bf3 = internal global %struct.single_byte_bitfield { i8 42 }
+// LLVM-DAG: @_ZZ8functionvE5p_bf1 = internal global { i8, i8, [2 x i8] } { i8 17, i8 3, [2 x i8] zeroinitializer }
+// LLVM-DAG: @_ZZ8functionvE5p_bf2 = internal global %struct.signed_partial_bitfields { i8 127, [3 x i8] zeroinitializer }
+// LLVM-DAG: @_ZZ8functionvE5p_bf3 = internal global %struct.mixed_partial_bitfields { i8 125 }
// LLVM-LABEL: define{{.*}} void @_Z8functionv
// LLVM: ret void
@@ -110,6 +192,12 @@ void function() {
// OGCG-DAG: @_ZZ8functionvE3paa = internal constant %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>
// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
// OGCG-DAG: @_ZZ8functionvE9paa_array = internal constant [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>, %struct.packed_and_aligned <{ i16 0, i8 0, float 0.000000e+00, i8 undef }>]
+// OGCG-DAG: @_ZZ8functionvE6ba_bf1 = internal constant { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
+// OGCG-DAG: @_ZZ8functionvE6ba_bf2 = internal constant { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] undef }
+// OGCG-DAG: @_ZZ8functionvE6ba_bf3 = internal constant %struct.single_byte_bitfield { i8 42 }
+// OGCG-DAG: @_ZZ8functionvE5p_bf1 = internal constant { i8, i8, [2 x i8] } { i8 17, i8 3, [2 x i8] undef }
+// OGCG-DAG: @_ZZ8functionvE5p_bf2 = internal constant %struct.signed_partial_bitfields { i8 127, [3 x i8] undef }
+// OGCG-DAG: @_ZZ8functionvE5p_bf3 = internal constant %struct.mixed_partial_bitfields { i8 125 }
// OGCG-LABEL: define{{.*}} void @_Z8functionv
// OGCG: ret void
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 3dcf7af..000ea5b 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -180,3 +180,56 @@ void test_new_with_complex_type() {
// OGCG: store float 1.000000e+00, ptr %[[COMPLEX_REAL_PTR]], align 8
// OGCG: store float 2.000000e+00, ptr %[[COMPLEX_IMAG_PTR]], align 4
// OGCG: store ptr %[[NEW_COMPLEX]], ptr %[[A_ADDR]], align 8
+
+void t_new_constant_size() {
+ auto p = new double[16];
+}
+
+// In this test, NUM_ELEMENTS isn't used because no cookie is needed and there
+// are no constructor calls needed.
+
+// CHECK: cir.func{{.*}} @_Z19t_new_constant_sizev()
+// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>, ["p", init] {alignment = 8 : i64}
+// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<16> : !u64i
+// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<128> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.double>
+// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.double>, !cir.ptr<!cir.ptr<!cir.double>>
+// CHECK: cir.return
+// CHECK: }
+
+// LLVM: define{{.*}} void @_Z19t_new_constant_sizev
+// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[CALL:.*]] = call ptr @_Znam(i64 128)
+// LLVM: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
+
+// OGCG: define{{.*}} void @_Z19t_new_constant_sizev
+// OGCG: %[[P_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[CALL:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 128)
+// OGCG: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
+
+
+void t_new_multidim_constant_size() {
+ auto p = new double[2][3][4];
+}
+
+// As above, NUM_ELEMENTS isn't used.
+
+// CHECK: cir.func{{.*}} @_Z28t_new_multidim_constant_sizev()
+// CHECK: %[[P_ADDR:.*]] = cir.alloca !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>, ["p", init] {alignment = 8 : i64}
+// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<24> : !u64i
+// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<192> : !u64i
+// CHECK: %[[RAW_PTR:.*]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[TYPED_PTR:.*]] = cir.cast bitcast %[[RAW_PTR]] : !cir.ptr<!void> -> !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>
+// CHECK: cir.store align(8) %[[TYPED_PTR]], %[[P_ADDR]] : !cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>, !cir.ptr<!cir.ptr<!cir.array<!cir.array<!cir.double x 4> x 3>>>
+// CHECK: }
+
+// LLVM: define{{.*}} void @_Z28t_new_multidim_constant_sizev
+// LLVM: %[[P_ADDR:.*]] = alloca ptr, i64 1, align 8
+// LLVM: %[[CALL:.*]] = call ptr @_Znam(i64 192)
+// LLVM: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
+
+// OGCG: define{{.*}} void @_Z28t_new_multidim_constant_sizev
+// OGCG: %[[P_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[CALL:.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 192)
+// OGCG: store ptr %[[CALL]], ptr %[[P_ADDR]], align 8
diff --git a/clang/test/CIR/CodeGen/throws.cpp b/clang/test/CIR/CodeGen/throws.cpp
index 0122f30..ff6aa62 100644
--- a/clang/test/CIR/CodeGen/throws.cpp
+++ b/clang/test/CIR/CodeGen/throws.cpp
@@ -5,7 +5,7 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
-void foo() {
+void rethrow() {
throw;
}
@@ -18,7 +18,7 @@ void foo() {
// OGCG: call void @__cxa_rethrow()
// OGCG: unreachable
-int foo1(int a, int b) {
+int rethrow_from_block(int a, int b) {
if (b == 0)
throw;
return a / b;
@@ -83,3 +83,43 @@ int foo1(int a, int b) {
// OGCG: %[[TMP_B:.*]] = load i32, ptr %[[B_ADDR]], align 4
// OGCG: %[[DIV_A_B:.*]] = sdiv i32 %[[TMP_A]], %[[TMP_B]]
// OGCG: ret i32 %[[DIV_A_B]]
+
+void throw_scalar() {
+ throw 1;
+}
+
+// CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
+// CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR: cir.unreachable
+
+// LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// LLVM: store i32 1, ptr %[[EXCEPTION_ADDR]], align 16
+// LLVM: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
+// LLVM: unreachable
+
+// OGCG: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// OGCG: store i32 1, ptr %[[EXCEPTION_ADDR]], align 16
+// OGCG: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
+// OGCG: unreachable
+
+void paren_expr() { (throw 0, 1 + 2); }
+
+// CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
+// CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR: cir.unreachable
+// CIR: ^bb1:
+// CIR: %[[CONST_1:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: %[[CONST_2:.*]] = cir.const #cir.int<2> : !s32i
+// CIR: %[[ADD:.*]] = cir.binop(add, %[[CONST_1]], %[[CONST_2]]) nsw : !s32i
+
+// LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// LLVM: store i32 0, ptr %[[EXCEPTION_ADDR]], align 16
+// LLVM: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
+
+// OGCG: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// OGCG: store i32 0, ptr %[[EXCEPTION_ADDR]], align 16
+// OGCG: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
diff --git a/clang/test/CIR/IR/dynamic-cast.cir b/clang/test/CIR/IR/dynamic-cast.cir
new file mode 100644
index 0000000..283f11e
--- /dev/null
+++ b/clang/test/CIR/IR/dynamic-cast.cir
@@ -0,0 +1,59 @@
+// RUN: cir-opt --verify-roundtrip %s | FileCheck %s
+
+!s64i = !cir.int<s, 64>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!rec_Base = !cir.record<struct "Base" {!cir.vptr}>
+!rec_Derived = !cir.record<struct "Derived" {!rec_Base}>
+
+#dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
+
+// CHECK: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
+
+module {
+ cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
+ cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u8i>
+ cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+ cir.func private @__cxa_bad_cast()
+
+ cir.func @test_ptr_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+ %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+ cir.return %0 : !cir.ptr<!rec_Derived>
+ }
+
+ // CHECK: cir.func @test_ptr_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+ // CHECK: %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+ // CHECK: cir.return %0 : !cir.ptr<!rec_Derived>
+ // CHECK: }
+
+ cir.func @test_ref_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+ %0 = cir.dyn_cast ref %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+ cir.return %0 : !cir.ptr<!rec_Derived>
+ }
+
+ // CHECK: cir.func @test_ref_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!rec_Derived> {
+ // CHECK: %0 = cir.dyn_cast ref %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!rec_Derived> #dyn_cast_info__ZTI4Base__ZTI7Derived
+ // CHECK: cir.return %0 : !cir.ptr<!rec_Derived>
+ // CHECK: }
+
+ cir.func dso_local @test_cast_to_void(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+ %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+ cir.return %0 : !cir.ptr<!void>
+ }
+
+ // CHECK: cir.func dso_local @test_cast_to_void(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+ // CHECK: %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+ // CHECK: cir.return %0 : !cir.ptr<!void>
+ // CHECK: }
+
+ cir.func dso_local @test_relative_layout_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+ %0 = cir.dyn_cast ptr relative_layout %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+ cir.return %0 : !cir.ptr<!void>
+ }
+
+ // CHECK: cir.func dso_local @test_relative_layout_cast(%arg0: !cir.ptr<!rec_Base>) -> !cir.ptr<!void> {
+ // CHECK: %0 = cir.dyn_cast ptr relative_layout %arg0 : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
+ // CHECK: cir.return %0 : !cir.ptr<!void>
+ // CHECK: }
+}
diff --git a/clang/test/CIR/IR/invalid-dyn-cast.cir b/clang/test/CIR/IR/invalid-dyn-cast.cir
new file mode 100644
index 0000000..65c7fc1
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-dyn-cast.cir
@@ -0,0 +1,43 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s64i = !cir.int<s, 64>
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!Base = !cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>
+!Derived = !cir.record<struct "Derived" {!cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>}>
+
+module {
+ cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u32i>
+ cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u8i>
+ cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+ cir.func private @__cxa_bad_cast()
+ cir.func @test(%arg0 : !cir.ptr<!Base>) {
+ // expected-error@+1 {{srcRtti must be an RTTI pointer}}
+ %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u32i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u8i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
+ }
+}
+
+// -----
+
+!s64i = !cir.int<s, 64>
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!Base = !cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>
+!Derived = !cir.record<struct "Derived" {!cir.record<struct "Base" {!cir.ptr<!cir.ptr<!cir.func<() -> !cir.int<u, 32>>>>}>}>
+
+module {
+ cir.global "private" constant external @_ZTI4Base : !cir.ptr<!u8i>
+ cir.global "private" constant external @_ZTI7Derived : !cir.ptr<!u32i>
+ cir.func private @__dynamic_cast(!cir.ptr<!void>, !cir.ptr<!u8i>, !cir.ptr<!u8i>, !s64i) -> !cir.ptr<!void>
+ cir.func private @__cxa_bad_cast()
+ cir.func @test(%arg0 : !cir.ptr<!Base>) {
+ // expected-error@+1 {{destRtti must be an RTTI pointer}}
+ %0 = cir.dyn_cast ptr %arg0 : !cir.ptr<!Base> -> !cir.ptr<!Derived> #cir.dyn_cast_info<src_rtti = #cir.global_view<@_ZTI4Base> : !cir.ptr<!u8i>, dest_rtti = #cir.global_view<@_ZTI7Derived> : !cir.ptr<!u32i>, runtime_func = @__dynamic_cast, bad_cast_func = @__cxa_bad_cast, offset_hint = #cir.int<0> : !s64i>
+ }
+}
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index e9f4f83..bcb6bd6 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -103,7 +103,6 @@ list(APPEND CLANG_TEST_DEPS
clang-linker-wrapper
clang-nvlink-wrapper
clang-offload-bundler
- clang-offload-packager
clang-sycl-linker
diagtool
hmaptool
@@ -173,6 +172,7 @@ if( NOT CLANG_BUILT_STANDALONE )
llvm-strip
llvm-symbolizer
llvm-windres
+ llvm-offload-binary
obj2yaml
opt
split-file
diff --git a/clang/test/CodeGen/X86/avx512fp16-builtins.c b/clang/test/CodeGen/X86/avx512fp16-builtins.c
index dbf89b3..2befff0 100644
--- a/clang/test/CodeGen/X86/avx512fp16-builtins.c
+++ b/clang/test/CodeGen/X86/avx512fp16-builtins.c
@@ -117,6 +117,7 @@ __m512h test_mm512_set1_pch(_Float16 _Complex h) {
// CHECK: bitcast <16 x float>{{.*}} to <32 x half>
return _mm512_set1_pch(h);
}
+TEST_CONSTEXPR(match_m512h(_mm512_set1_pch(1.0), 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0));
__m512h test_mm512_set_ph(_Float16 __h1, _Float16 __h2, _Float16 __h3, _Float16 __h4,
_Float16 __h5, _Float16 __h6, _Float16 __h7, _Float16 __h8,
@@ -340,18 +341,21 @@ __m128h test_mm256_castph256_ph128(__m256h __a) {
// CHECK: shufflevector <16 x half> %{{.*}}, <16 x half> %{{.*}}, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
return _mm256_castph256_ph128(__a);
}
+TEST_CONSTEXPR(match_m128h(_mm256_castph256_ph128((__m256h){-1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0, 14.0, -15.0, -16.0}), -1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0, 8.0));
__m128h test_mm512_castph512_ph128(__m512h __a) {
// CHECK-LABEL: test_mm512_castph512_ph128
// CHECK: shufflevector <32 x half> %{{.*}}, <32 x half> %{{.*}}, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
return _mm512_castph512_ph128(__a);
}
+TEST_CONSTEXPR(match_m128h(_mm512_castph512_ph128((__m512h){0.0, -1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0, 14.0, -15.0, -16.0, -17.0, 18.0, -19.0, 20.0, -21.0, 22.0, -23.0, 24.0, -25.0, 26.0, -27.0, 28.0, -29.0, 30.0, -31.0}), 0.0, -1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0));
__m256h test_mm512_castph512_ph256(__m512h __a) {
// CHECK-LABEL: test_mm512_castph512_ph256
// CHECK: shufflevector <32 x half> %{{.*}}, <32 x half> %{{.*}}, <16 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
return _mm512_castph512_ph256(__a);
}
+TEST_CONSTEXPR(match_m256h(_mm512_castph512_ph256((__m512h){-1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0, 14.0, -15.0, -16.0, -17.0, 18.0, -19.0, 20.0, -21.0, 22.0, -23.0, 24.0, -25.0, 26.0, -27.0, 28.0, -29.0, 30.0, -31.0, 32.0}), -1.0, 2.0, -3.0, 4.0, -5.0, 6.0, -7.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0, 14.0, -15.0, -16.0));
__m256h test_mm256_castph128_ph256(__m128h __a) {
// CHECK-LABEL: test_mm256_castph128_ph256
diff --git a/clang/test/CodeGen/X86/avx512vlfp16-builtins.c b/clang/test/CodeGen/X86/avx512vlfp16-builtins.c
index f1865aa..68d0984 100644
--- a/clang/test/CodeGen/X86/avx512vlfp16-builtins.c
+++ b/clang/test/CodeGen/X86/avx512vlfp16-builtins.c
@@ -17,12 +17,14 @@ _Float16 test_mm_cvtsh_h(__m128h __A) {
// CHECK: extractelement <8 x half> %{{.*}}, i32 0
return _mm_cvtsh_h(__A);
}
+TEST_CONSTEXPR(_mm_cvtsh_h((__m128h){-8.0, 7.0, -6.0, 5.0, -4.0, 3.0, -2.0, 1.0}) == -8.0);
_Float16 test_mm256_cvtsh_h(__m256h __A) {
// CHECK-LABEL: test_mm256_cvtsh_h
// CHECK: extractelement <16 x half> %{{.*}}, i32 0
return _mm256_cvtsh_h(__A);
}
+TEST_CONSTEXPR(_mm256_cvtsh_h((__m256h){-32.0, 31.0, -30.0, 29.0, -28.0, 27.0, -26.0, 25.0, -24.0, 23.0, -22.0, 21.0, -20.0, 19.0, -18.0, 17.0}) == -32.0);
__m128h test_mm_set_sh(_Float16 __h) {
// CHECK-LABEL: test_mm_set_sh
diff --git a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
index 26d17e7..3dd8a36 100644
--- a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
+++ b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
@@ -25,12 +25,6 @@
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 15 } }
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_700:.*]] = {{.*}}, i32 700, i32 15 } }
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_800:.*]] = {{.*}}, i32 800, i32 15 } }
-// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_900:.*]] = {{.*}}, i32 900, i32 15 } }
-// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1100:.*]] = {{.*}}, i32 1100, i32 15 } }
-// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1200:.*]] = {{.*}}, i32 1200, i32 15 } }
-// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1300:.*]] = {{.*}}, i32 1300, i32 15 } }
-// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1500:.*]] = {{.*}}, i32 1500, i32 15 } }
-// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1600:.*]] = {{.*}}, i32 1600, i32 15 } }
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1700:.*]] = {{.*}}, i32 1700, i32 15 } }
// CHECK-SANITIZE-ANYRECOVER-DAG: @[[LINE_1800:.*]] = {{.*}}, i32 1800, i32 20 } }
@@ -225,172 +219,6 @@ char *nullptr_allones_BAD(void) {
//------------------------------------------------------------------------------
-char *one_var(unsigned long offset) {
- // CHECK: define{{.*}} ptr @one_var(i64 noundef %[[OFFSET:.*]])
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8
- // CHECK-NEXT: store i64 %[[OFFSET]], ptr %[[OFFSET_ADDR]], align 8
- // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, ptr %[[OFFSET_ADDR]], align 8
- // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds nuw i8, ptr inttoptr (i64 1 to ptr), i64 %[[OFFSET_RELOADED]]
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[OR_OV:.+]] = or i1 %[[COMPUTED_OFFSET_OVERFLOWED]], false, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 1, %[[COMPUTED_OFFSET]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[OTHER_IS_NOT_NULL:.*]] = icmp ne ptr inttoptr (i64 1 to ptr), null
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[OTHER_IS_NOT_NULL]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[OR_OV]], true, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], 1, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
- // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_900]], i64 1, i64 %[[COMPUTED_GEP]])
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_900]], i64 1, i64 %[[COMPUTED_GEP]])
- // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
- // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
- // CHECK-SANITIZE: [[CONT]]:
- // CHECK-NEXT: ret ptr %[[ADD_PTR]]
- static char *const base = (char *)1;
-#line 900
- return base + offset;
-}
-
-char *one_zero(void) {
- // CHECK: define{{.*}} ptr @one_zero()
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-NEXT: ret ptr inttoptr (i64 1 to ptr)
- static char *const base = (char *)1;
- static const unsigned long offset = 0;
-#line 1000
- return base + offset;
-}
-
-char *one_one_OK(void) {
- // CHECK: define{{.*}} ptr @one_one_OK()
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-SANITIZE-NEXT: %[[CMP1:.*]] = icmp ne ptr inttoptr (i64 1 to ptr), null, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[CMP2:.*]] = icmp ne i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 1) to i64), i64 1), i64 1), 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COND:.*]] = icmp eq i1 %[[CMP1]], %[[CMP2]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[COND]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
- // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1100]], i64 1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 1) to i64), i64 1), i64 1))
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1100]], i64 1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 1) to i64), i64 1), i64 1))
- // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
- // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
- // CHECK-SANITIZE: [[CONT]]:
- // CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 1)
- static char *const base = (char *)1;
- static const unsigned long offset = 1;
-#line 1100
- return base + offset;
-}
-
-char *one_allones_BAD(void) {
- // CHECK: define{{.*}} ptr @one_allones_BAD()
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-SANITIZE-NEXT: %[[CMP1:.*]] = icmp ne ptr inttoptr (i64 1 to ptr), null, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[CMP2:.*]] = icmp ne i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 -1) to i64), i64 1), i64 1), 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COND:.*]] = icmp eq i1 %[[CMP1]], %[[CMP2]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[COND]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
- // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1200]], i64 1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 -1) to i64), i64 1), i64 1))
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1200]], i64 1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 -1) to i64), i64 1), i64 1))
- // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
- // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
- // CHECK-SANITIZE: [[CONT]]:
- // CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 1 to ptr), i64 -1)
- static char *const base = (char *)1;
- static const unsigned long offset = -1;
-#line 1200
- return base + offset;
-}
-
-//------------------------------------------------------------------------------
-
-char *allones_var(unsigned long offset) {
- // CHECK: define{{.*}} ptr @allones_var(i64 noundef %[[OFFSET:.*]])
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-NEXT: %[[OFFSET_ADDR:.*]] = alloca i64, align 8
- // CHECK-NEXT: store i64 %[[OFFSET]], ptr %[[OFFSET_ADDR]], align 8
- // CHECK-NEXT: %[[OFFSET_RELOADED:.*]] = load i64, ptr %[[OFFSET_ADDR]], align 8
- // CHECK-NEXT: %[[ADD_PTR:.*]] = getelementptr inbounds nuw i8, ptr inttoptr (i64 -1 to ptr), i64 %[[OFFSET_RELOADED]]
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_AGGREGATE:.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 1, i64 %[[OFFSET_RELOADED]]), !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_OVERFLOWED:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 1, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[OR_OV:.+]] = or i1 %[[COMPUTED_OFFSET_OVERFLOWED]], false, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET:.*]] = extractvalue { i64, i1 } %[[COMPUTED_OFFSET_AGGREGATE]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP:.*]] = add i64 -1, %[[COMPUTED_OFFSET]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[OTHER_IS_NOT_NULL:.*]] = icmp ne ptr inttoptr (i64 -1 to ptr), null, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_NOT_NULL:.*]] = icmp ne i64 %[[COMPUTED_GEP]], 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL:.*]] = icmp eq i1 %[[OTHER_IS_NOT_NULL]], %[[COMPUTED_GEP_IS_NOT_NULL]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW:.*]] = xor i1 %[[OR_OV]], true, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COMPUTED_GEP_IS_UGE_BASE:.*]] = icmp uge i64 %[[COMPUTED_GEP]], -1, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[GEP_DID_NOT_OVERFLOW:.*]] = and i1 %[[COMPUTED_GEP_IS_UGE_BASE]], %[[COMPUTED_OFFSET_DID_NOT_OVERFLOW]], !nosanitize
- // CHECK-SANITIZE-NEXT: %[[GEP_IS_OKAY:.*]] = and i1 %[[BOTH_POINTERS_ARE_NULL_OR_BOTH_ARE_NONNULL]], %[[GEP_DID_NOT_OVERFLOW]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[GEP_IS_OKAY]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
- // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1300]], i64 -1, i64 %[[COMPUTED_GEP]])
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1300]], i64 -1, i64 %[[COMPUTED_GEP]])
- // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
- // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
- // CHECK-SANITIZE: [[CONT]]:
- // CHECK-NEXT: ret ptr %[[ADD_PTR]]
- static char *const base = (char *)-1;
-#line 1300
- return base + offset;
-}
-
-char *allones_zero_OK(void) {
- // CHECK: define{{.*}} ptr @allones_zero_OK()
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-NEXT: ret ptr inttoptr (i64 -1 to ptr)
- static char *const base = (char *)-1;
- static const unsigned long offset = 0;
-#line 1400
- return base + offset;
-}
-
-char *allones_one_BAD(void) {
- // CHECK: define{{.*}} ptr @allones_one_BAD()
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-SANITIZE-NEXT: %[[CMP1:.*]] = icmp ne ptr inttoptr (i64 -1 to ptr), null, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[CMP2:.*]] = icmp ne i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 1) to i64), i64 -1), i64 -1), 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COND:.*]] = icmp eq i1 %[[CMP1]], %[[CMP2]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[COND]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
- // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1500]], i64 -1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 1) to i64), i64 -1), i64 -1))
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1500]], i64 -1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 1) to i64), i64 -1), i64 -1))
- // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
- // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
- // CHECK-SANITIZE: [[CONT]]:
- // CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 1)
- static char *const base = (char *)-1;
- static const unsigned long offset = 1;
-#line 1500
- return base + offset;
-}
-
-char *allones_allones_OK(void) {
- // CHECK: define{{.*}} ptr @allones_allones_OK()
- // CHECK-NEXT: [[ENTRY:.*]]:
- // CHECK-SANITIZE-NEXT: %[[CMP1:.*]] = icmp ne ptr inttoptr (i64 -1 to ptr), null, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[CMP2:.*]] = icmp ne i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 -1) to i64), i64 -1), i64 -1), 0, !nosanitize
- // CHECK-SANITIZE-NEXT: %[[COND:.*]] = icmp eq i1 %[[CMP1]], %[[CMP2]], !nosanitize
- // CHECK-SANITIZE-NEXT: br i1 %[[COND]], label %[[CONT:.*]], label %[[HANDLER_POINTER_OVERFLOW:[^,]+]],{{.*}} !nosanitize
- // CHECK-SANITIZE: [[HANDLER_POINTER_OVERFLOW]]:
- // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_pointer_overflow_abort(ptr @[[LINE_1600]], i64 -1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 -1) to i64), i64 -1), i64 -1))
- // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_pointer_overflow(ptr @[[LINE_1600]], i64 -1, i64 add (i64 sub (i64 ptrtoint (ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 -1) to i64), i64 -1), i64 -1))
- // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 19){{.*}}, !nosanitize
- // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
- // CHECK-SANITIZE: [[CONT]]:
- // CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr inttoptr (i64 -1 to ptr), i64 -1)
- static char *const base = (char *)-1;
- static const unsigned long offset = -1;
-#line 1600
- return base + offset;
-}
-
// C++ does not allow void* arithmetic even as a GNU extension. Replace void*
// with char* in that case to keep test expectations the same.
#ifdef __cplusplus
diff --git a/clang/test/CodeGenCXX/builtin-invoke.cpp b/clang/test/CodeGenCXX/builtin-invoke.cpp
index af66dfd4d..0f84f83 100644
--- a/clang/test/CodeGenCXX/builtin-invoke.cpp
+++ b/clang/test/CodeGenCXX/builtin-invoke.cpp
@@ -55,7 +55,7 @@ extern "C" void call_memptr(std::reference_wrapper<Callable> wrapper) {
// CHECK-NEXT: br label %memptr.end
// CHECK-EMPTY:
// CHECK-NEXT: memptr.end:
- // CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ @_ZN8Callable4funcEv, %memptr.nonvirtual ]
+ // CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ inttoptr (i64 ptrtoint (ptr @_ZN8Callable4funcEv to i64) to ptr), %memptr.nonvirtual ]
// CHECK-NEXT: call void %2(ptr noundef nonnull align 1 dereferenceable(1) %0)
// CHECK-NEXT: ret void
}
diff --git a/clang/test/Driver/aarch64-cpu-defaults-appleos26.c b/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
index 9917605..fe468a5 100644
--- a/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
+++ b/clang/test/Driver/aarch64-cpu-defaults-appleos26.c
@@ -4,12 +4,16 @@
// RUN: %clang -target arm64-apple-ios26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
// RUN: %clang -target arm64e-apple-ios26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+/// iOS 18 came before iOS 26, compare its defaults.
+// RUN: %clang -target arm64-apple-ios18 -### -c %s 2>&1 | FileCheck %s --check-prefix=A10
+// RUN: %clang -target arm64e-apple-ios18 -### -c %s 2>&1 | FileCheck %s --check-prefix=A12
+
/// arm64e/arm64_32 watchOS 26 default to apple-s6.
// RUN: %clang -target arm64e-apple-watchos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
// RUN: %clang -target arm64_32-apple-watchos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
-/// arm64 is new in watchOS 26, and defaults to apple-s6.
-// RUN: %clang -target arm64-apple-watchos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=S6
+/// arm64 is new in watchOS 26, and defaults to apple-s9.
+// RUN: %clang -target arm64-apple-watchos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=S9
/// llvm usually treats tvOS like iOS, but it runs on different hardware.
// RUN: %clang -target arm64-apple-tvos26 -### -c %s 2>&1 | FileCheck %s --check-prefix=A7
@@ -18,5 +22,7 @@
/// Simulators are tested with other Mac-like targets in aarch64-mac-cpus.c.
// A12: "-target-cpu" "apple-a12"
+// A10: "-target-cpu" "apple-a10"
// S6: "-target-cpu" "apple-s6"
+// S9: "-target-cpu" "apple-s9"
// A7: "-target-cpu" "apple-a7"
diff --git a/clang/test/Driver/amdgpu-openmp-sanitize-options.c b/clang/test/Driver/amdgpu-openmp-sanitize-options.c
index 985eca1..914e018 100644
--- a/clang/test/Driver/amdgpu-openmp-sanitize-options.c
+++ b/clang/test/Driver/amdgpu-openmp-sanitize-options.c
@@ -59,6 +59,6 @@
// GPUSAN: {{"[^"]*clang[^"]*" "-cc1" "-triple" "amdgcn-amd-amdhsa" "-aux-triple" "x86_64-unknown-linux-gnu".* "-emit-llvm-bc".* "-mlink-bitcode-file" "[^"]*asanrtl.bc".* "-mlink-bitcode-file" "[^"]*ockl.bc".* "-target-cpu" "(gfx908|gfx900)".* "-fopenmp".* "-fsanitize=address".* "-x" "c".*}}
// NOGPUSAN: {{"[^"]*clang[^"]*" "-cc1" "-triple" "amdgcn-amd-amdhsa" "-aux-triple" "x86_64-unknown-linux-gnu".* "-emit-llvm-bc".* "-target-cpu" "(gfx908|gfx900)".* "-fopenmp".* "-x" "c".*}}
-// SAN: {{"[^"]*clang-offload-packager[^"]*" "-o".* "--image=file=.*.bc,triple=amdgcn-amd-amdhsa,arch=gfx908(:xnack\-|:xnack\+)?,kind=openmp(,feature=(\-xnack|\+xnack))?"}}
+// SAN: {{"[^"]*llvm-offload-binary[^"]*" "-o".* "--image=file=.*.bc,triple=amdgcn-amd-amdhsa,arch=gfx908(:xnack\-|:xnack\+)?,kind=openmp(,feature=(\-xnack|\+xnack))?"}}
// SAN: {{"[^"]*clang[^"]*" "-cc1" "-triple" "x86_64-unknown-linux-gnu".* "-fopenmp".* "-fsanitize=address".* "--offload-targets=amdgcn-amd-amdhsa".* "-x" "ir".*}}
// SAN: {{"[^"]*clang-linker-wrapper[^"]*".* "--host-triple=x86_64-unknown-linux-gnu".* "--linker-path=[^"]*".* "--whole-archive" "[^"]*(libclang_rt.asan_static.a|libclang_rt.asan_static-x86_64.a)".* "--whole-archive" "[^"]*(libclang_rt.asan.a|libclang_rt.asan-x86_64.a)".*}}
diff --git a/clang/test/Driver/amdgpu-openmp-toolchain.c b/clang/test/Driver/amdgpu-openmp-toolchain.c
index 1091e6e..5e73e2d 100644
--- a/clang/test/Driver/amdgpu-openmp-toolchain.c
+++ b/clang/test/Driver/amdgpu-openmp-toolchain.c
@@ -22,7 +22,7 @@
// CHECK-PHASES: 6: offload, "host-openmp (x86_64-unknown-linux-gnu)" {2}, "device-openmp (amdgcn-amd-amdhsa:gfx906)" {5}, ir
// CHECK-PHASES: 7: backend, {6}, ir, (device-openmp, gfx906)
// CHECK-PHASES: 8: offload, "device-openmp (amdgcn-amd-amdhsa:gfx906)" {7}, ir
-// CHECK-PHASES: 9: clang-offload-packager, {8}, image, (device-openmp)
+// CHECK-PHASES: 9: llvm-offload-binary, {8}, image, (device-openmp)
// CHECK-PHASES: 10: offload, "host-openmp (x86_64-unknown-linux-gnu)" {2}, "device-openmp (x86_64-unknown-linux-gnu)" {9}, ir
// CHECK-PHASES: 11: backend, {10}, assembler, (host-openmp)
// CHECK-PHASES: 12: assembler, {11}, object, (host-openmp)
@@ -64,7 +64,7 @@
// RUN: %clang -### -target x86_64-pc-linux-gnu -fopenmp --offload-arch=gfx90a:sramecc-:xnack+ \
// RUN: -nogpulib %s 2>&1 | FileCheck %s --check-prefix=CHECK-TARGET-ID
// CHECK-TARGET-ID: "-cc1" "-triple" "amdgcn-amd-amdhsa" {{.*}} "-target-cpu" "gfx90a" "-target-feature" "-sramecc" "-target-feature" "+xnack"
-// CHECK-TARGET-ID: clang-offload-packager{{.*}}arch=gfx90a:sramecc-:xnack+,kind=openmp
+// CHECK-TARGET-ID: llvm-offload-binary{{.*}}arch=gfx90a:sramecc-:xnack+,kind=openmp
// RUN: not %clang -### -target x86_64-pc-linux-gnu -fopenmp --offload-arch=gfx90a,gfx90a:xnack+ \
// RUN: -nogpulib %s 2>&1 | FileCheck %s --check-prefix=CHECK-TARGET-ID-ERROR
diff --git a/clang/test/Driver/cuda-phases.cu b/clang/test/Driver/cuda-phases.cu
index 220a320..db7d29e 100644
--- a/clang/test/Driver/cuda-phases.cu
+++ b/clang/test/Driver/cuda-phases.cu
@@ -235,7 +235,7 @@
// NEW-DRIVER-RDC-NEXT: 12: backend, {11}, assembler, (device-cuda, sm_70)
// NEW-DRIVER-RDC-NEXT: 13: assembler, {12}, object, (device-cuda, sm_70)
// NEW-DRIVER-RDC-NEXT: 14: offload, "device-cuda (nvptx64-nvidia-cuda:sm_70)" {13}, object
-// NEW-DRIVER-RDC-NEXT: 15: clang-offload-packager, {8, 14}, image, (device-cuda)
+// NEW-DRIVER-RDC-NEXT: 15: llvm-offload-binary, {8, 14}, image, (device-cuda)
// NEW-DRIVER-RDC-NEXT: 16: offload, "host-cuda (powerpc64le-ibm-linux-gnu)" {2}, "device-cuda (powerpc64le-ibm-linux-gnu)" {15}, ir
// NEW-DRIVER-RDC-NEXT: 17: backend, {16}, assembler, (host-cuda)
// NEW-DRIVER-RDC-NEXT: 18: assembler, {17}, object, (host-cuda)
@@ -312,7 +312,7 @@
// LTO-NEXT: 10: compiler, {9}, ir, (device-cuda, sm_70)
// LTO-NEXT: 11: backend, {10}, lto-bc, (device-cuda, sm_70)
// LTO-NEXT: 12: offload, "device-cuda (nvptx64-nvidia-cuda:sm_70)" {11}, lto-bc
-// LTO-NEXT: 13: clang-offload-packager, {7, 12}, image, (device-cuda)
+// LTO-NEXT: 13: llvm-offload-binary, {7, 12}, image, (device-cuda)
// LTO-NEXT: 14: offload, "host-cuda (powerpc64le-ibm-linux-gnu)" {2}, "device-cuda (powerpc64le-ibm-linux-gnu)" {13}, ir
// LTO-NEXT: 15: backend, {14}, assembler, (host-cuda)
// LTO-NEXT: 16: assembler, {15}, object, (host-cuda)
diff --git a/clang/test/Driver/darwin-maccatalyst-error.c b/clang/test/Driver/darwin-maccatalyst-error.c
new file mode 100644
index 0000000..009249b
--- /dev/null
+++ b/clang/test/Driver/darwin-maccatalyst-error.c
@@ -0,0 +1,6 @@
+// RUN: not %clang -target x86_64-apple-ios13.0-macabi -c %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=CHECK-ERROR %s
+// RUN: not %clang -target x86_64-apple-ios12.0-macabi -c %s -### 2>&1 | \
+// RUN: FileCheck --check-prefix=CHECK-ERROR %s
+
+// CHECK-ERROR: error: invalid version number in '-target x86_64-apple-ios
diff --git a/clang/test/Driver/darwin-maccatalyst.c b/clang/test/Driver/darwin-maccatalyst.c
index 74a02ed..a8d53df 100644
--- a/clang/test/Driver/darwin-maccatalyst.c
+++ b/clang/test/Driver/darwin-maccatalyst.c
@@ -2,11 +2,6 @@
// RUN: FileCheck --check-prefix=CHECK-VERSION1 %s
// RUN: %clang -target x86_64-apple-ios-macabi -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-VERSION1 %s
-// RUN: not %clang -target x86_64-apple-ios13.0-macabi -c %s -### 2>&1 | \
-// RUN: FileCheck --check-prefix=CHECK-ERROR %s
-// RUN: not %clang -target x86_64-apple-ios12.0-macabi -c %s -### 2>&1 | \
-// RUN: FileCheck --check-prefix=CHECK-ERROR %s
// CHECK-VERSION1-NOT: error:
// CHECK-VERSION1: "x86_64-apple-ios13.1.0-macabi"
-// CHECK-ERROR: error: invalid version number in '-target x86_64-apple-ios
diff --git a/clang/test/Driver/env.c b/clang/test/Driver/env.c
index 56c037c..d1267c0 100644
--- a/clang/test/Driver/env.c
+++ b/clang/test/Driver/env.c
@@ -1,5 +1,7 @@
// Some assertions in this test use Linux style (/) file paths.
-// UNSUPPORTED: system-windows
+// TODO: Use LIBPATH on AIX
+// UNSUPPORTED: system-windows, system-aix
+
// RUN: bash -c env | grep LD_LIBRARY_PATH | sed -ne 's/^.*=//p' | tr -d '\n' > %t.ld_library_path
// The PATH variable is heavily used when trying to find a linker.
// RUN: env -i LC_ALL=C LD_LIBRARY_PATH="%{readfile:%t.ld_library_path}" CLANG_NO_DEFAULT_CONFIG=1 \
diff --git a/clang/test/Driver/hip-phases.hip b/clang/test/Driver/hip-phases.hip
index 6bac97a..13f682f 100644
--- a/clang/test/Driver/hip-phases.hip
+++ b/clang/test/Driver/hip-phases.hip
@@ -40,7 +40,7 @@
// OLD-DAG: [[P9:[0-9]+]]: offload, "device-[[T]] (amdgcn-amd-amdhsa:[[ARCH]])" {[[P8]]}, image
// NEW-DAG: [[P9:[0-9]+]]: offload, "device-[[T]] (amdgcn-amd-amdhsa:[[ARCH]])" {[[P6]]}, ir
// OLDN-DAG: [[P10:[0-9]+]]: linker, {[[P9]]}, hip-fatbin, (device-[[T]])
-// NEW-DAG: [[P10:[0-9]+]]: clang-offload-packager, {[[P9]]}, image, (device-[[T]])
+// NEW-DAG: [[P10:[0-9]+]]: llvm-offload-binary, {[[P9]]}, image, (device-[[T]])
// OLDR-DAG: [[P10:[0-9]+]]: linker, {[[P9]]}, object, (device-[[T]])
// OLDN-DAG: [[P11:[0-9]+]]: offload, "host-[[T]] (x86_64-unknown-linux-gnu)" {[[P2]]}, "device-[[T]] (amdgcn-amd-amdhsa)" {[[P10]]}, ir
@@ -665,7 +665,7 @@
// LTO-NEXT: 10: compiler, {9}, ir, (device-hip, gfx90a)
// LTO-NEXT: 11: backend, {10}, lto-bc, (device-hip, gfx90a)
// LTO-NEXT: 12: offload, "device-hip (amdgcn-amd-amdhsa:gfx90a)" {11}, lto-bc
-// LTO-NEXT: 13: clang-offload-packager, {7, 12}, image, (device-hip)
+// LTO-NEXT: 13: llvm-offload-binary, {7, 12}, image, (device-hip)
// LTO-NEXT: 14: offload, "host-hip (x86_64-unknown-linux-gnu)" {2}, "device-hip (x86_64-unknown-linux-gnu)" {13}, ir
// LTO-NEXT: 15: backend, {14}, assembler, (host-hip)
// LTO-NEXT: 16: assembler, {15}, object, (host-hip)
diff --git a/clang/test/Driver/hip-toolchain-no-rdc.hip b/clang/test/Driver/hip-toolchain-no-rdc.hip
index dc8f0a9..a94299e 100644
--- a/clang/test/Driver/hip-toolchain-no-rdc.hip
+++ b/clang/test/Driver/hip-toolchain-no-rdc.hip
@@ -97,7 +97,7 @@
// OLD-SAME: "-targets={{.*}},hipv4-amdgcn-amd-amdhsa--gfx803,hipv4-amdgcn-amd-amdhsa--gfx900"
// OLD-SAME: "-input={{.*}}" "-input=[[IMG_DEV_A_803]]" "-input=[[IMG_DEV_A_900]]" "-output=[[BUNDLE_A:.*hipfb]]"
-// NEW: [[PACKAGER:".*clang-offload-packager"]] "-o" "[[PACKAGE_A:.*.out]]"
+// NEW: [[PACKAGER:".*llvm-offload-binary"]] "-o" "[[PACKAGE_A:.*.out]]"
// NEW-SAME: "--image=file=[[OBJ_DEV_A_803]],triple=amdgcn-amd-amdhsa,arch=gfx803,kind=hip"
// NEW-SAME: "--image=file=[[OBJ_DEV_A_900]],triple=amdgcn-amd-amdhsa,arch=gfx900,kind=hip"
@@ -169,7 +169,7 @@
// OLD-SAME: "-targets={{.*}},hipv4-amdgcn-amd-amdhsa--gfx803,hipv4-amdgcn-amd-amdhsa--gfx900"
// OLD-SAME: "-input={{.*}}" "-input=[[IMG_DEV_B_803]]" "-input=[[IMG_DEV_B_900]]" "-output=[[BUNDLE_B:.*hipfb]]"
-// NEW: [[PACKAGER:".*clang-offload-packager"]] "-o" "[[PACKAGE_B:.*.out]]"
+// NEW: [[PACKAGER:".*llvm-offload-binary"]] "-o" "[[PACKAGE_B:.*.out]]"
// NEW-SAME: "--image=file=[[OBJ_DEV_B_803]],triple=amdgcn-amd-amdhsa,arch=gfx803,kind=hip"
// NEW-SAME: "--image=file=[[OBJ_DEV_B_900]],triple=amdgcn-amd-amdhsa,arch=gfx900,kind=hip"
diff --git a/clang/test/Driver/linker-wrapper-image.c b/clang/test/Driver/linker-wrapper-image.c
index 3147617..b932712 100644
--- a/clang/test/Driver/linker-wrapper-image.c
+++ b/clang/test/Driver/linker-wrapper-image.c
@@ -5,7 +5,7 @@
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.elf.o
-// RUN: clang-offload-packager -o %t.out --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
+// RUN: llvm-offload-binary -o %t.out --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
@@ -42,7 +42,7 @@
// OPENMP-NEXT: ret void
// OPENMP-NEXT: }
-// RUN: clang-offload-packager -o %t.out --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_70
+// RUN: llvm-offload-binary -o %t.out --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
@@ -153,7 +153,7 @@
// CUDA-NEXT: ret void
// CUDA-NEXT: }
-// RUN: clang-offload-packager -o %t.out --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx908
+// RUN: llvm-offload-binary -o %t.out --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
@@ -265,7 +265,7 @@
// HIP-NEXT: ret void
// HIP-NEXT: }
-// RUN: clang-offload-packager -o %t.out --image=file=%t.elf.o,kind=sycl,triple=spirv64-unknown-unknown,arch=generic
+// RUN: llvm-offload-binary -o %t.out --image=file=%t.elf.o,kind=sycl,triple=spirv64-unknown-unknown,arch=generic
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
diff --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c
index 1c0fb96..52a961d 100644
--- a/clang/test/Driver/linker-wrapper.c
+++ b/clang/test/Driver/linker-wrapper.c
@@ -12,7 +12,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: %clang -cc1 %s -triple amdgcn-amd-amdhsa -emit-llvm-bc -o %t.amdgpu.bc
// RUN: %clang -cc1 %s -triple spirv64-unknown-unknown -emit-llvm-bc -o %t.spirv.bc
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -24,7 +24,7 @@ __attribute__((visibility("protected"), used)) int x;
// NVPTX-LINK: clang{{.*}} -o {{.*}}.img -dumpdir a.out.nvptx64.sm_70.img. --target=nvptx64-nvidia-cuda -march=sm_70 {{.*}}.o {{.*}}.o
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -33,7 +33,7 @@ __attribute__((visibility("protected"), used)) int x;
// NVPTX-LINK-DEBUG: clang{{.*}} --target=nvptx64-nvidia-cuda -march=sm_70 {{.*}}-g
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -42,7 +42,7 @@ __attribute__((visibility("protected"), used)) int x;
// AMDGPU-LINK: clang{{.*}} -o {{.*}}.img -dumpdir a.out.amdgcn.gfx908.img. --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.amdgpu.bc,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx1030 \
// RUN: --image=file=%t.amdgpu.bc,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx1030
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -51,7 +51,7 @@ __attribute__((visibility("protected"), used)) int x;
// AMDGPU-LTO-TEMPS: clang{{.*}} --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -flto {{.*}}-save-temps
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.spirv.bc,kind=sycl,triple=spirv64-unknown-unknown,arch=generic
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
@@ -59,7 +59,7 @@ __attribute__((visibility("protected"), used)) int x;
// SPIRV-LINK: clang{{.*}} -o {{.*}}.img -dumpdir a.out.spirv64..img. --target=spirv64-unknown-unknown {{.*}}.o --sycl-link -Xlinker -triple=spirv64-unknown-unknown -Xlinker -arch=
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -77,12 +77,12 @@ __attribute__((visibility("protected"), used)) int x;
// HOST-LINK: ld.lld{{.*}}-a -b -c {{.*}}.o -o a.out
// HOST-LINK-NOT: ld.lld{{.*}}-abc
-// RUN: clang-offload-packager -o %t-lib.out \
+// RUN: llvm-offload-binary -o %t-lib.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_52
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t-lib.out
// RUN: llvm-ar rcs %t.a %t.o
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-obj.o -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
@@ -91,7 +91,7 @@ __attribute__((visibility("protected"), used)) int x;
// STATIC-LIBRARY: clang{{.*}} -march=sm_70
// STATIC-LIBRARY-NOT: clang{{.*}} -march=sm_50
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_52
@@ -105,7 +105,7 @@ __attribute__((visibility("protected"), used)) int x;
// CUDA: fatbinary{{.*}}-64 --create {{.*}}.fatbin --image=profile=sm_70,file=[[IMG_SM70]] --image=profile=sm_52,file=[[IMG_SM52]]
// CUDA: usr/bin/ld{{.*}} {{.*}}.openmp.image.{{.*}}.o {{.*}}.cuda.image.{{.*}}.o
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_80 \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_75 \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_70 \
@@ -119,7 +119,7 @@ __attribute__((visibility("protected"), used)) int x;
// CUDA-PAR: fatbinary{{.*}}-64 --create {{.*}}.fatbin
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx90a \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a \
// RUN: --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx908
@@ -133,7 +133,7 @@ __attribute__((visibility("protected"), used)) int x;
// HIP: clang{{.*}} -o [[IMG_GFX908:.+]] -dumpdir a.out.amdgcn.gfx908.img. --target=amdgcn-amd-amdhsa -mcpu=gfx908
// HIP: clang-offload-bundler{{.*}}-type=o -bundle-align=4096 -compress -compression-level=6 -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx90a,hip-amdgcn-amd-amdhsa--gfx908 -input={{/dev/null|NUL}} -input=[[IMG_GFX90A]] -input=[[IMG_GFX908]] -output={{.*}}.hipfb
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
@@ -152,7 +152,7 @@ __attribute__((visibility("protected"), used)) int x;
// MISSING-LIBRARY: error: unable to find library -ldummy
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.amdgpu.bc,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t.amdgpu.bc,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -161,7 +161,7 @@ __attribute__((visibility("protected"), used)) int x;
// CLANG-BACKEND: clang{{.*}} -o {{.*}}.img -dumpdir a.out.amdgcn.gfx908.img. --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-windows-msvc -emit-obj -o %t.o -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-windows-msvc --dry-run \
@@ -169,14 +169,14 @@ __attribute__((visibility("protected"), used)) int x;
// COFF: "/usr/bin/lld-link" {{.*}}.o -libpath:./ -out:a.exe {{.*}}openmp.image.wrapper{{.*}}
-// RUN: clang-offload-packager -o %t-lib.out \
+// RUN: llvm-offload-binary -o %t-lib.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t-lib.out
// RUN: llvm-ar rcs %t.a %t.o
-// RUN: clang-offload-packager -o %t-on.out \
+// RUN: llvm-offload-binary -o %t-on.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a:xnack+
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-on.o -fembed-offload-object=%t-on.out
-// RUN: clang-offload-packager -o %t-off.out \
+// RUN: llvm-offload-binary -o %t-off.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a:xnack-
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-off.o -fembed-offload-object=%t-off.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
@@ -185,14 +185,14 @@ __attribute__((visibility("protected"), used)) int x;
// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img -dumpdir a.out.amdgcn.gfx90a:xnack+.img. --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack+ -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// AMD-TARGET-ID: clang{{.*}} -o {{.*}}.img -dumpdir a.out.amdgcn.gfx90a:xnack-.img. --target=amdgcn-amd-amdhsa -mcpu=gfx90a:xnack- -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
-// RUN: clang-offload-packager -o %t-lib.out \
+// RUN: llvm-offload-binary -o %t-lib.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=generic
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t-lib.out
// RUN: llvm-ar rcs %t.a %t.o
-// RUN: clang-offload-packager -o %t1.out \
+// RUN: llvm-offload-binary -o %t1.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t1.o -fembed-offload-object=%t1.out
-// RUN: clang-offload-packager -o %t2.out \
+// RUN: llvm-offload-binary -o %t2.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t2.o -fembed-offload-object=%t2.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
@@ -201,7 +201,7 @@ __attribute__((visibility("protected"), used)) int x;
// ARCH-ALL: clang{{.*}} -o {{.*}}.img -dumpdir a.out.amdgcn.gfx90a.img. --target=amdgcn-amd-amdhsa -mcpu=gfx90a -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
// ARCH-ALL: clang{{.*}} -o {{.*}}.img -dumpdir a.out.amdgcn.gfx908.img. --target=amdgcn-amd-amdhsa -mcpu=gfx908 -flto -Wl,--no-undefined {{.*}}.o {{.*}}.o
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -213,7 +213,7 @@ __attribute__((visibility("protected"), used)) int x;
// RELOCATABLE-LINK: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx90a \
// RUN: --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx90a
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -227,7 +227,7 @@ __attribute__((visibility("protected"), used)) int x;
// RELOCATABLE-LINK-HIP: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading
// RELOCATABLE-LINK-HIP: --rename-section llvm_offload_entries
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_89 \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_89
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
@@ -247,7 +247,7 @@ __attribute__((visibility("protected"), used)) int x;
// OVERRIDE-NOT: clang
// OVERRIDE: /usr/bin/ld
-// RUN: clang-offload-packager -o %t.out \
+// RUN: llvm-offload-binary -o %t.out \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
diff --git a/clang/test/Driver/offload-packager.c b/clang/test/Driver/offload-packager.c
index fb5f100..adf2565 100644
--- a/clang/test/Driver/offload-packager.c
+++ b/clang/test/Driver/offload-packager.c
@@ -7,26 +7,26 @@
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t/elf.o
// Check that we can extract files from the packaged binary.
-// RUN: clang-offload-packager -o %t/package.out \
+// RUN: llvm-offload-binary -o %t/package.out \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_80 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90c
-// RUN: clang-offload-packager %t/package.out \
+// RUN: llvm-offload-binary %t/package.out \
// RUN: --image=file=%t/sm_70.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t/gfx908.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: diff %t/sm_70.o %t/elf.o
// RUN: diff %t/gfx908.o %t/elf.o
// Check that we generate a new name if one is not given
-// RUN: clang-offload-packager -o %t/package \
+// RUN: llvm-offload-binary -o %t/package \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_80 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx90a \
// RUN: --image=file=%t/elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx90c
-// RUN: clang-offload-packager %t/package --image=kind=openmp
+// RUN: llvm-offload-binary %t/package --image=kind=openmp
// RUN: diff *-nvptx64-nvidia-cuda-sm_70.0.o %t/elf.o; rm *-nvptx64-nvidia-cuda-sm_70.0.o
// RUN: diff *-nvptx64-nvidia-cuda-sm_80.1.o %t/elf.o; rm *-nvptx64-nvidia-cuda-sm_80.1.o
// RUN: diff *-amdgcn-amd-amdhsa-gfx908.2.o %t/elf.o; rm *-amdgcn-amd-amdhsa-gfx908.2.o
@@ -34,33 +34,33 @@
// RUN: not diff *-amdgcn-amd-amdhsa-gfx90c.4.o %t/elf.o
// Check that we can extract from an ELF object file
-// RUN: clang-offload-packager -o %t/package.out \
+// RUN: llvm-offload-binary -o %t/package.out \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t/package.o -fembed-offload-object=%t/package.out
-// RUN: clang-offload-packager %t/package.out \
+// RUN: llvm-offload-binary %t/package.out \
// RUN: --image=file=%t/sm_70.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t/gfx908.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: diff %t/sm_70.o %t/elf.o
// RUN: diff %t/gfx908.o %t/elf.o
// Check that we can extract from a bitcode file
-// RUN: clang-offload-packager -o %t/package.out \
+// RUN: llvm-offload-binary -o %t/package.out \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -o %t/package.bc -fembed-offload-object=%t/package.out
-// RUN: clang-offload-packager %t/package.out \
+// RUN: llvm-offload-binary %t/package.out \
// RUN: --image=file=%t/sm_70.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
// RUN: --image=file=%t/gfx908.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
// RUN: diff %t/sm_70.o %t/elf.o
// RUN: diff %t/gfx908.o %t/elf.o
// Check that we can extract from an archive file to an archive file.
-// RUN: clang-offload-packager -o %t/package.out \
+// RUN: llvm-offload-binary -o %t/package.out \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
// RUN: --image=file=%t/elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t/package.o -fembed-offload-object=%t/package.out
// RUN: llvm-ar rcs %t/package.a %t/package.o
-// RUN: clang-offload-packager %t/package.a --archive --image=file=%t/gfx908.a,arch=gfx908
+// RUN: llvm-offload-binary %t/package.a --archive --image=file=%t/gfx908.a,arch=gfx908
// RUN: llvm-ar t %t/gfx908.a 2>&1 | FileCheck %s
// CHECK: {{.*}}.o
diff --git a/clang/test/Driver/openmp-offload-gpu.c b/clang/test/Driver/openmp-offload-gpu.c
index 77f4cfb..edce14e 100644
--- a/clang/test/Driver/openmp-offload-gpu.c
+++ b/clang/test/Driver/openmp-offload-gpu.c
@@ -242,7 +242,7 @@
// CHECK-PHASES: 7: backend, {6}, assembler, (device-openmp, sm_52)
// CHECK-PHASES: 8: assembler, {7}, object, (device-openmp, sm_52)
// CHECK-PHASES: 9: offload, "device-openmp (nvptx64-nvidia-cuda:sm_52)" {8}, object
-// CHECK-PHASES: 10: clang-offload-packager, {9}, image
+// CHECK-PHASES: 10: llvm-offload-binary, {9}, image
// CHECK-PHASES: 11: offload, "host-openmp (x86_64-unknown-linux-gnu)" {2}, "device-openmp (x86_64-unknown-linux-gnu)" {10}, ir
// CHECK-PHASES: 12: backend, {11}, assembler, (host-openmp)
// CHECK-PHASES: 13: assembler, {12}, object, (host-openmp)
@@ -346,13 +346,13 @@
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fopenmp=libomp --offload-arch=sm_52 -nogpulib \
// RUN: -foffload-lto %s 2>&1 | FileCheck --check-prefix=CHECK-LTO-FEATURES %s
-// CHECK-LTO-FEATURES: clang-offload-packager{{.*}}--image={{.*}}feature=+ptx{{[0-9]+}}
+// CHECK-LTO-FEATURES: llvm-offload-binary{{.*}}--image={{.*}}feature=+ptx{{[0-9]+}}
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fopenmp=libomp --offload-arch=sm_52 -nogpulib \
// RUN: -Xopenmp-target=nvptx64-nvidia-cuda --cuda-feature=+ptx64 -foffload-lto %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-SET-FEATURES %s
-// CHECK-SET-FEATURES: clang-offload-packager{{.*}}--image={{.*}}feature=+ptx64
+// CHECK-SET-FEATURES: llvm-offload-binary{{.*}}--image={{.*}}feature=+ptx64
//
// Check that `-Xarch_host` works for OpenMP offloading.
diff --git a/clang/test/Driver/openmp-offload-jit.c b/clang/test/Driver/openmp-offload-jit.c
index b3566f0..6ced5c1 100644
--- a/clang/test/Driver/openmp-offload-jit.c
+++ b/clang/test/Driver/openmp-offload-jit.c
@@ -25,7 +25,7 @@
// PHASES-JIT-NEXT: 6: offload, "host-openmp (x86_64-unknown-linux-gnu)" {2}, "device-openmp ([[TARGET:.+]])" {5}, ir
// PHASES-JIT-NEXT: 7: backend, {6}, lto-bc, (device-openmp, {{.*}})
// PHASES-JIT-NEXT: 8: offload, "device-openmp ([[TARGET]])" {7}, lto-bc
-// PHASES-JIT-NEXT: 9: clang-offload-packager, {8}, image, (device-openmp)
+// PHASES-JIT-NEXT: 9: llvm-offload-binary, {8}, image, (device-openmp)
// PHASES-JIT-NEXT: 10: offload, "host-openmp (x86_64-unknown-linux-gnu)" {2}, "device-openmp (x86_64-unknown-linux-gnu)" {9}, ir
// PHASES-JIT-NEXT: 11: backend, {10}, assembler, (host-openmp)
// PHASES-JIT-NEXT: 12: assembler, {11}, object, (host-openmp)
diff --git a/clang/test/Driver/openmp-offload.c b/clang/test/Driver/openmp-offload.c
index 64d45f9..fce1b88 100644
--- a/clang/test/Driver/openmp-offload.c
+++ b/clang/test/Driver/openmp-offload.c
@@ -103,7 +103,7 @@
// CHK-PHASES-NEXT: 7: backend, {6}, assembler, (device-openmp)
// CHK-PHASES-NEXT: 8: assembler, {7}, object, (device-openmp)
// CHK-PHASES-NEXT: 9: offload, "device-openmp (powerpc64-ibm-linux-gnu)" {8}, object
-// CHK-PHASES-NEXT: 10: clang-offload-packager, {9}, image, (device-openmp)
+// CHK-PHASES-NEXT: 10: llvm-offload-binary, {9}, image, (device-openmp)
// CHK-PHASES-NEXT: 11: offload, "host-openmp (powerpc64-ibm-linux-gnu)" {2}, "device-openmp (powerpc64-ibm-linux-gnu)" {10}, ir
// CHK-PHASES-NEXT: 12: backend, {11}, assembler, (host-openmp)
// CHK-PHASES-NEXT: 13: assembler, {12}, object, (host-openmp)
@@ -132,7 +132,7 @@
// CHK-PHASES-FILES-NEXT: 15: backend, {14}, assembler, (device-openmp)
// CHK-PHASES-FILES-NEXT: 16: assembler, {15}, object, (device-openmp)
// CHK-PHASES-FILES-NEXT: 17: offload, "device-openmp (x86_64-pc-linux-gnu)" {16}, object
-// CHK-PHASES-FILES-NEXT: 18: clang-offload-packager, {10, 17}, image, (device-openmp)
+// CHK-PHASES-FILES-NEXT: 18: llvm-offload-binary, {10, 17}, image, (device-openmp)
// CHK-PHASES-FILES-NEXT: 19: offload, "host-openmp (powerpc64-ibm-linux-gnu)" {3}, "device-openmp (powerpc64-ibm-linux-gnu)" {18}, ir
// CHK-PHASES-FILES-NEXT: 20: backend, {19}, assembler, (host-openmp)
// CHK-PHASES-FILES-NEXT: 21: assembler, {20}, object, (host-openmp)
@@ -153,7 +153,7 @@
// CHK-PHASES-FILES-NEXT: 36: backend, {35}, assembler, (device-openmp)
// CHK-PHASES-FILES-NEXT: 37: assembler, {36}, object, (device-openmp)
// CHK-PHASES-FILES-NEXT: 38: offload, "device-openmp (x86_64-pc-linux-gnu)" {37}, object
-// CHK-PHASES-FILES-NEXT: 39: clang-offload-packager, {31, 38}, image, (device-openmp)
+// CHK-PHASES-FILES-NEXT: 39: llvm-offload-binary, {31, 38}, image, (device-openmp)
// CHK-PHASES-FILES-NEXT: 40: offload, "host-openmp (powerpc64-ibm-linux-gnu)" {24}, "device-openmp (powerpc64-ibm-linux-gnu)" {39}, ir
// CHK-PHASES-FILES-NEXT: 41: backend, {40}, assembler, (host-openmp)
// CHK-PHASES-FILES-NEXT: 42: assembler, {41}, object, (host-openmp)
diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c
index c44a0e8..7972b9e 100644
--- a/clang/test/Driver/print-supported-extensions-riscv.c
+++ b/clang/test/Driver/print-supported-extensions-riscv.c
@@ -216,7 +216,7 @@
// CHECK-NEXT: zibi 0.1 'Zibi' (Branch with Immediate)
// CHECK-NEXT: zicfilp 1.0 'Zicfilp' (Landing pad)
// CHECK-NEXT: zicfiss 1.0 'Zicfiss' (Shadow stack)
-// CHECK-NEXT: zalasr 0.1 'Zalasr' (Load-Acquire and Store-Release Instructions)
+// CHECK-NEXT: zalasr 0.9 'Zalasr' (Load-Acquire and Store-Release Instructions)
// CHECK-NEXT: zvbc32e 0.7 'Zvbc32e' (Vector Carryless Multiplication with 32-bits elements)
// CHECK-NEXT: zvfbfa 0.1 'Zvfbfa' (Additional BF16 vector compute support)
// CHECK-NEXT: zvfofp8min 0.2 'Zvfofp8min' (Vector OFP8 Converts)
diff --git a/clang/test/Driver/riscv-arch.c b/clang/test/Driver/riscv-arch.c
index 1da8311..37fe7a0 100644
--- a/clang/test/Driver/riscv-arch.c
+++ b/clang/test/Driver/riscv-arch.c
@@ -384,9 +384,9 @@
// RUN: not %clang --target=riscv32-unknown-elf -march=rv32izalasr0p7 -menable-experimental-extensions -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-BADVERS %s
// RV32-EXPERIMENTAL-BADVERS: error: invalid arch name 'rv32izalasr0p7'
-// RV32-EXPERIMENTAL-BADVERS: unsupported version number 0.7 for experimental extension 'zalasr' (this compiler supports 0.1)
+// RV32-EXPERIMENTAL-BADVERS: unsupported version number 0.7 for experimental extension 'zalasr' (this compiler supports 0.9)
-// RUN: %clang --target=riscv32-unknown-elf -march=rv32izalasr0p1 -menable-experimental-extensions -### %s \
+// RUN: %clang --target=riscv32-unknown-elf -march=rv32izalasr0p9 -menable-experimental-extensions -### %s \
// RUN: -fsyntax-only 2>&1 | FileCheck -check-prefix=RV32-EXPERIMENTAL-GOODVERS %s
// RV32-EXPERIMENTAL-GOODVERS: "-target-feature" "+experimental-zalasr"
diff --git a/clang/test/Driver/spirv-openmp-toolchain.c b/clang/test/Driver/spirv-openmp-toolchain.c
index 1542f50..6bf8984 100644
--- a/clang/test/Driver/spirv-openmp-toolchain.c
+++ b/clang/test/Driver/spirv-openmp-toolchain.c
@@ -21,7 +21,7 @@
// CHECK-PHASES: 7: backend, {6}, assembler, (device-openmp)
// CHECK-PHASES: 8: assembler, {7}, object, (device-openmp)
// CHECK-PHASES: 9: offload, "device-openmp (spirv64-intel)" {8}, object
-// CHECK-PHASES: 10: clang-offload-packager, {9}, image, (device-openmp)
+// CHECK-PHASES: 10: llvm-offload-binary, {9}, image, (device-openmp)
// CHECK-PHASES: 11: offload, "host-openmp (x86_64-unknown-linux-gnu)" {2}, "device-openmp (x86_64-unknown-linux-gnu)" {10}, ir
// CHECK-PHASES: 12: backend, {11}, assembler, (host-openmp)
// CHECK-PHASES: 13: assembler, {12}, object, (host-openmp)
diff --git a/clang/test/Driver/sycl-offload-jit.cpp b/clang/test/Driver/sycl-offload-jit.cpp
index e040f4d..72c2390 100644
--- a/clang/test/Driver/sycl-offload-jit.cpp
+++ b/clang/test/Driver/sycl-offload-jit.cpp
@@ -13,21 +13,21 @@
// CHK-PHASES-NEXT: 5: compiler, {4}, ir, (device-sycl)
// CHK-PHASES-NEXT: 6: backend, {5}, ir, (device-sycl)
// CHK-PHASES-NEXT: 7: offload, "device-sycl (spirv64-unknown-unknown)" {6}, ir
-// CHK-PHASES-NEXT: 8: clang-offload-packager, {7}, image, (device-sycl)
+// CHK-PHASES-NEXT: 8: llvm-offload-binary, {7}, image, (device-sycl)
// CHK-PHASES-NEXT: 9: offload, "host-sycl (x86_64{{.*}})" {2}, "device-sycl (x86_64{{.*}})" {8}, ir
// CHK-PHASES-NEXT: 10: backend, {9}, assembler, (host-sycl)
// CHK-PHASES-NEXT: 11: assembler, {10}, object, (host-sycl)
// CHK-PHASES-NEXT: 12: clang-linker-wrapper, {11}, image, (host-sycl)
/// Check expected default values for device compilation when using -fsycl as
-/// well as clang-offload-packager inputs.
+/// well as llvm-offload-binary inputs.
// RUN: %clang -### -fsycl -c --target=x86_64-unknown-linux-gnu %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHK-DEVICE-TRIPLE %s
// CHK-DEVICE-TRIPLE: "-cc1"{{.*}} "-triple" "spirv64-unknown-unknown"
// CHK-DEVICE-TRIPLE-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
// CHK-DEVICE-TRIPLE-SAME: "-fsycl-is-device"
// CHK-DEVICE-TRIPLE-SAME: "-O2"
-// CHK-DEVICE-TRIPLE: clang-offload-packager{{.*}} "--image=file={{.*}}.bc,triple=spirv64-unknown-unknown,arch=generic,kind=sycl"
+// CHK-DEVICE-TRIPLE: llvm-offload-binary{{.*}} "--image=file={{.*}}.bc,triple=spirv64-unknown-unknown,arch=generic,kind=sycl"
/// Check -fsycl-is-device is passed when compiling for the device.
/// Check -fsycl-is-host is passed when compiling for host.
diff --git a/clang/test/Preprocessor/riscv-target-features.c b/clang/test/Preprocessor/riscv-target-features.c
index 71d8453..77731a9 100644
--- a/clang/test/Preprocessor/riscv-target-features.c
+++ b/clang/test/Preprocessor/riscv-target-features.c
@@ -1531,12 +1531,12 @@
// Experimental extensions
// RUN: %clang --target=riscv32 -menable-experimental-extensions \
-// RUN: -march=rv32i_zalasr0p1 -E -dM %s \
+// RUN: -march=rv32i_zalasr0p9 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-ZALASR-EXT %s
// RUN: %clang --target=riscv64 -menable-experimental-extensions \
-// RUN: -march=rv64i_zalasr0p1 -E -dM %s \
+// RUN: -march=rv64i_zalasr0p9 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-ZALASR-EXT %s
-// CHECK-ZALASR-EXT: __riscv_zalasr 1000{{$}}
+// CHECK-ZALASR-EXT: __riscv_zalasr 9000{{$}}
// RUN: %clang --target=riscv32 -menable-experimental-extensions \
// RUN: -march=rv32izfbfmin1p0 -E -dM %s \
diff --git a/clang/test/Sema/format-strings-signedness.cpp b/clang/test/Sema/format-strings-signedness.cpp
new file mode 100644
index 0000000..66bf1de
--- /dev/null
+++ b/clang/test/Sema/format-strings-signedness.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -verify -Wformat -Wformat-signedness %s
+// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fsyntax-only -verify -Wformat -Wformat-signedness %s
+
+// Verify that -Wformat-signedness alone (without -Wformat) trigger the
+// warnings. Note in gcc this will not trigger the signedness warnings as
+// -Wformat is default off in gcc.
+// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -verify -Wformat-signedness %s
+// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fsyntax-only -verify -Wformat-signedness %s
+
+// Verify that -Wformat-signedness warnings are not reported with only -Wformat
+// (gcc compat).
+// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -Wformat -verify=okay %s
+
+// Verify that -Wformat-signedness with -Wno-format are not reported (gcc compat).
+// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -Wformat-signedness -Wno-format -verify=okay %s
+// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -Wno-format -Wformat-signedness -verify=okay %s
+// okay-no-diagnostics
+
+// Ignore 'GCC requires a function with the 'format' attribute to be variadic'.
+#pragma GCC diagnostic ignored "-Wgcc-compat"
+namespace GH161075 {
+template <typename... Args>
+void format(const char *fmt, Args &&...args)
+ __attribute__((format(printf, 1, 2)));
+
+void do_format() {
+ bool b = false;
+ format("%hhi %hhu %hi %hu %i %u", b, b, b, b, b, b); // expected-warning {{format specifies type 'unsigned char' but the argument has type 'bool', which differs in signedness}}
+}
+} // namespace GH161075
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index f6bec6e..4f234f0 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -126,11 +126,15 @@ void definite_single_pointer_multiple_loans_gsl(bool cond) {
v.use(); // expected-note 2 {{later used here}}
}
-
-//===----------------------------------------------------------------------===//
-// Potential (Maybe) Use-After-Free (-W...strict)
-// These are cases where the pointer *may* become dangling, depending on the path taken.
-//===----------------------------------------------------------------------===//
+void definite_if_branch(bool cond) {
+ MyObj safe;
+ MyObj* p = &safe;
+ if (cond) {
+ MyObj temp;
+ p = &temp; // expected-warning {{object whose reference is captured does not live long enough}}
+ } // expected-note {{destroyed here}}
+ (void)*p; // expected-note {{later used here}}
+}
void potential_if_branch(bool cond) {
MyObj safe;
@@ -139,15 +143,18 @@ void potential_if_branch(bool cond) {
MyObj temp;
p = &temp; // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
- (void)*p; // expected-note {{later used here}}
+ if (!cond)
+ (void)*p; // expected-note {{later used here}}
+ else
+ p = &safe;
}
-void potential_if_branch_gsl(bool cond) {
+void definite_if_branch_gsl(bool cond) {
MyObj safe;
View v = safe;
if (cond) {
MyObj temp;
- v = temp; // expected-warning {{object whose reference is captured may not live long enough}}
+ v = temp; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
@@ -159,13 +166,14 @@ void definite_potential_together(bool cond) {
{
MyObj s;
- p_definite = &s; // expected-warning {{does not live long enough}}
- if (cond) {
- p_maybe = &s; // expected-warning {{may not live long enough}}
- }
- } // expected-note 2 {{destroyed here}}
- (void)*p_definite; // expected-note {{later used here}}
- (void)*p_maybe; // expected-note {{later used here}}
+ if (cond)
+ p_definite = &s; // expected-warning {{does not live long enough}}
+ if (cond)
+ p_maybe = &s; // expected-warning {{may not live long enough}}
+ } // expected-note 2 {{destroyed here}}
+ (void)*p_definite; // expected-note {{later used here}}
+ if (!cond)
+ (void)*p_maybe; // expected-note {{later used here}}
}
void definite_overrides_potential(bool cond) {
@@ -189,10 +197,19 @@ void definite_overrides_potential(bool cond) {
(void)*q;
}
-
-//===----------------------------------------------------------------------===//
-// Control Flow Tests
-//===----------------------------------------------------------------------===//
+void potential_due_to_conditional_killing(bool cond) {
+ MyObj safe;
+ MyObj* q;
+ {
+ MyObj s;
+ q = &s; // expected-warning {{may not live long enough}}
+ } // expected-note {{destroyed here}}
+ if (cond) {
+ // 'q' is conditionally "rescued". 'p' is not.
+ q = &safe;
+ }
+ (void)*q; // expected-note {{later used here}}
+}
void potential_for_loop_use_after_loop_body(MyObj safe) {
MyObj* p = &safe;
@@ -215,34 +232,35 @@ void potential_for_loop_gsl() {
void potential_for_loop_use_before_loop_body(MyObj safe) {
MyObj* p = &safe;
+ // Prefer the earlier use for diagnsotics.
for (int i = 0; i < 1; ++i) {
(void)*p; // expected-note {{later used here}}
MyObj s;
- p = &s; // expected-warning {{may not live long enough}}
+ p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p;
}
-void potential_loop_with_break(bool cond) {
+void definite_loop_with_break(bool cond) {
MyObj safe;
MyObj* p = &safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj temp;
- p = &temp; // expected-warning {{may not live long enough}}
+ p = &temp; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note {{later used here}}
}
-void potential_loop_with_break_gsl(bool cond) {
+void definite_loop_with_break_gsl(bool cond) {
MyObj safe;
View v = safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj temp;
- v = temp; // expected-warning {{object whose reference is captured may not live long enough}}
+ v = temp; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
@@ -250,37 +268,52 @@ void potential_loop_with_break_gsl(bool cond) {
}
void potential_multiple_expiry_of_same_loan(bool cond) {
- // Choose the last expiry location for the loan.
+ // Choose the last expiry location for the loan (e.g., through scope-ends and break statements).
MyObj safe;
MyObj* p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond) {
- p = &unsafe; // expected-warning {{may not live long enough}}
- break;
+ p = &unsafe; // expected-warning {{does not live long enough}}
+ break; // expected-note {{destroyed here}}
}
- } // expected-note {{destroyed here}}
+ }
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond) {
- p = &unsafe; // expected-warning {{may not live long enough}}
+ p = &unsafe; // expected-warning {{does not live long enough}}
if (cond)
- break;
+ break; // expected-note {{destroyed here}}
}
- } // expected-note {{destroyed here}}
+ }
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj unsafe2;
- p = &unsafe2; // expected-warning {{may not live long enough}}
+ p = &unsafe2; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
+
+ // TODO: This can be argued to be a "maybe" warning. This is because
+ // we only check for confidence of liveness and not the confidence of
+ // the loan contained in an origin. To deal with this, we can introduce
+ // a confidence in loan propagation analysis as well like liveness.
+ (void)*p; // expected-note {{later used here}}
+
+ p = &safe;
+ for (int i = 0; i < 10; ++i) {
+ MyObj unsafe;
+ if (cond)
+ p = &unsafe; // expected-warning {{does not live long enough}}
+ if (cond)
+ break; // expected-note {{destroyed here}}
+ }
(void)*p; // expected-note {{later used here}}
}
@@ -298,13 +331,14 @@ void potential_switch(int mode) {
break;
}
}
- (void)*p; // expected-note {{later used here}}
+ if (mode == 2)
+ (void)*p; // expected-note {{later used here}}
}
void definite_switch(int mode) {
MyObj safe;
MyObj* p = &safe;
- // All cases are UaF --> Definite error.
+ // A use domintates all the loan expires --> all definite error.
switch (mode) {
case 1: {
MyObj temp1;
@@ -347,6 +381,21 @@ void definite_switch_gsl(int mode) {
v.use(); // expected-note 3 {{later used here}}
}
+void loan_from_previous_iteration(MyObj safe, bool condition) {
+ MyObj* p = &safe;
+ MyObj* q = &safe;
+
+ while (condition) {
+ MyObj x;
+ p = &x; // expected-warning {{may not live long enough}}
+
+ if (condition)
+ q = p;
+ (void)*p;
+ (void)*q; // expected-note {{later used here}}
+ } // expected-note {{destroyed here}}
+}
+
//===----------------------------------------------------------------------===//
// No-Error Cases
//===----------------------------------------------------------------------===//
@@ -372,6 +421,19 @@ void no_error_if_dangle_then_rescue_gsl() {
v.use(); // This is safe.
}
+void no_error_loan_from_current_iteration(bool cond) {
+ // See https://github.com/llvm/llvm-project/issues/156959.
+ MyObj b;
+ while (cond) {
+ MyObj a;
+ View p = b;
+ if (cond) {
+ p = a;
+ }
+ (void)p;
+ }
+}
+
//===----------------------------------------------------------------------===//
// Lifetimebound Attribute Tests
@@ -415,9 +477,9 @@ void lifetimebound_multiple_args_potential(bool cond) {
MyObj obj1;
if (cond) {
MyObj obj2;
- v = Choose(true,
- obj1, // expected-warning {{object whose reference is captured may not live long enough}}
- obj2); // expected-warning {{object whose reference is captured may not live long enough}}
+ v = Choose(true,
+ obj1, // expected-warning {{object whose reference is captured does not live long enough}}
+ obj2); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
} // expected-note {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
@@ -488,7 +550,7 @@ void lifetimebound_partial_safety(bool cond) {
MyObj temp_obj;
v = Choose(true,
safe_obj,
- temp_obj); // expected-warning {{object whose reference is captured may not live long enough}}
+ temp_obj); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
diff --git a/clang/test/SemaCXX/GH161671.cpp b/clang/test/SemaCXX/GH161671.cpp
new file mode 100644
index 0000000..de09e54
--- /dev/null
+++ b/clang/test/SemaCXX/GH161671.cpp
@@ -0,0 +1,339 @@
+// RUN: %clang_cc1 -std=c++20 -w %s
+// RUN: %clang_cc1 -std=c++2c -w %s
+// expected-no-diagnostics
+
+namespace std {
+template <typename _Tp, _Tp __v> struct integral_constant {
+ static constexpr _Tp value = __v;
+ using value_type = _Tp;
+};
+template <bool __v> using __bool_constant = integral_constant<bool, __v>;
+template <typename> struct is_integral : integral_constant<bool, true> {};
+template <typename> struct is_signed : integral_constant<bool, false> {};
+template <typename _Tp, typename _Up = _Tp> _Up __declval(int);
+template <typename _Tp> auto declval() -> decltype(__declval<_Tp>(0));
+template <typename> struct make_unsigned {
+ using type = int;
+};
+template <typename _Tp> struct decay {
+ using type = _Tp;
+};
+template <int, typename _Iftrue, typename> struct conditional {
+ using type = _Iftrue;
+};
+} // namespace std
+namespace meta {
+template <template <typename...> class> struct quote;
+template <template <typename> class C, typename... Ts>
+concept valid = requires { typename C<Ts...>; };
+template <typename T>
+concept trait = requires { typename T; };
+template <typename T>
+concept invocable = requires { typename quote<T::template invoke>; };
+template <typename T>
+concept integral = requires { T::value; };
+template <trait T> using _t = T::type;
+template <integral T> constexpr T::value_type _v = T::value;
+template <bool B> using bool_ = std::integral_constant<bool, B>;
+template <invocable Fn, typename... Args>
+using invoke = Fn::template invoke<Args...>;
+template <typename> struct id;
+namespace detail {
+template <template <typename> class, typename...> struct defer_;
+template <template <typename> class C, typename... Ts>
+ requires valid<C, Ts...>
+struct defer_<C, Ts...> {
+ using type = C<Ts...>;
+};
+} // namespace detail
+template <template <typename> class C, typename... Ts>
+struct defer : detail::defer_<C, Ts...> {};
+template <template <typename...> class C> struct quote {
+ template <typename... Ts> using invoke = _t<defer<C, Ts...>>;
+};
+namespace detail {
+template <int> struct _cond {
+ template <typename Then, typename> using invoke = Then;
+};
+template <> struct _cond<false>;
+} // namespace detail
+template <bool If, typename Then, typename Else>
+using conditional_t = detail::_cond<If>::template invoke<Then, Else>;
+namespace detail {
+template <typename...> struct _if_;
+template <typename If, typename Then, typename Else>
+struct _if_<If, Then, Else> : std::conditional<_v<If>, Then, Else> {};
+} // namespace detail
+template <bool If, typename... Args>
+using if_c = _t<detail::_if_<bool_<If>, Args...>>;
+} // namespace meta
+template <bool> void requires_();
+template <typename A, typename B>
+concept same_as = __is_same(B, A);
+namespace ranges {
+template <typename> struct view_closure;
+template <typename T> using decay_t = meta::_t<std::decay<T>>;
+enum cardinality { unknown };
+template <cardinality> struct basic_view {};
+} // namespace ranges
+namespace std {
+template <typename> struct vector {};
+} // namespace std
+namespace ranges {
+struct {
+ template <typename F, typename... Args>
+ auto operator()(F f, Args... args) -> decltype(f(args...));
+} invoke;
+template <typename Fun, typename... Args>
+using invoke_result_t =
+ decltype(invoke(std::declval<Fun>(), std::declval<Args>()...));
+namespace detail {
+struct with_difference_type_;
+template <typename T> using iter_value_t_ = T ::value_type;
+} // namespace detail
+template <typename R> using iter_value_t = detail::iter_value_t_<R>;
+namespace detail {
+template <typename I>
+using iter_size_t =
+ meta::_t<meta::conditional_t<std::is_integral<I>::value,
+ std::make_unsigned<I>, meta::id<I>>>;
+template <typename D>
+concept signed_integer_like_impl_concept_ =
+ std::integral_constant<bool, -D()>::value;
+template <typename D>
+concept signed_integer_like_ = signed_integer_like_impl_concept_<D>;
+} // namespace detail
+template <typename S, typename I>
+concept sized_sentinel_for_requires_ =
+ requires(S s, I i) { requires_<same_as<I, decltype(i - s)>>; };
+template <typename S, typename I>
+concept sized_sentinel_for = sized_sentinel_for_requires_<S, I>;
+struct range_access {
+ template <typename Rng>
+ static auto begin_cursor(Rng rng) -> decltype(rng.begin_cursor());
+ template <typename Cur, typename O>
+ static auto distance_to(Cur pos, O other) -> decltype(pos.distance_to(other));
+};
+namespace detail {
+template <typename S, typename C>
+concept sized_sentinel_for_cursor_requires_ = requires(S s, C c) {
+ requires_<signed_integer_like_<decltype(range_access::distance_to(c, s))>>;
+};
+template <typename S, typename C>
+concept sized_sentinel_for_cursor = sized_sentinel_for_cursor_requires_<S, C>;
+struct iterator_associated_types_base_ {
+ typedef range_access value_type;
+};
+template <typename>
+using iterator_associated_types_base = iterator_associated_types_base_;
+} // namespace detail
+template <typename>
+struct basic_iterator : detail::iterator_associated_types_base<int> {};
+template <typename Cur2, typename Cur>
+ requires detail::sized_sentinel_for_cursor<Cur2, Cur>
+void operator-(basic_iterator<Cur2>, basic_iterator<Cur>);
+namespace _begin_ {
+template <typename T>
+concept has_member_begin_requires_ = requires(T t) { t; };
+template <typename T>
+concept has_member_begin = has_member_begin_requires_<T>;
+struct _member_result_ {
+ template <typename R>
+ using invoke = decltype(static_cast<R (*)()>(nullptr)().begin());
+};
+struct _non_member_result_;
+struct fn {
+ template <typename R>
+ using _result_t =
+ meta::invoke<meta::conditional_t<has_member_begin<R>, _member_result_,
+ _non_member_result_>,
+ R>;
+ template <typename R> _result_t<R> operator()(R);
+};
+} // namespace _begin_
+_begin_::fn begin;
+namespace _end_ {
+template <typename>
+concept has_member_end_requires_ = requires { begin; };
+template <typename T>
+concept has_member_end = has_member_end_requires_<T>;
+struct _member_result_ {
+ template <typename R>
+ using invoke = decltype(static_cast<R (*)()>(nullptr)().end());
+};
+struct _non_member_result_;
+struct fn {
+ template <typename R>
+ using _result_t =
+ meta::invoke<meta::conditional_t<has_member_end<R>, _member_result_,
+ _non_member_result_>,
+ R>;
+ template <typename R> _result_t<R> operator()(R);
+};
+} // namespace _end_
+_end_::fn end;
+template <typename Rng>
+using iterator_t = decltype(begin(static_cast<Rng (*)()>(nullptr)()));
+template <typename Rng>
+using sentinel_t = decltype(end(static_cast<Rng (*)()>(nullptr)()));
+template <typename T>
+concept has_member_size_requires_ = requires(T t) { t.size(); };
+template <typename T>
+concept has_member_size = has_member_size_requires_<T>;
+struct _other_result_;
+struct _member_result_ {
+ template <typename> using invoke = decltype(0);
+ template <typename R>
+ using _result_t = meta::invoke<
+ meta::conditional_t<has_member_size<R>, _member_result_, _other_result_>,
+ R>;
+ template <typename R> _result_t<R> operator()(R r) { r.size(); }
+} size;
+template <typename Rng> using range_value_t = iter_value_t<iterator_t<Rng>>;
+namespace detail {
+template <cardinality Card>
+std::integral_constant<cardinality, Card> test_cardinality(basic_view<Card> *);
+}
+template <typename Rng>
+struct range_cardinality
+ : meta::conditional_t<__is_same(Rng, Rng),
+ decltype(detail::test_cardinality(
+ static_cast<Rng *>(nullptr))),
+ Rng> {};
+template <typename T>
+concept sized_range_requires_ = requires(T t) { size(t); };
+template <typename T>
+concept sized_range = sized_range_requires_<T>;
+namespace detail {
+template <int> struct dependent_ {
+ template <typename T> using invoke = T;
+};
+} // namespace detail
+template <typename Derived, cardinality Cardinality>
+struct view_interface : basic_view<Cardinality> {
+ template <bool B> using D = meta::invoke<detail::dependent_<B>, Derived>;
+ Derived derived();
+ template <bool True = true>
+ requires sized_sentinel_for<sentinel_t<D<True>>, iterator_t<D<True>>>
+ detail::iter_size_t<iterator_t<D<True>>> size() {
+ derived().end() - derived().begin();
+ }
+};
+struct {
+ template <typename Fun> view_closure<Fun> operator()(Fun);
+} make_view_closure;
+struct view_closure_base {
+ template <typename Rng, typename ViewFn>
+ friend auto operator|(Rng rng, ViewFn vw) {
+ return vw(rng);
+ }
+};
+template <typename ViewFn> struct view_closure : view_closure_base, ViewFn {};
+namespace detail {
+template <typename Derived>
+using begin_cursor_t =
+ decay_t<decltype(range_access::begin_cursor(std::declval<Derived>()))>;
+template <typename Derived>
+using facade_iterator_t = basic_iterator<begin_cursor_t<Derived>>;
+template <typename Derived>
+using facade_sentinel_t =
+ meta::if_c<same_as<Derived, Derived>, facade_iterator_t<Derived>, Derived>;
+} // namespace detail
+template <typename Derived, cardinality Cardinality>
+struct view_facade : view_interface<Derived, Cardinality> {
+ template <typename D = Derived> auto begin() -> detail::facade_iterator_t<D>;
+ template <typename D = Derived> auto end() -> detail::facade_sentinel_t<D>;
+};
+template <typename Derived, cardinality Cardinality>
+struct view_adaptor : view_facade<Derived, Cardinality> {
+ auto begin_cursor() -> decltype(0);
+};
+namespace detail {
+template <typename...> struct bind_back_fn_;
+template <typename Fn, typename Arg> struct bind_back_fn_<Fn, Arg> {
+ template <typename... CallArgs>
+ invoke_result_t<Fn, CallArgs..., Arg> operator()(CallArgs...);
+};
+template <typename Fn, typename... Args>
+using bind_back_fn = bind_back_fn_<Fn, Args...>;
+} // namespace detail
+struct {
+ template <typename Fn, typename Arg1>
+ detail::bind_back_fn<Fn, Arg1> operator()(Fn, Arg1);
+} bind_back;
+namespace detail {
+struct to_container {
+ template <typename> struct fn;
+ template <typename, typename> struct closure;
+};
+template <typename, typename, typename R>
+concept to_container_reserve = sized_range<R>;
+template <typename MetaFn, typename Rng>
+using container_t = meta::invoke<MetaFn, Rng>;
+struct to_container_closure_base {
+ template <typename Rng, typename MetaFn, typename Fn>
+ friend auto operator|(Rng rng, to_container::closure<MetaFn, Fn> fn) {
+ return fn(rng);
+ }
+};
+template <typename, typename Fn>
+struct to_container::closure : to_container_closure_base, Fn {};
+template <typename MetaFn> struct to_container::fn {
+ template <typename Rng> void impl(Rng, std::__bool_constant<false>);
+ template <typename Rng> void impl(Rng rng, std::__bool_constant<true>) {
+ size(rng);
+ }
+ template <typename Rng> container_t<MetaFn, Rng> operator()(Rng rng) {
+ using cont_t = container_t<MetaFn, Rng>;
+ using iter_t = Rng;
+ using use_reserve_t =
+ meta::bool_<to_container_reserve<cont_t, iter_t, Rng>>;
+ impl(rng, use_reserve_t{});
+ }
+};
+template <typename MetaFn, typename Fn>
+using to_container_closure = to_container::closure<MetaFn, Fn>;
+template <typename MetaFn>
+using to_container_fn = to_container_closure<MetaFn, to_container::fn<MetaFn>>;
+template <template <typename> class ContT> struct from_range {
+ template <typename Rng>
+ static auto from_rng_(long)
+ -> meta::invoke<meta::quote<ContT>, range_value_t<Rng>>;
+ template <typename Rng> using invoke = decltype(from_rng_<Rng>(0));
+};
+} // namespace detail
+detail::to_container_fn<detail::from_range<std::vector>> to_vector;
+template <typename Rng>
+struct remove_if_view
+ : view_adaptor<remove_if_view<Rng>, range_cardinality<Rng>::value> {};
+struct filter_base_fn {
+ template <typename Rng, typename Pred>
+ remove_if_view<Rng> operator()(Rng, Pred);
+ template <typename Pred> auto operator()(Pred pred) {
+ return make_view_closure(bind_back(filter_base_fn{}, pred));
+ }
+} filter;
+namespace detail {
+struct promote_as_signed_;
+template <typename I>
+using iota_difference_t =
+ meta::conditional_t<std::is_integral<I>::value, promote_as_signed_,
+ with_difference_type_>;
+} // namespace detail
+template <typename, typename>
+struct iota_view : view_facade<iota_view<int, int>, unknown> {
+ struct cursor {
+ auto distance_to(cursor) -> detail::iota_difference_t<int>;
+ };
+ cursor begin_cursor();
+};
+struct {
+ template <typename From, typename To>
+ requires(std::is_signed<From>::value == std::is_signed<To>::value)
+ iota_view<From, To> operator()(From, To);
+} iota;
+} // namespace ranges
+void foo() {
+ ranges::iota(0, 1) | ranges::to_vector =
+ ranges::iota(0, 1) | ranges::filter([] {}) | ranges::to_vector;
+}
diff --git a/clang/test/SemaOpenCL/features.cl b/clang/test/SemaOpenCL/features.cl
index 3f59b4e..dd82689 100644
--- a/clang/test/SemaOpenCL/features.cl
+++ b/clang/test/SemaOpenCL/features.cl
@@ -26,6 +26,12 @@
// RUN: %clang_cc1 -triple spir-unknown-unknown %s -E -dM -o - -x cl -cl-std=clc++1.0 \
// RUN: | FileCheck -match-full-lines %s --check-prefix=NO-FEATURES
+// For OpenCL C 2.0, header-only features can be disabled using macros.
+// RUN: %clang_cc1 -triple spir-unknown-unknown %s -E -dM -o - -x cl -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header \
+// RUN: -D__undef___opencl_c_integer_dot_product_input_4x8bit \
+// RUN: -D__undef___opencl_c_integer_dot_product_input_4x8bit_packed \
+// RUN: | FileCheck %s --check-prefix=NO-HEADERONLY-FEATURES-CL20
+
// For OpenCL C 3.0, header-only features can be disabled using macros.
// RUN: %clang_cc1 -triple spir-unknown-unknown %s -E -dM -o - -x cl -cl-std=CL3.0 -fdeclare-opencl-builtins -finclude-default-header \
// RUN: -D__undef___opencl_c_work_group_collective_functions=1 \
@@ -64,6 +70,9 @@
// NO-FEATURES-NOT: #define __opencl_c_read_write_images
// NO-FEATURES-NOT: #define __opencl_c_subgroups
+// NO-HEADERONLY-FEATURES-CL20-NOT: #define __opencl_c_integer_dot_product_input_4x8bit
+// NO-HEADERONLY-FEATURES-CL20-NOT: #define __opencl_c_integer_dot_product_input_4x8bit_packed
+
// NO-HEADERONLY-FEATURES-NOT: #define __opencl_c_work_group_collective_functions
// NO-HEADERONLY-FEATURES-NOT: #define __opencl_c_atomic_order_seq_cst
// NO-HEADERONLY-FEATURES-NOT: #define __opencl_c_atomic_scope_device
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index e5e081f..3b7c138 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -std=c++20 -ferror-limit 0 -verify %s
+// RUN: %clang_cc1 -std=c++20 -ferror-limit 0 -verify=expected,cxx20 %s
+// RUN: %clang_cc1 -std=c++2c -ferror-limit 0 -verify=expected %s
namespace PR47043 {
template<typename T> concept True = true;
@@ -1405,3 +1406,41 @@ static_assert(!std::is_constructible_v<span<4>, array<int, 3>>);
}
}
+
+
+namespace GH162125 {
+template<typename, int size>
+concept true_int = (size, true);
+
+template<typename, typename... Ts>
+concept true_types = true_int<void, sizeof...(Ts)>;
+
+template<typename, typename... Ts>
+concept true_types2 = true_int<void, Ts...[0]{1}>; // cxx20-warning {{pack indexing is a C++2c extension}}
+
+template<typename... Ts>
+struct s {
+ template<typename T> requires true_types<T, Ts...> && true_types2<T, Ts...>
+ static void f(T);
+};
+void(*test)(int) = &s<bool>::f<int>;
+}
+
+namespace GH162125_reversed {
+template<int size, typename>
+concept true_int = (size, true);
+
+template<typename, typename... Ts>
+concept true_types = true_int<sizeof...(Ts), void>;
+
+template<typename, typename... Ts>
+concept true_types2 = true_int<Ts...[0]{1}, void>; // cxx20-warning {{pack indexing is a C++2c extension}}
+
+template<typename... Ts>
+struct s {
+ template<typename T> requires true_types<T, Ts...> && true_types2<T, Ts...>
+ static void f(T);
+};
+
+void(*test)(int) = &s<bool>::f<int>;
+}
diff --git a/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp b/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp
index 85208fc..8a7d36d 100644
--- a/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp
+++ b/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp
@@ -6,7 +6,7 @@
// RUN: cd %t_tmp
// RUN: %clangxx -fopenmp -fopenmp-targets=spirv64-intel -nogpulib -c -o %t_clang-linker-wrapper-spirv-elf.o %s
// RUN: not clang-linker-wrapper -o a.out %t_clang-linker-wrapper-spirv-elf.o --save-temps --linker-path=ld
-// RUN: clang-offload-packager --image=triple=spirv64-intel,kind=openmp,file=%t.elf %t_tmp/a.out.openmp.image.wrapper.o
+// RUN: llvm-offload-binary --image=triple=spirv64-intel,kind=openmp,file=%t.elf %t_tmp/a.out.openmp.image.wrapper.o
// RUN: llvm-readelf -h %t.elf | FileCheck -check-prefix=CHECK-MACHINE %s
// RUN: llvm-readelf -t %t.elf | FileCheck -check-prefix=CHECK-SECTION %s
// RUN: llvm-readelf -n %t.elf | FileCheck -check-prefix=CHECK-NOTES %s
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index e6c79d7..29088ef 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -92,7 +92,7 @@ tools = [
"clang-diff",
"clang-format",
"clang-repl",
- "clang-offload-packager",
+ "llvm-offload-binary",
"clang-tblgen",
"clang-scan-deps",
"clang-installapi",
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index 50e3d69..7a7c56ae 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -14,7 +14,6 @@ add_clang_subdirectory(clang-fuzzer)
add_clang_subdirectory(clang-import-test)
add_clang_subdirectory(clang-linker-wrapper)
add_clang_subdirectory(clang-nvlink-wrapper)
-add_clang_subdirectory(clang-offload-packager)
add_clang_subdirectory(clang-offload-bundler)
add_clang_subdirectory(clang-scan-deps)
add_clang_subdirectory(clang-sycl-linker)
diff --git a/clang/tools/clang-offload-packager/CMakeLists.txt b/clang/tools/clang-offload-packager/CMakeLists.txt
deleted file mode 100644
index 1c29e37..0000000
--- a/clang/tools/clang-offload-packager/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-set(LLVM_LINK_COMPONENTS
- ${LLVM_TARGETS_TO_BUILD}
- BinaryFormat
- Object
- Support)
-
-add_clang_tool(clang-offload-packager
- ClangOffloadPackager.cpp
-
- DEPENDS
- ${tablegen_deps}
- )
-
-clang_target_link_libraries(clang-offload-packager
- PRIVATE
- clangBasic
- )
diff --git a/clang/tools/clang-offload-packager/ClangOffloadPackager.cpp b/clang/tools/clang-offload-packager/ClangOffloadPackager.cpp
deleted file mode 100644
index 64b058e..0000000
--- a/clang/tools/clang-offload-packager/ClangOffloadPackager.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-//===-- clang-offload-packager/ClangOffloadPackager.cpp - file bundler ---===//
-//
-// 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 tool takes several device object files and bundles them into a single
-// binary image using a custom binary format. This is intended to be used to
-// embed many device files into an application to create a fat binary.
-//
-//===---------------------------------------------------------------------===//
-
-#include "clang/Basic/Version.h"
-
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/BinaryFormat/Magic.h"
-#include "llvm/Object/ArchiveWriter.h"
-#include "llvm/Object/OffloadBinary.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/Signals.h"
-#include "llvm/Support/StringSaver.h"
-#include "llvm/Support/WithColor.h"
-
-using namespace llvm;
-using namespace llvm::object;
-
-static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
-
-static cl::OptionCategory
- ClangOffloadPackagerCategory("clang-offload-packager options");
-
-static cl::opt<std::string> OutputFile("o", cl::desc("Write output to <file>."),
- cl::value_desc("file"),
- cl::cat(ClangOffloadPackagerCategory));
-
-static cl::opt<std::string> InputFile(cl::Positional,
- cl::desc("Extract from <file>."),
- cl::value_desc("file"),
- cl::cat(ClangOffloadPackagerCategory));
-
-static cl::list<std::string>
- DeviceImages("image",
- cl::desc("List of key and value arguments. Required keywords "
- "are 'file' and 'triple'."),
- cl::value_desc("<key>=<value>,..."),
- cl::cat(ClangOffloadPackagerCategory));
-
-static cl::opt<bool>
- CreateArchive("archive",
- cl::desc("Write extracted files to a static archive"),
- cl::cat(ClangOffloadPackagerCategory));
-
-/// Path of the current binary.
-static const char *PackagerExecutable;
-
-static void PrintVersion(raw_ostream &OS) {
- OS << clang::getClangToolFullVersion("clang-offload-packager") << '\n';
-}
-
-// Get a map containing all the arguments for the image. Repeated arguments will
-// be placed in a comma separated list.
-static DenseMap<StringRef, StringRef> getImageArguments(StringRef Image,
- StringSaver &Saver) {
- DenseMap<StringRef, StringRef> Args;
- for (StringRef Arg : llvm::split(Image, ",")) {
- auto [Key, Value] = Arg.split("=");
- auto [It, Inserted] = Args.try_emplace(Key, Value);
- if (!Inserted)
- It->second = Saver.save(It->second + "," + Value);
- }
-
- return Args;
-}
-
-static Error writeFile(StringRef Filename, StringRef Data) {
- Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
- FileOutputBuffer::create(Filename, Data.size());
- if (!OutputOrErr)
- return OutputOrErr.takeError();
- std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
- llvm::copy(Data, Output->getBufferStart());
- if (Error E = Output->commit())
- return E;
- return Error::success();
-}
-
-static Error bundleImages() {
- SmallVector<char, 1024> BinaryData;
- raw_svector_ostream OS(BinaryData);
- for (StringRef Image : DeviceImages) {
- BumpPtrAllocator Alloc;
- StringSaver Saver(Alloc);
- DenseMap<StringRef, StringRef> Args = getImageArguments(Image, Saver);
-
- if (!Args.count("triple") || !Args.count("file"))
- return createStringError(
- inconvertibleErrorCode(),
- "'file' and 'triple' are required image arguments");
-
- // Permit using multiple instances of `file` in a single string.
- for (auto &File : llvm::split(Args["file"], ",")) {
- OffloadBinary::OffloadingImage ImageBinary{};
-
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ObjectOrErr =
- llvm::MemoryBuffer::getFileOrSTDIN(File);
- if (std::error_code EC = ObjectOrErr.getError())
- return errorCodeToError(EC);
-
- // Clang uses the '.o' suffix for LTO bitcode.
- if (identify_magic((*ObjectOrErr)->getBuffer()) == file_magic::bitcode)
- ImageBinary.TheImageKind = object::IMG_Bitcode;
- else
- ImageBinary.TheImageKind =
- getImageKind(sys::path::extension(File).drop_front());
- ImageBinary.Image = std::move(*ObjectOrErr);
- for (const auto &[Key, Value] : Args) {
- if (Key == "kind") {
- ImageBinary.TheOffloadKind = getOffloadKind(Value);
- } else if (Key != "file") {
- ImageBinary.StringData[Key] = Value;
- }
- }
- llvm::SmallString<0> Buffer = OffloadBinary::write(ImageBinary);
- if (Buffer.size() % OffloadBinary::getAlignment() != 0)
- return createStringError(inconvertibleErrorCode(),
- "Offload binary has invalid size alignment");
- OS << Buffer;
- }
- }
-
- if (Error E = writeFile(OutputFile,
- StringRef(BinaryData.begin(), BinaryData.size())))
- return E;
- return Error::success();
-}
-
-static Error unbundleImages() {
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
- MemoryBuffer::getFileOrSTDIN(InputFile);
- if (std::error_code EC = BufferOrErr.getError())
- return createFileError(InputFile, EC);
- std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrErr);
-
- // This data can be misaligned if extracted from an archive.
- if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
- Buffer->getBufferStart()))
- Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
- Buffer->getBufferIdentifier());
-
- SmallVector<OffloadFile> Binaries;
- if (Error Err = extractOffloadBinaries(*Buffer, Binaries))
- return Err;
-
- // Try to extract each device image specified by the user from the input file.
- for (StringRef Image : DeviceImages) {
- BumpPtrAllocator Alloc;
- StringSaver Saver(Alloc);
- auto Args = getImageArguments(Image, Saver);
-
- SmallVector<const OffloadBinary *> Extracted;
- for (const OffloadFile &File : Binaries) {
- const auto *Binary = File.getBinary();
- // We handle the 'file' and 'kind' identifiers differently.
- bool Match = llvm::all_of(Args, [&](auto &Arg) {
- const auto [Key, Value] = Arg;
- if (Key == "file")
- return true;
- if (Key == "kind")
- return Binary->getOffloadKind() == getOffloadKind(Value);
- return Binary->getString(Key) == Value;
- });
- if (Match)
- Extracted.push_back(Binary);
- }
-
- if (Extracted.empty())
- continue;
-
- if (CreateArchive) {
- if (!Args.count("file"))
- return createStringError(inconvertibleErrorCode(),
- "Image must have a 'file' argument.");
-
- SmallVector<NewArchiveMember> Members;
- for (const OffloadBinary *Binary : Extracted)
- Members.emplace_back(MemoryBufferRef(
- Binary->getImage(),
- Binary->getMemoryBufferRef().getBufferIdentifier()));
-
- if (Error E = writeArchive(
- Args["file"], Members, SymtabWritingMode::NormalSymtab,
- Archive::getDefaultKind(), true, false, nullptr))
- return E;
- } else if (auto It = Args.find("file"); It != Args.end()) {
- if (Extracted.size() > 1)
- WithColor::warning(errs(), PackagerExecutable)
- << "Multiple inputs match to a single file, '" << It->second
- << "'\n";
- if (Error E = writeFile(It->second, Extracted.back()->getImage()))
- return E;
- } else {
- uint64_t Idx = 0;
- for (const OffloadBinary *Binary : Extracted) {
- StringRef Filename =
- Saver.save(sys::path::stem(InputFile) + "-" + Binary->getTriple() +
- "-" + Binary->getArch() + "." + std::to_string(Idx++) +
- "." + getImageKindName(Binary->getImageKind()));
- if (Error E = writeFile(Filename, Binary->getImage()))
- return E;
- }
- }
- }
-
- return Error::success();
-}
-
-int main(int argc, const char **argv) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- cl::HideUnrelatedOptions(ClangOffloadPackagerCategory);
- cl::SetVersionPrinter(PrintVersion);
- cl::ParseCommandLineOptions(
- argc, argv,
- "A utility for bundling several object files into a single binary.\n"
- "The output binary can then be embedded into the host section table\n"
- "to create a fatbinary containing offloading code.\n");
-
- if (Help) {
- cl::PrintHelpMessage();
- return EXIT_SUCCESS;
- }
-
- PackagerExecutable = argv[0];
- auto reportError = [argv](Error E) {
- logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
- return EXIT_FAILURE;
- };
-
- if (!InputFile.empty() && !OutputFile.empty())
- return reportError(
- createStringError(inconvertibleErrorCode(),
- "Packaging to an output file and extracting from an "
- "input file are mutually exclusive."));
-
- if (!OutputFile.empty()) {
- if (Error Err = bundleImages())
- return reportError(std::move(Err));
- } else if (!InputFile.empty()) {
- if (Error Err = unbundleImages())
- return reportError(std::move(Err));
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/clang/unittests/AST/DeclPrinterTest.cpp b/clang/unittests/AST/DeclPrinterTest.cpp
index 28750c4..a412a98 100644
--- a/clang/unittests/AST/DeclPrinterTest.cpp
+++ b/clang/unittests/AST/DeclPrinterTest.cpp
@@ -1022,91 +1022,91 @@ TEST(DeclPrinter, TestFieldDecl2) {
}
TEST(DeclPrinter, TestClassTemplateDecl1) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<typename T>"
- "struct A { T a; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <typename T> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("template<typename T>"
+ "struct A { T a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl2) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<typename T = int>"
- "struct A { T a; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <typename T = int> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("template<typename T = int>"
+ "struct A { T a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T = int> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl3) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<class T>"
- "struct A { T a; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <class T> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("template<class T>"
+ "struct A { T a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <class T> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl4) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<typename T, typename U>"
- "struct A { T a; U b; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <typename T, typename U> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("template<typename T, typename U>"
+ "struct A { T a; U b; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename T, typename U> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl5) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<int N>"
- "struct A { int a[N]; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <int N> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("template<int N>"
+ "struct A { int a[N]; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <int N> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl6) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<int N = 42>"
- "struct A { int a[N]; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <int N = 42> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("template<int N = 42>"
+ "struct A { int a[N]; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <int N = 42> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl7) {
- ASSERT_TRUE(PrintedDeclCXX98Matches(
- "typedef int MyInt;"
- "template<MyInt N>"
- "struct A { int a[N]; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <MyInt N> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX98Matches("typedef int MyInt;"
+ "template<MyInt N>"
+ "struct A { int a[N]; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <MyInt N> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl8) {
ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<template<typename U> class T> struct A { };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <template <typename U> class T> struct A {}"));
+ "template<template<typename U> class T> struct A { };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <template <typename U> class T> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl9) {
ASSERT_TRUE(PrintedDeclCXX98Matches(
- "template<typename T> struct Z { };"
- "template<template<typename U> class T = Z> struct A { };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <template <typename U> class T> struct A {}"));
+ "template<typename T> struct Z { };"
+ "template<template<typename U> class T = Z> struct A { };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <template <typename U> class T = Z> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl10) {
- ASSERT_TRUE(PrintedDeclCXX11Matches(
- "template<typename... T>"
- "struct A { int a; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <typename ...T> struct A {}"));
+ ASSERT_TRUE(
+ PrintedDeclCXX11Matches("template<typename... T>"
+ "struct A { int a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename ...T> struct A {}"));
}
TEST(DeclPrinter, TestClassTemplateDecl11) {
ASSERT_TRUE(PrintedDeclCXX11Matches(
- "template<typename... T>"
- "struct A : public T... { int a; };",
- classTemplateDecl(hasName("A")).bind("id"),
- "template <typename ...T> struct A : public T... {}"));
+ "template<typename... T>"
+ "struct A : public T... { int a; };",
+ classTemplateDecl(hasName("A")).bind("id"),
+ "template <typename ...T> struct A : public T... {}"));
}
TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl1) {
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 3821015..169b2d2 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -126,12 +126,12 @@ public:
return Analysis.getLoansAtPoint(OID, PP);
}
- std::optional<std::vector<LoanID>>
- getExpiredLoansAtPoint(llvm::StringRef Annotation) {
+ std::optional<std::vector<std::pair<OriginID, LivenessKind>>>
+ getLiveOriginsAtPoint(llvm::StringRef Annotation) {
ProgramPoint PP = Runner.getProgramPoint(Annotation);
if (!PP)
return std::nullopt;
- return Analysis.getExpiredLoansAtPoint(PP);
+ return Analysis.getLiveOriginsAtPoint(PP);
}
private:
@@ -180,6 +180,15 @@ public:
LifetimeTestHelper &Helper;
};
+// A helper class to represent a set of origins, identified by variable names.
+class OriginsInfo {
+public:
+ OriginsInfo(const std::vector<std::string> &Vars, LifetimeTestHelper &H)
+ : OriginVars(Vars), Helper(H) {}
+ std::vector<std::string> OriginVars;
+ LifetimeTestHelper &Helper;
+};
+
/// Matcher to verify the set of loans held by an origin at a specific
/// program point.
///
@@ -221,14 +230,15 @@ MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {
std::sort(ExpectedLoans.begin(), ExpectedLoans.end());
std::sort(ActualLoans.begin(), ActualLoans.end());
if (ExpectedLoans != ActualLoans) {
- *result_listener << "Expected: ";
+ *result_listener << "Expected: {";
for (const auto &LoanID : ExpectedLoans) {
*result_listener << LoanID.Value << ", ";
}
- *result_listener << "Actual: ";
+ *result_listener << "} Actual: {";
for (const auto &LoanID : ActualLoans) {
*result_listener << LoanID.Value << ", ";
}
+ *result_listener << "}";
return false;
}
@@ -236,32 +246,71 @@ MATCHER_P2(HasLoansToImpl, LoanVars, Annotation, "") {
ActualLoans, result_listener);
}
-/// Matcher to verify that the complete set of expired loans at a program point
-/// matches the expected loan set.
-MATCHER_P(AreExpiredAt, Annotation, "") {
- const LoanSetInfo &Info = arg;
- auto &Helper = Info.Helper;
+enum class LivenessKindFilter { Maybe, Must, All };
- auto ActualExpiredSetOpt = Helper.getExpiredLoansAtPoint(Annotation);
- if (!ActualExpiredSetOpt) {
- *result_listener << "could not get a valid expired loan set at point '"
+/// Matcher to verify the complete set of live origins at a program point.
+MATCHER_P2(AreLiveAtImpl, Annotation, ConfFilter, "") {
+ const OriginsInfo &Info = arg;
+ auto &Helper = Info.Helper;
+ auto ActualLiveSetOpt = Helper.getLiveOriginsAtPoint(Annotation);
+ if (!ActualLiveSetOpt) {
+ *result_listener << "could not get a valid live origin set at point '"
<< Annotation << "'";
return false;
}
- std::vector<LoanID> ActualExpiredLoans = *ActualExpiredSetOpt;
- std::vector<LoanID> ExpectedExpiredLoans;
- for (const auto &VarName : Info.LoanVars) {
- auto LoanIDs = Helper.getLoansForVar(VarName);
- if (LoanIDs.empty()) {
- *result_listener << "could not find a loan for variable '" << VarName
+ std::vector<OriginID> ActualLiveOrigins;
+ for (const auto [OID, ActualConfidence] : ActualLiveSetOpt.value()) {
+ if (ConfFilter == LivenessKindFilter::All)
+ ActualLiveOrigins.push_back(OID);
+ if (ActualConfidence == LivenessKind::Maybe &&
+ ConfFilter == LivenessKindFilter::Maybe)
+ ActualLiveOrigins.push_back(OID);
+ if (ActualConfidence == LivenessKind::Must &&
+ ConfFilter == LivenessKindFilter::Must)
+ ActualLiveOrigins.push_back(OID);
+ }
+
+ std::vector<OriginID> ExpectedLiveOrigins;
+ for (const auto &VarName : Info.OriginVars) {
+ auto OriginIDOpt = Helper.getOriginForDecl(VarName);
+ if (!OriginIDOpt) {
+ *result_listener << "could not find an origin for variable '" << VarName
<< "'";
return false;
}
- ExpectedExpiredLoans.insert(ExpectedExpiredLoans.end(), LoanIDs.begin(),
- LoanIDs.end());
+ ExpectedLiveOrigins.push_back(*OriginIDOpt);
}
- return ExplainMatchResult(UnorderedElementsAreArray(ExpectedExpiredLoans),
- ActualExpiredLoans, result_listener);
+ std::sort(ExpectedLiveOrigins.begin(), ExpectedLiveOrigins.end());
+ std::sort(ActualLiveOrigins.begin(), ActualLiveOrigins.end());
+ if (ExpectedLiveOrigins != ActualLiveOrigins) {
+ *result_listener << "Expected: {";
+ for (const auto &OriginID : ExpectedLiveOrigins) {
+ *result_listener << OriginID.Value << ", ";
+ }
+ *result_listener << "} Actual: {";
+ for (const auto &OriginID : ActualLiveOrigins) {
+ *result_listener << OriginID.Value << ", ";
+ }
+ *result_listener << "}";
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P(MustBeLiveAt, Annotation, "") {
+ return ExplainMatchResult(AreLiveAtImpl(Annotation, LivenessKindFilter::Must),
+ arg, result_listener);
+}
+
+MATCHER_P(MaybeLiveAt, Annotation, "") {
+ return ExplainMatchResult(
+ AreLiveAtImpl(Annotation, LivenessKindFilter::Maybe), arg,
+ result_listener);
+}
+
+MATCHER_P(AreLiveAt, Annotation, "") {
+ return ExplainMatchResult(AreLiveAtImpl(Annotation, LivenessKindFilter::All),
+ arg, result_listener);
}
// Base test fixture to manage the runner and helper.
@@ -277,6 +326,13 @@ protected:
}
/// Factory function that hides the std::vector creation.
+ OriginsInfo Origins(std::initializer_list<std::string> OriginVars) {
+ return OriginsInfo({OriginVars}, *Helper);
+ }
+
+ OriginsInfo NoOrigins() { return Origins({}); }
+
+ /// Factory function that hides the std::vector creation.
LoanSetInfo LoansTo(std::initializer_list<std::string> LoanVars) {
return LoanSetInfo({LoanVars}, *Helper);
}
@@ -428,29 +484,6 @@ TEST_F(LifetimeAnalysisTest, AssignInSwitch) {
EXPECT_THAT(Origin("p"), HasLoansTo({"s1", "s2", "s3"}, "after_switch"));
}
-TEST_F(LifetimeAnalysisTest, LoanInLoop) {
- SetupTest(R"(
- void target(bool condition) {
- MyObj* p = nullptr;
- while (condition) {
- POINT(start_loop);
- MyObj inner;
- p = &inner;
- POINT(end_loop);
- }
- POINT(after_loop);
- }
- )");
- EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "start_loop"));
- EXPECT_THAT(LoansTo({"inner"}), AreExpiredAt("start_loop"));
-
- EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "end_loop"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("end_loop"));
-
- EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_loop"));
- EXPECT_THAT(LoansTo({"inner"}), AreExpiredAt("after_loop"));
-}
-
TEST_F(LifetimeAnalysisTest, LoopWithBreak) {
SetupTest(R"(
void target(int count) {
@@ -528,20 +561,16 @@ TEST_F(LifetimeAnalysisTest, PointersAndExpirationInACycle) {
)");
EXPECT_THAT(Origin("p1"), HasLoansTo({"v1"}, "before_while"));
EXPECT_THAT(Origin("p2"), HasLoansTo({"v2"}, "before_while"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_while"));
EXPECT_THAT(Origin("p1"),
HasLoansTo({"v1", "v2", "temp"}, "in_loop_before_temp"));
EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "in_loop_before_temp"));
- EXPECT_THAT(LoansTo({"temp"}), AreExpiredAt("in_loop_before_temp"));
EXPECT_THAT(Origin("p1"), HasLoansTo({"temp"}, "in_loop_after_temp"));
EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "in_loop_after_temp"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("in_loop_after_temp"));
EXPECT_THAT(Origin("p1"), HasLoansTo({"v1", "v2", "temp"}, "after_loop"));
EXPECT_THAT(Origin("p2"), HasLoansTo({"v2", "temp"}, "after_loop"));
- EXPECT_THAT(LoansTo({"temp"}), AreExpiredAt("after_loop"));
}
TEST_F(LifetimeAnalysisTest, InfiniteLoopPrunesEdges) {
@@ -585,178 +614,6 @@ TEST_F(LifetimeAnalysisTest, NestedScopes) {
EXPECT_THAT(Origin("p"), HasLoansTo({"inner"}, "after_inner_scope"));
}
-TEST_F(LifetimeAnalysisTest, SimpleExpiry) {
- SetupTest(R"(
- void target() {
- MyObj* p = nullptr;
- {
- MyObj s;
- p = &s;
- POINT(before_expiry);
- } // s goes out of scope here
- POINT(after_expiry);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_expiry"));
- EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_expiry"));
-}
-
-TEST_F(LifetimeAnalysisTest, NestedExpiry) {
- SetupTest(R"(
- void target() {
- MyObj s1;
- MyObj* p = &s1;
- POINT(before_inner);
- {
- MyObj s2;
- p = &s2;
- POINT(in_inner);
- } // s2 expires
- POINT(after_inner);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_inner"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("in_inner"));
- EXPECT_THAT(LoansTo({"s2"}), AreExpiredAt("after_inner"));
-}
-
-TEST_F(LifetimeAnalysisTest, ConditionalExpiry) {
- SetupTest(R"(
- void target(bool cond) {
- MyObj s1;
- MyObj* p = &s1;
- POINT(before_if);
- if (cond) {
- MyObj s2;
- p = &s2;
- POINT(then_block);
- } // s2 expires here
- POINT(after_if);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_if"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("then_block"));
- EXPECT_THAT(LoansTo({"s2"}), AreExpiredAt("after_if"));
-}
-
-TEST_F(LifetimeAnalysisTest, LoopExpiry) {
- SetupTest(R"(
- void target() {
- MyObj *p = nullptr;
- for (int i = 0; i < 2; ++i) {
- POINT(start_loop);
- MyObj s;
- p = &s;
- POINT(end_loop);
- } // s expires here on each iteration
- POINT(after_loop);
- }
- )");
- EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("start_loop"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("end_loop"));
- EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, MultipleExpiredLoans) {
- SetupTest(R"(
- void target() {
- MyObj *p1, *p2, *p3;
- {
- MyObj s1;
- p1 = &s1;
- POINT(p1);
- } // s1 expires
- POINT(p2);
- {
- MyObj s2;
- p2 = &s2;
- MyObj s3;
- p3 = &s3;
- POINT(p3);
- } // s2, s3 expire
- POINT(p4);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("p1"));
- EXPECT_THAT(LoansTo({"s1"}), AreExpiredAt("p2"));
- EXPECT_THAT(LoansTo({"s1"}), AreExpiredAt("p3"));
- EXPECT_THAT(LoansTo({"s1", "s2", "s3"}), AreExpiredAt("p4"));
-}
-
-TEST_F(LifetimeAnalysisTest, GotoJumpsOutOfScope) {
- SetupTest(R"(
- void target(bool cond) {
- MyObj *p = nullptr;
- {
- MyObj s;
- p = &s;
- POINT(before_goto);
- if (cond) {
- goto end;
- }
- } // `s` expires here on the path that doesn't jump
- POINT(after_scope);
- end:
- POINT(after_goto);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_goto"));
- EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_scope"));
- EXPECT_THAT(LoansTo({"s"}), AreExpiredAt("after_goto"));
-}
-
-TEST_F(LifetimeAnalysisTest, ContinueInLoop) {
- SetupTest(R"(
- void target(int count) {
- MyObj *p = nullptr;
- MyObj outer;
- p = &outer;
- POINT(before_loop);
-
- for (int i = 0; i < count; ++i) {
- if (i % 2 == 0) {
- MyObj s_even;
- p = &s_even;
- POINT(in_even_iter);
- continue;
- }
- MyObj s_odd;
- p = &s_odd;
- POINT(in_odd_iter);
- }
- POINT(after_loop);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_loop"));
- EXPECT_THAT(LoansTo({"s_odd"}), AreExpiredAt("in_even_iter"));
- EXPECT_THAT(LoansTo({"s_even"}), AreExpiredAt("in_odd_iter"));
- EXPECT_THAT(LoansTo({"s_even", "s_odd"}), AreExpiredAt("after_loop"));
-}
-
-TEST_F(LifetimeAnalysisTest, ReassignedPointerThenOriginalExpires) {
- SetupTest(R"(
- void target() {
- MyObj* p = nullptr;
- {
- MyObj s1;
- p = &s1;
- POINT(p_has_s1);
- {
- MyObj s2;
- p = &s2;
- POINT(p_has_s2);
- }
- POINT(p_after_s2_expires);
- } // s1 expires here.
- POINT(p_after_s1_expires);
- }
- )");
- EXPECT_THAT(NoLoans(), AreExpiredAt("p_has_s1"));
- EXPECT_THAT(NoLoans(), AreExpiredAt("p_has_s2"));
- EXPECT_THAT(LoansTo({"s2"}), AreExpiredAt("p_after_s2_expires"));
- EXPECT_THAT(LoansTo({"s1", "s2"}), AreExpiredAt("p_after_s1_expires"));
-}
-
TEST_F(LifetimeAnalysisTest, NoDuplicateLoansForImplicitCastToConst) {
SetupTest(R"(
void target() {
@@ -880,23 +737,6 @@ TEST_F(LifetimeAnalysisTest, GslPointerPropagation) {
EXPECT_THAT(Origin("z"), HasLoansTo({"a"}, "p3"));
}
-TEST_F(LifetimeAnalysisTest, GslPointerLoanExpiration) {
- SetupTest(R"(
- void target() {
- View x;
- {
- MyObj a;
- x = a;
- POINT(before_expiry);
- } // `a` is destroyed here.
- POINT(after_expiry);
- }
- )");
-
- EXPECT_THAT(NoLoans(), AreExpiredAt("before_expiry"));
- EXPECT_THAT(LoansTo({"a"}), AreExpiredAt("after_expiry"));
-}
-
TEST_F(LifetimeAnalysisTest, GslPointerReassignment) {
SetupTest(R"(
void target() {
@@ -916,7 +756,6 @@ TEST_F(LifetimeAnalysisTest, GslPointerReassignment) {
EXPECT_THAT(Origin("v"), HasLoansTo({"safe"}, "p1"));
EXPECT_THAT(Origin("v"), HasLoansTo({"unsafe"}, "p2"));
EXPECT_THAT(Origin("v"), HasLoansTo({"unsafe"}, "p3"));
- EXPECT_THAT(LoansTo({"unsafe"}), AreExpiredAt("p3"));
}
TEST_F(LifetimeAnalysisTest, GslPointerConversionOperator) {
@@ -1174,5 +1013,187 @@ TEST_F(LifetimeAnalysisTest, LifetimeboundConversionOperator) {
)");
EXPECT_THAT(Origin("v"), HasLoansTo({"owner"}, "p1"));
}
+
+TEST_F(LifetimeAnalysisTest, LivenessDeadPointer) {
+ SetupTest(R"(
+ void target() {
+ POINT(p1);
+ MyObj s;
+ MyObj* p = &s;
+ POINT(p2);
+ }
+ )");
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessSimpleReturn) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj s;
+ MyObj* p = &s;
+ POINT(p1);
+ return p;
+ }
+ )");
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessKilledByReassignment) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj s1, s2;
+ MyObj* p = &s1;
+ POINT(p1);
+ p = &s2;
+ POINT(p2);
+ return p;
+ }
+ )");
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessAcrossBranches) {
+ SetupTest(R"(
+ MyObj* target(bool c) {
+ MyObj x, y;
+ MyObj* p = nullptr;
+ POINT(p1);
+ if (c) {
+ p = &x;
+ POINT(p2);
+ } else {
+ p = &y;
+ POINT(p3);
+ }
+ return p;
+ }
+ )");
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p3"));
+ // Before the `if`, the value of `p` (`nullptr`) is always overwritten before.
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoop) {
+ SetupTest(R"(
+ MyObj* target(bool c) {
+ MyObj s1, s2;
+ MyObj* p = &s1;
+ MyObj* q = &s2;
+ POINT(p1);
+ while(c) {
+ POINT(p2);
+
+ p = q;
+ POINT(p3);
+ }
+ POINT(p4);
+ return p;
+ }
+ )");
+
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p4"));
+ EXPECT_THAT(NoOrigins(), MaybeLiveAt("p4"));
+
+ EXPECT_THAT(Origins({"p", "q"}), MaybeLiveAt("p3"));
+
+ EXPECT_THAT(Origins({"q"}), MustBeLiveAt("p2"));
+ EXPECT_THAT(NoOrigins(), MaybeLiveAt("p2"));
+
+ EXPECT_THAT(Origins({"p", "q"}), MaybeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoopAndIf) {
+ // See https://github.com/llvm/llvm-project/issues/156959.
+ SetupTest(R"(
+ void target(bool cond) {
+ MyObj b;
+ while (cond) {
+ POINT(p1);
+
+ MyObj a;
+ View p = b;
+
+ POINT(p2);
+
+ if (cond) {
+ POINT(p3);
+ p = a;
+ }
+ POINT(p4);
+ (void)p;
+ POINT(p5);
+ }
+ }
+ )");
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p5"));
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p4"));
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p3"));
+ EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p2"));
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoopAndIf2) {
+ SetupTest(R"(
+ void target(MyObj safe, bool condition) {
+ MyObj* p = &safe;
+ MyObj* q = &safe;
+ POINT(p1);
+
+ while (condition) {
+ POINT(p2);
+ MyObj x;
+ p = &x;
+
+ POINT(p3);
+
+ if (condition) {
+ q = p;
+ POINT(p4);
+ }
+
+ POINT(p5);
+ (void)*p;
+ (void)*q;
+ POINT(p6);
+ }
+ }
+ )");
+ EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p6"));
+ EXPECT_THAT(NoOrigins(), MustBeLiveAt("p6"));
+
+ EXPECT_THAT(Origins({"p", "q"}), MustBeLiveAt("p5"));
+
+ EXPECT_THAT(Origins({"p", "q"}), MustBeLiveAt("p4"));
+
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p3"));
+ EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p3"));
+
+ EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p2"));
+ EXPECT_THAT(NoOrigins(), MustBeLiveAt("p2"));
+
+ EXPECT_THAT(Origins({"q"}), MaybeLiveAt("p1"));
+ EXPECT_THAT(NoOrigins(), MustBeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessOutsideLoop) {
+ SetupTest(R"(
+ void target(MyObj safe) {
+ MyObj* p = &safe;
+ for (int i = 0; i < 1; ++i) {
+ MyObj s;
+ p = &s;
+ POINT(p1);
+ }
+ POINT(p2);
+ (void)*p;
+ }
+ )");
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));
+ EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p1"));
+}
+
} // anonymous namespace
} // namespace clang::lifetimes::internal
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index 4b3af00..de09086 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -314,6 +314,42 @@ TEST_F(SuppressionMappingTest, LongestMatchWins) {
locForFile("clang/lib/Sema/foo.h")));
}
+TEST_F(SuppressionMappingTest, LongShortMatch) {
+ llvm::StringLiteral SuppressionMappingFile = R"(
+ [unused]
+ src:*test/*
+ src:*lld/*=emit)";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+ FS->addFile("foo.txt", /*ModificationTime=*/{},
+ llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+ clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+ EXPECT_THAT(diags(), IsEmpty());
+
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+ locForFile("test/t1.cpp")));
+
+ // FIXME: This is confusing.
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+ locForFile("lld/test/t2.cpp")));
+}
+
+TEST_F(SuppressionMappingTest, ShortLongMatch) {
+ llvm::StringLiteral SuppressionMappingFile = R"(
+ [unused]
+ src:*lld/*=emit
+ src:*test/*)";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+ FS->addFile("foo.txt", /*ModificationTime=*/{},
+ llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+ clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+ EXPECT_THAT(diags(), IsEmpty());
+
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+ locForFile("test/t1.cpp")));
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+ locForFile("lld/test/t2.cpp")));
+}
+
TEST_F(SuppressionMappingTest, IsIgnored) {
llvm::StringLiteral SuppressionMappingFile = R"(
[unused]
diff --git a/clang/unittests/Driver/ToolChainTest.cpp b/clang/unittests/Driver/ToolChainTest.cpp
index c1c5c96..afa17ff 100644
--- a/clang/unittests/Driver/ToolChainTest.cpp
+++ b/clang/unittests/Driver/ToolChainTest.cpp
@@ -442,7 +442,7 @@ TEST(ToolChainTest, ParsedClangName) {
TEST(ToolChainTest, GetTargetAndMode) {
llvm::InitializeAllTargets();
std::string IgnoredError;
- if (!llvm::TargetRegistry::lookupTarget("x86_64", IgnoredError))
+ if (!llvm::TargetRegistry::lookupTarget(llvm::Triple("x86_64"), IgnoredError))
GTEST_SKIP();
ParsedClangName Res = ToolChain::getTargetAndModeFromProgramName("clang");