141 changes: 7 additions & 134 deletions clang/tools/driver/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
Expand All @@ -41,7 +42,6 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
Expand Down Expand Up @@ -73,136 +73,8 @@ std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
return llvm::sys::fs::getMainExecutable(Argv0, P);
}

static const char *GetStableCStr(std::set<std::string> &SavedStrings,
StringRef S) {
return SavedStrings.insert(std::string(S)).first->c_str();
}

/// ApplyOneQAOverride - Apply a list of edits to the input argument lists.
///
/// The input string is a space separated list of edits to perform,
/// they are applied in order to the input argument lists. Edits
/// should be one of the following forms:
///
/// '#': Silence information about the changes to the command line arguments.
///
/// '^': Add FOO as a new argument at the beginning of the command line.
///
/// '+': Add FOO as a new argument at the end of the command line.
///
/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command
/// line.
///
/// 'xOPTION': Removes all instances of the literal argument OPTION.
///
/// 'XOPTION': Removes all instances of the literal argument OPTION,
/// and the following argument.
///
/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox'
/// at the end of the command line.
///
/// \param OS - The stream to write edit information to.
/// \param Args - The vector of command line arguments.
/// \param Edit - The override command to perform.
/// \param SavedStrings - Set to use for storing string representations.
static void ApplyOneQAOverride(raw_ostream &OS,
SmallVectorImpl<const char*> &Args,
StringRef Edit,
std::set<std::string> &SavedStrings) {
// This does not need to be efficient.

if (Edit[0] == '^') {
const char *Str =
GetStableCStr(SavedStrings, Edit.substr(1));
OS << "### Adding argument " << Str << " at beginning\n";
Args.insert(Args.begin() + 1, Str);
} else if (Edit[0] == '+') {
const char *Str =
GetStableCStr(SavedStrings, Edit.substr(1));
OS << "### Adding argument " << Str << " at end\n";
Args.push_back(Str);
} else if (Edit[0] == 's' && Edit[1] == '/' && Edit.ends_with("/") &&
Edit.slice(2, Edit.size() - 1).contains('/')) {
StringRef MatchPattern = Edit.substr(2).split('/').first;
StringRef ReplPattern = Edit.substr(2).split('/').second;
ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1);

for (unsigned i = 1, e = Args.size(); i != e; ++i) {
// Ignore end-of-line response file markers
if (Args[i] == nullptr)
continue;
std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]);

if (Repl != Args[i]) {
OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n";
Args[i] = GetStableCStr(SavedStrings, Repl);
}
}
} else if (Edit[0] == 'x' || Edit[0] == 'X') {
auto Option = Edit.substr(1);
for (unsigned i = 1; i < Args.size();) {
if (Option == Args[i]) {
OS << "### Deleting argument " << Args[i] << '\n';
Args.erase(Args.begin() + i);
if (Edit[0] == 'X') {
if (i < Args.size()) {
OS << "### Deleting argument " << Args[i] << '\n';
Args.erase(Args.begin() + i);
} else
OS << "### Invalid X edit, end of command line!\n";
}
} else
++i;
}
} else if (Edit[0] == 'O') {
for (unsigned i = 1; i < Args.size();) {
const char *A = Args[i];
// Ignore end-of-line response file markers
if (A == nullptr)
continue;
if (A[0] == '-' && A[1] == 'O' &&
(A[2] == '\0' ||
(A[3] == '\0' && (A[2] == 's' || A[2] == 'z' ||
('0' <= A[2] && A[2] <= '9'))))) {
OS << "### Deleting argument " << Args[i] << '\n';
Args.erase(Args.begin() + i);
} else
++i;
}
OS << "### Adding argument " << Edit << " at end\n";
Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str()));
} else {
OS << "### Unrecognized edit: " << Edit << "\n";
}
}

/// ApplyQAOverride - Apply a space separated list of edits to the
/// input argument lists. See ApplyOneQAOverride.
static void ApplyQAOverride(SmallVectorImpl<const char*> &Args,
const char *OverrideStr,
std::set<std::string> &SavedStrings) {
raw_ostream *OS = &llvm::errs();

if (OverrideStr[0] == '#') {
++OverrideStr;
OS = &llvm::nulls();
}

*OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n";

// This does not need to be efficient.

const char *S = OverrideStr;
while (*S) {
const char *End = ::strchr(S, ' ');
if (!End)
End = S + strlen(S);
if (End != S)
ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings);
S = End;
if (*S != '\0')
++S;
}
static const char *GetStableCStr(llvm::StringSet<> &SavedStrings, StringRef S) {
return SavedStrings.insert(S).first->getKeyData();
}

extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
Expand All @@ -215,7 +87,7 @@ extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv,

static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
std::set<std::string> &SavedStrings) {
llvm::StringSet<> &SavedStrings) {
// Put target and mode arguments at the start of argument list so that
// arguments specified in command line could override them. Avoid putting
// them at index 0, as an option like '-cc1' must remain the first.
Expand Down Expand Up @@ -419,12 +291,13 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
}
}

std::set<std::string> SavedStrings;
llvm::StringSet<> SavedStrings;
// Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the
// scenes.
if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) {
// FIXME: Driver shouldn't take extra initial argument.
ApplyQAOverride(Args, OverrideStr, SavedStrings);
driver::applyOverrideOptions(Args, OverrideStr, SavedStrings,
&llvm::errs());
}

std::string Path = GetExecutablePath(ToolContext.Path, CanonicalPrefixes);
Expand Down
7 changes: 1 addition & 6 deletions clang/unittests/Format/.clang-format
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
BasedOnStyle: LLVM
InsertBraces: true
InsertNewlineAtEOF: true
LineEnding: LF
RemoveBracesLLVM: true
RemoveParentheses: ReturnStatement
BasedOnStyle: clang-format
21 changes: 21 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3802,6 +3802,27 @@ TEST_F(FormatTest, FormatsEnum) {
" // Comment 2\n"
" TWO,\n"
"};");
verifyFormat("enum [[clang::enum_extensibility(open)]] E {\n"
" // Comment 1\n"
" ONE,\n"
" // Comment 2\n"
" TWO\n"
"};");
verifyFormat("enum [[nodiscard]] [[clang::enum_extensibility(open)]] E {\n"
" // Comment 1\n"
" ONE,\n"
" // Comment 2\n"
" TWO\n"
"};");
verifyFormat("enum [[clang::enum_extensibility(open)]] E { // foo\n"
" A,\n"
" // bar\n"
" B\n"
"};",
"enum [[clang::enum_extensibility(open)]] E{// foo\n"
" A,\n"
" // bar\n"
" B};");

// Not enums.
verifyFormat("enum X f() {\n"
Expand Down
5 changes: 5 additions & 0 deletions clang/unittests/Format/TokenAnnotatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2645,6 +2645,11 @@ TEST_F(TokenAnnotatorTest, StartOfName) {
EXPECT_TOKEN(Tokens[2], tok::identifier, TT_Unknown);
EXPECT_TOKEN(Tokens[3], tok::identifier, TT_Unknown);
EXPECT_TOKEN(Tokens[4], tok::identifier, TT_StartOfName);

Tokens = annotate("@interface NSCoder (TestCoder)");
ASSERT_EQ(Tokens.size(), 7u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::at, TT_ObjCDecl);
EXPECT_TOKEN(Tokens[2], tok::identifier, TT_StartOfName);
}

TEST_F(TokenAnnotatorTest, BraceKind) {
Expand Down
8 changes: 4 additions & 4 deletions compiler-rt/test/tsan/signal_reset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ static void* reset(void *p) {
struct sigaction act = {};
for (int i = 0; i < 1000000; i++) {
act.sa_handler = &handler;
if (sigaction(SIGPROF, &act, 0)) {
if (sigaction(SIGALRM, &act, 0)) {
perror("sigaction");
exit(1);
}
act.sa_handler = SIG_IGN;
if (sigaction(SIGPROF, &act, 0)) {
if (sigaction(SIGALRM, &act, 0)) {
perror("sigaction");
exit(1);
}
Expand All @@ -44,7 +44,7 @@ static void* reset(void *p) {
int main() {
struct sigaction act = {};
act.sa_handler = SIG_IGN;
if (sigaction(SIGPROF, &act, 0)) {
if (sigaction(SIGALRM, &act, 0)) {
perror("sigaction");
exit(1);
}
Expand All @@ -53,7 +53,7 @@ int main() {
t.it_value.tv_sec = 0;
t.it_value.tv_usec = 10;
t.it_interval = t.it_value;
if (setitimer(ITIMER_PROF, &t, 0)) {
if (setitimer(ITIMER_REAL, &t, 0)) {
perror("setitimer");
exit(1);
}
Expand Down
2 changes: 1 addition & 1 deletion libc/config/baremetal/api.td
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def StdlibAPI : PublicAPI<"stdlib.h"> {
"size_t",
"__bsearchcompare_t",
"__qsortcompare_t",
"__qsortrcompare_t",
"__atexithandler_t",
];
}

Expand Down
6 changes: 5 additions & 1 deletion libc/docs/gpu/building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,15 @@ targets. This section will briefly describe their purpose.
be used to enable host services for anyone looking to interface with the
:ref:`RPC client<libc_gpu_rpc>`.

.. _gpu_cmake_options:

CMake options
=============

This section briefly lists a few of the CMake variables that specifically
control the GPU build of the C library.
control the GPU build of the C library. These options can be passed individually
to each target using ``-DRUNTIMES_<target>_<variable>=<value>`` when using a
standard runtime build.

**LLVM_LIBC_FULL_BUILD**:BOOL
This flag controls whether or not the libc build will generate its own
Expand Down
144 changes: 127 additions & 17 deletions libc/docs/gpu/testing.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.. _libc_gpu_testing:


============================
Testing the GPU libc library
============================
=========================
Testing the GPU C library
=========================

.. note::
Running GPU tests with high parallelism is likely to cause spurious failures,
Expand All @@ -14,24 +14,134 @@ Testing the GPU libc library
:depth: 4
:local:

Testing Infrastructure
Testing infrastructure
======================

The testing support in LLVM's libc implementation for GPUs is designed to mimic
the standard unit tests as much as possible. We use the :ref:`libc_gpu_rpc`
support to provide the necessary utilities like printing from the GPU. Execution
is performed by emitting a ``_start`` kernel from the GPU
that is then called by an external loader utility. This is an example of how
this can be done manually:
The LLVM C library supports different kinds of :ref:`tests <build_and_test>`
depending on the build configuration. The GPU target is considered a full build
and therefore provides all of its own utilities to build and run the generated
tests. Currently the GPU supports two kinds of tests.

#. **Hermetic tests** - These are unit tests built with a test suite similar to
Google's ``gtest`` infrastructure. These use the same infrastructure as unit
tests except that the entire environment is self-hosted. This allows us to
run them on the GPU using our custom utilities. These are used to test the
majority of functional implementations.

#. **Integration tests** - These are lightweight tests that simply call a
``main`` function and checks if it returns non-zero. These are primarily used
to test interfaces that are sensitive to threading.

The GPU uses the same testing infrastructure as the other supported ``libc``
targets. We do this by treating the GPU as a standard hosted environment capable
of launching a ``main`` function. Effectively, this means building our own
startup libraries and loader.

Testing utilities
=================

We provide two utilities to execute arbitrary programs on the GPU. That is the
``loader`` and the ``start`` object.

Startup object
--------------

This object mimics the standard object used by existing C library
implementations. Its job is to perform the necessary setup prior to calling the
``main`` function. In the GPU case, this means exporting GPU kernels that will
perform the necessary operations. Here we use ``_begin`` and ``_end`` to handle
calling global constructors and destructors while ``_start`` begins the standard
execution. The following code block shows the implementation for AMDGPU
architectures.

.. code-block:: c++

extern "C" [[gnu::visibility("protected"), clang::amdgpu_kernel]] void
_begin(int argc, char **argv, char **env) {
LIBC_NAMESPACE::atexit(&LIBC_NAMESPACE::call_fini_array_callbacks);
LIBC_NAMESPACE::call_init_array_callbacks(argc, argv, env);
}
extern "C" [[gnu::visibility("protected"), clang::amdgpu_kernel]] void
_start(int argc, char **argv, char **envp, int *ret) {
__atomic_fetch_or(ret, main(argc, argv, envp), __ATOMIC_RELAXED);
}
extern "C" [[gnu::visibility("protected"), clang::amdgpu_kernel]] void
_end(int retval) {
LIBC_NAMESPACE::exit(retval);
}

Loader runtime
--------------

The startup object provides a GPU executable with callable kernels for the
respective runtime. We can then define a minimal runtime that will launch these
kernels on the given device. Currently we provide the ``amdhsa-loader`` and
``nvptx-loader`` targeting the AMD HSA runtime and CUDA driver runtime
respectively. By default these will launch with a single thread on the GPU.

.. code-block:: sh
$> clang++ crt1.o test.cpp --target=amdgcn-amd-amdhsa -mcpu=gfx90a -flto
$> ./amdhsa_loader --threads 1 --blocks 1 a.out
$> clang++ crt1.o test.cpp --target=amdgcn-amd-amdhsa -mcpu=native -flto
$> amdhsa_loader --threads 1 --blocks 1 ./a.out
Test Passed!
Unlike the exported ``libcgpu.a``, the testing architecture can only support a
single architecture at a time. This is either detected automatically, or set
manually by the user using ``LIBC_GPU_TEST_ARCHITECTURE``. The latter is useful
in cases where the user does not build LLVM's libc on machine with the GPU to
use for testing.
The loader utility will forward any arguments passed after the executable image
to the program on the GPU as well as any set environment variables. The number
of threads and blocks to be set can be controlled with ``--threads`` and
``--blocks``. These also accept additional ``x``, ``y``, ``z`` variants for
multidimensional grids.

Running tests
=============

Tests will only be built and run if a GPU target architecture is set and the
corresponding loader utility was built. These can be overridden with the
``LIBC_GPU_TEST_ARCHITECTURE`` and ``LIBC_GPU_LOADER_EXECUTABLE`` :ref:`CMake
options <gpu_cmake_options>`. Once built, they can be run like any other tests.
The CMake target depends on how the library was built.

#. **Cross build** - If the C library was built using ``LLVM_ENABLE_PROJECTS``
or a runtimes cross build, then the standard targets will be present in the
base CMake build directory.

#. All tests - You can run all supported tests with the command:

.. code-block:: sh
$> ninja check-libc
#. Hermetic tests - You can run hermetic with tests the command:

.. code-block:: sh
$> ninja libc-hermetic-tests
#. Integration tests - You can run integration tests by the command:

.. code-block:: sh
$> ninja libc-integration-tests
#. **Runtimes build** - If the library was built using ``LLVM_ENABLE_RUNTIMES``
then the actual ``libc`` build will be in a separate directory.

#. All tests - You can run all supported tests with the command:

.. code-block:: sh
$> ninja check-libc-amdgcn-amd-amdhsa
$> ninja check-libc-nvptx64-nvidia-cuda
#. Specific tests - You can use the same targets as above by entering the
runtimes build directory.

.. code-block:: sh
$> ninja -C runtimes/runtimes-amdgcn-amd-amdhsa-bins check-libc
$> ninja -C runtimes/runtimes-nvptx64-nvidia-cuda-bins check-libc
$> cd runtimes/runtimes-amdgcn-amd-amdhsa-bins && ninja check-libc
$> cd runtimes/runtimes-nvptx64-nvidia-cuda-bins && ninja check-libc
Tests can also be built and run manually using the respective loader utility.
14 changes: 9 additions & 5 deletions libc/docs/gpu/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,21 @@ GPUs.
}

We can then compile this for both NVPTX and AMDGPU into LLVM-IR using the
following commands.
following commands. This will yield valid LLVM-IR for the given target just like
if we were using CUDA, OpenCL, or OpenMP.

.. code-block:: sh

$> clang id.c --target=amdgcn-amd-amdhsa -mcpu=native -nogpulib -flto -c
$> clang id.c --target=nvptx64-nvidia-cuda -march=native -nogpulib -flto -c

We use this support to treat the GPU as a hosted environment by providing a C
library and startup object just like a standard C library running on the host
machine. Then, in order to execute these programs, we provide a loader utility
to launch the executable on the GPU similar to a cross-compiling emulator.
We can also use this support to treat the GPU as a hosted environment by
providing a C library and startup object just like a standard C library running
on the host machine. Then, in order to execute these programs, we provide a
loader utility to launch the executable on the GPU similar to a cross-compiling
emulator. This is how we run :ref:`unit tests <libc_gpu_testing>` targeting the
GPU. This is clearly not the most efficient way to use a GPU, but it provides a
simple method to test execution on a GPU for debugging or development.

Building for AMDGPU targets
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"`P2396R1 <https://wg21.link/P2396R1>`__","LWG", "Concurrency TS 2 fixes ", "November 2022","","","|concurrency TS|"
"`P2505R5 <https://wg21.link/P2505R5>`__","LWG", "Monadic Functions for ``std::expected``", "November 2022","|Complete|","17.0",""
"`P2539R4 <https://wg21.link/P2539R4>`__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","|Complete|","18.0","|format|"
"`P2602R2 <https://wg21.link/P2602R2>`__","LWG", "Poison Pills are Too Toxic", "November 2022","","","|ranges|"
"`P2602R2 <https://wg21.link/P2602R2>`__","LWG", "Poison Pills are Too Toxic", "November 2022","|Complete|","19.0","|ranges|"
"`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
"","","","","","",""
"`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|"
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/__compare/partial_order.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD

// [cmp.alg]
namespace __partial_order {
void partial_order() = delete;

struct __fn {
// NOLINTBEGIN(libcpp-robust-against-adl) partial_order should use ADL, but only here
template <class _Tp, class _Up>
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/__compare/strong_order.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD

// [cmp.alg]
namespace __strong_order {
void strong_order() = delete;

struct __fn {
// NOLINTBEGIN(libcpp-robust-against-adl) strong_order should use ADL, but only here
template <class _Tp, class _Up>
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/__compare/weak_order.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD

// [cmp.alg]
namespace __weak_order {
void weak_order() = delete;

struct __fn {
// NOLINTBEGIN(libcpp-robust-against-adl) weak_order should use ADL, but only here
template <class _Tp, class _Up>
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__format/format_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include <cstddef>

#ifndef _LIBCPP_HAS_NO_LOCALIZATION
# include <locale>
# include <__locale>
# include <optional>
#endif

Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__format/format_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#include <string_view>

#ifndef _LIBCPP_HAS_NO_LOCALIZATION
# include <locale>
# include <__locale>
#endif

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__format/formatter_floating_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include <cstddef>

#ifndef _LIBCPP_HAS_NO_LOCALIZATION
# include <locale>
# include <__locale>
#endif

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__iterator/iter_move.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
namespace __iter_move {

void iter_move();
void iter_move() = delete;

template <class _Tp>
concept __unqualified_iter_move = __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
Expand Down
6 changes: 2 additions & 4 deletions libcxx/include/__ranges/access.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ concept __member_begin = __can_borrow<_Tp> && __workaround_52970<_Tp> && require
{ _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
};

void begin(auto&) = delete;
void begin(const auto&) = delete;
void begin() = delete;

template <class _Tp>
concept __unqualified_begin =
Expand Down Expand Up @@ -109,8 +108,7 @@ concept __member_end = __can_borrow<_Tp> && __workaround_52970<_Tp> && requires(
{ _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
};

void end(auto&) = delete;
void end(const auto&) = delete;
void end() = delete;

template <class _Tp>
concept __unqualified_end =
Expand Down
3 changes: 1 addition & 2 deletions libcxx/include/__ranges/rbegin.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ concept __member_rbegin = __can_borrow<_Tp> && __workaround_52970<_Tp> && requir
{ _LIBCPP_AUTO_CAST(__t.rbegin()) } -> input_or_output_iterator;
};

void rbegin(auto&) = delete;
void rbegin(const auto&) = delete;
void rbegin() = delete;

template <class _Tp>
concept __unqualified_rbegin =
Expand Down
3 changes: 1 addition & 2 deletions libcxx/include/__ranges/rend.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ concept __member_rend = __can_borrow<_Tp> && __workaround_52970<_Tp> && requires
{ _LIBCPP_AUTO_CAST(__t.rend()) } -> sentinel_for<decltype(ranges::rbegin(__t))>;
};

void rend(auto&) = delete;
void rend(const auto&) = delete;
void rend() = delete;

template <class _Tp>
concept __unqualified_rend =
Expand Down
3 changes: 1 addition & 2 deletions libcxx/include/__ranges/size.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ inline constexpr bool disable_sized_range = false;

namespace ranges {
namespace __size {
void size(auto&) = delete;
void size(const auto&) = delete;
void size() = delete;

template <class _Tp>
concept __size_enabled = !disable_sized_range<remove_cvref_t<_Tp>>;
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,7 @@ constexpr chrono::year operator ""y(unsigned lo

#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER == 20
# include <charconv>
# include <locale>
#endif

#endif // _LIBCPP_CHRONO
7 changes: 1 addition & 6 deletions libcxx/include/complex
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ template<class T> complex<T> tanh (const complex<T>&);

#include <__config>
#include <__fwd/complex.h>
#include <__fwd/tuple.h>
#include <__tuple/tuple_element.h>
#include <__tuple/tuple_size.h>
#include <__utility/move.h>
Expand Down Expand Up @@ -1443,15 +1444,9 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const complex<_Tp>& __x) {

// [complex.tuple], tuple interface

template <class _Tp>
struct tuple_size;

template <class _Tp>
struct tuple_size<complex<_Tp>> : integral_constant<size_t, 2> {};

template <size_t _Ip, class _Tp>
struct tuple_element;

template <size_t _Ip, class _Tp>
struct tuple_element<_Ip, complex<_Tp>> {
static_assert(_Ip < 2, "Index value is out of range.");
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/format
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,8 @@ namespace std {
# pragma GCC system_header
#endif

#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
# include <locale>
#endif

#endif // _LIBCPP_FORMAT
20 changes: 1 addition & 19 deletions libcxx/include/mutex
Original file line number Diff line number Diff line change
Expand Up @@ -418,24 +418,6 @@ inline _LIBCPP_HIDE_FROM_ABI void lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&...
std::__lock_first(0, __l0, __l1, __l2, __l3...);
}

template <class _L0>
inline _LIBCPP_HIDE_FROM_ABI void __unlock(_L0& __l0) {
__l0.unlock();
}

template <class _L0, class _L1>
inline _LIBCPP_HIDE_FROM_ABI void __unlock(_L0& __l0, _L1& __l1) {
__l0.unlock();
__l1.unlock();
}

template <class _L0, class _L1, class _L2, class... _L3>
inline _LIBCPP_HIDE_FROM_ABI void __unlock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3) {
__l0.unlock();
__l1.unlock();
std::__unlock(__l2, __l3...);
}

# endif // _LIBCPP_CXX03_LANG

# if _LIBCPP_STD_VER >= 17
Expand Down Expand Up @@ -498,7 +480,7 @@ public:
private:
template <size_t... _Indx>
_LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(__tuple_indices<_Indx...>, _MutexTuple& __mt) {
std::__unlock(std::get<_Indx>(__mt)...);
(std::get<_Indx>(__mt).unlock(), ...);
}

_MutexTuple __t_;
Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx03.csv
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,14 @@ filesystem system_error
filesystem type_traits
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx11.csv
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,14 @@ filesystem system_error
filesystem type_traits
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx14.csv
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,14 @@ filesystem system_error
filesystem type_traits
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx17.csv
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,14 @@ filesystem system_error
filesystem type_traits
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
Expand Down
10 changes: 10 additions & 0 deletions libcxx/test/libcxx/transitive_includes/cxx20.csv
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,19 @@ charconv type_traits
charconv version
chrono array
chrono bit
chrono cctype
chrono cerrno
chrono charconv
chrono clocale
chrono cmath
chrono compare
chrono concepts
chrono cstddef
chrono cstdint
chrono cstdlib
chrono cstring
chrono ctime
chrono cwchar
chrono forward_list
chrono limits
chrono locale
Expand Down Expand Up @@ -275,9 +280,14 @@ filesystem system_error
filesystem type_traits
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
Expand Down
16 changes: 14 additions & 2 deletions libcxx/test/libcxx/transitive_includes/cxx23.csv
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,20 @@ charconv limits
charconv new
charconv version
chrono array
chrono cctype
chrono cerrno
chrono clocale
chrono cmath
chrono compare
chrono cstddef
chrono cstdint
chrono cstdlib
chrono cstring
chrono ctime
chrono cwchar
chrono forward_list
chrono initializer_list
chrono limits
chrono locale
chrono new
chrono optional
chrono ostream
Expand All @@ -85,6 +90,8 @@ chrono sstream
chrono stdexcept
chrono string
chrono string_view
chrono tuple
chrono typeinfo
chrono vector
chrono version
cinttypes cstdint
Expand Down Expand Up @@ -184,12 +191,16 @@ filesystem string
filesystem string_view
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
format new
format optional
format queue
Expand All @@ -198,6 +209,7 @@ format stdexcept
format string
format string_view
format tuple
format typeinfo
format version
forward_list compare
forward_list cstddef
Expand Down
16 changes: 14 additions & 2 deletions libcxx/test/libcxx/transitive_includes/cxx26.csv
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,20 @@ charconv limits
charconv new
charconv version
chrono array
chrono cctype
chrono cerrno
chrono clocale
chrono cmath
chrono compare
chrono cstddef
chrono cstdint
chrono cstdlib
chrono cstring
chrono ctime
chrono cwchar
chrono forward_list
chrono initializer_list
chrono limits
chrono locale
chrono new
chrono optional
chrono ostream
Expand All @@ -85,6 +90,8 @@ chrono sstream
chrono stdexcept
chrono string
chrono string_view
chrono tuple
chrono typeinfo
chrono vector
chrono version
cinttypes cstdint
Expand Down Expand Up @@ -184,12 +191,16 @@ filesystem string
filesystem string_view
filesystem version
format array
format cctype
format clocale
format cmath
format cstddef
format cstdint
format cstdlib
format cstring
format cwchar
format initializer_list
format limits
format locale
format new
format optional
format queue
Expand All @@ -198,6 +209,7 @@ format stdexcept
format string
format string_view
format tuple
format typeinfo
format version
forward_list compare
forward_list cstddef
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,14 @@ int main(int, char**)
errno = E2BIG; // something that message will never generate
const std::error_category& e_cat1 = std::generic_category();
const std::string msg = e_cat1.message(-1);
// Exact message format varies by platform. We can't detect
// some of these (Musl in particular) using the preprocessor,
// so accept a few sensible messages. Newlib unfortunately
// responds with an empty message, which we probably want to
// treat as a failure code otherwise, but we can detect that
// with the preprocessor.
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0 // AIX
|| msg.rfind("No error information", 0) == 0 // Musl
|| msg.rfind("Unknown error", 0) == 0 // Glibc
#if defined(_NEWLIB_VERSION)
|| msg.empty()
// Exact message format varies by platform.
#if defined(_AIX)
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0);
#elif defined(_NEWLIB_VERSION)
LIBCPP_ASSERT(msg.empty());
#else
LIBCPP_ASSERT(msg.rfind("Unknown error", 0) == 0);
#endif
);
assert(errno == E2BIG);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,14 @@ int main(int, char**) {
errno = E2BIG; // something that message will never generate
const std::error_category& e_cat1 = std::system_category();
const std::string msg = e_cat1.message(-1);
// Exact message format varies by platform. We can't detect
// some of these (Musl in particular) using the preprocessor,
// so accept a few sensible messages. Newlib unfortunately
// responds with an empty message, which we probably want to
// treat as a failure code otherwise, but we can detect that
// with the preprocessor.
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0 // AIX
|| msg.rfind("No error information", 0) == 0 // Musl
|| msg.rfind("Unknown error", 0) == 0 // Glibc
#if defined(_NEWLIB_VERSION)
|| msg.empty()
// Exact message format varies by platform.
#if defined(_AIX)
LIBCPP_ASSERT(msg.rfind("Error -1 occurred", 0) == 0);
#elif defined(_NEWLIB_VERSION)
LIBCPP_ASSERT(msg.empty());
#else
LIBCPP_ASSERT(msg.rfind("Unknown error", 0) == 0);
#endif
);
assert(errno == E2BIG);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, HasIterSwap&>);
static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, int&>);
static_assert(!std::is_invocable_v<IterSwapT&&, int&, HasIterSwap&>);

struct StructWithNotMoreSpecializedIterSwap {
friend void iter_swap(auto&, auto&);
};

static_assert(
!std::is_invocable_v<IterSwapT, StructWithNotMoreSpecializedIterSwap&, StructWithNotMoreSpecializedIterSwap&>);

struct NodiscardIterSwap {
[[nodiscard]] friend int iter_swap(NodiscardIterSwap&, NodiscardIterSwap&) { return 0; }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <locale>
#include <ios>
#include <cassert>
#include <cstdio>
#include <streambuf>
#include <cmath>
#include "test_macros.h"
Expand Down Expand Up @@ -8935,12 +8934,11 @@ void test4()
char str[200];
std::locale lc = std::locale::classic();
std::locale lg(lc, new my_numpunct);

std::string inf;

// This should match the underlying C library
std::sprintf(str, "%f", INFINITY);
inf = str;
#ifdef _AIX
std::string inf = "INF";
#else
std::string inf = "inf";
#endif

const my_facet f(1);
{
Expand Down Expand Up @@ -10729,27 +10727,24 @@ void test5()
std::locale lc = std::locale::classic();
std::locale lg(lc, new my_numpunct);
const my_facet f(1);

std::string nan;
std::string NaN;
std::string pnan_sign;

// The output here depends on the underlying C library, so work out what
// that does.
std::sprintf(str, "%f", std::nan(""));
nan = str;

std::sprintf(str, "%F", std::nan(""));
NaN = str;

std::sprintf(str, "%+f", std::nan(""));
if (str[0] == '+') {
pnan_sign = "+";
}

std::string nan_padding25 = std::string(25 - nan.length(), '*');
std::string pnan_padding25 = std::string(25 - nan.length() - pnan_sign.length(), '*');

#if defined(_AIX)
std::string nan= "NaNQ";
std::string NaN = "NaNQ";
std::string nan_padding25 = "*********************";
std::string pnan_sign = "+";
std::string pnan_padding25 = "********************";
#else
std::string nan= "nan";
std::string NaN = "NAN";
std::string nan_padding25 = "**********************";
#if defined(TEST_HAS_GLIBC) || defined(_WIN32)
std::string pnan_sign = "+";
std::string pnan_padding25 = "*********************";
#else
std::string pnan_sign = "";
std::string pnan_padding25 = "**********************";
#endif
#endif
{
long double v = std::nan("");
std::ios ios(0);
Expand Down
10 changes: 5 additions & 5 deletions libcxx/test/std/ranges/range.access/begin.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ struct BeginFunction {
};
static_assert( std::is_invocable_v<RangeBeginT, BeginFunction const&>);
static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &&>);
static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &>);
static_assert(std::is_invocable_v<RangeBeginT, BeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>);
static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>);

Expand Down Expand Up @@ -245,7 +245,7 @@ struct BeginFunctionWithPrivateBeginMember {
constexpr bool testBeginFunction() {
BeginFunction a{};
const BeginFunction aa{};
static_assert(!std::invocable<RangeBeginT, decltype((a))>);
assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cbegin(a) == &a.x);
assert(std::ranges::begin(aa) == &aa.x);
assert(std::ranges::cbegin(aa) == &aa.x);
Expand All @@ -266,21 +266,21 @@ constexpr bool testBeginFunction() {

BeginFunctionReturnsEmptyPtr d{};
const BeginFunctionReturnsEmptyPtr dd{};
static_assert(!std::invocable<RangeBeginT, decltype((d))>);
assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cbegin(d) == &d.x);
assert(std::ranges::begin(dd) == &dd.x);
assert(std::ranges::cbegin(dd) == &dd.x);

BeginFunctionWithDataMember e{};
const BeginFunctionWithDataMember ee{};
static_assert(!std::invocable<RangeBeginT, decltype((e))>);
assert(std::ranges::begin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::begin(ee) == &ee.x);
assert(std::ranges::cbegin(e) == &e.x);
assert(std::ranges::cbegin(ee) == &ee.x);

BeginFunctionWithPrivateBeginMember f{};
const BeginFunctionWithPrivateBeginMember ff{};
static_assert(!std::invocable<RangeBeginT, decltype((f))>);
assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cbegin(f) == &f.y);
assert(std::ranges::begin(ff) == &ff.y);
assert(std::ranges::cbegin(ff) == &ff.y);
Expand Down
12 changes: 6 additions & 6 deletions libcxx/test/std/ranges/range.access/end.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ static_assert(!std::is_invocable_v<RangeEndT, EndFunction &&>);

static_assert( std::is_invocable_v<RangeEndT, EndFunction const&>);
static_assert(!std::is_invocable_v<RangeEndT, EndFunction &&>);
static_assert(!std::is_invocable_v<RangeEndT, EndFunction &>);
static_assert(std::is_invocable_v<RangeEndT, EndFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
static_assert( std::is_invocable_v<RangeCEndT, EndFunction const&>);
static_assert( std::is_invocable_v<RangeCEndT, EndFunction &>);

Expand Down Expand Up @@ -271,7 +271,7 @@ constexpr bool testEndFunction() {
assert(std::ranges::end(a) == &a.x);
assert(std::ranges::cend(a) == &a.x);
EndFunction aa{};
static_assert(!std::is_invocable_v<RangeEndT, decltype((aa))>);
assert(std::ranges::end(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cend(aa) == &aa.x);

EndFunctionByValue b;
Expand All @@ -286,28 +286,28 @@ constexpr bool testEndFunction() {
assert(std::ranges::end(d) == &d.x);
assert(std::ranges::cend(d) == &d.x);
EndFunctionReturnsEmptyPtr dd{};
static_assert(!std::is_invocable_v<RangeEndT, decltype((dd))>);
assert(std::ranges::end(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cend(dd) == &dd.x);

const EndFunctionWithDataMember e{};
assert(std::ranges::end(e) == &e.x);
assert(std::ranges::cend(e) == &e.x);
EndFunctionWithDataMember ee{};
static_assert(!std::is_invocable_v<RangeEndT, decltype((ee))>);
assert(std::ranges::end(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cend(ee) == &ee.x);

const EndFunctionWithPrivateEndMember f{};
assert(std::ranges::end(f) == &f.y);
assert(std::ranges::cend(f) == &f.y);
EndFunctionWithPrivateEndMember ff{};
static_assert(!std::is_invocable_v<RangeEndT, decltype((ff))>);
assert(std::ranges::end(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cend(ff) == &ff.y);

const BeginMemberEndFunction g{};
assert(std::ranges::end(g) == &g.x);
assert(std::ranges::cend(g) == &g.x);
BeginMemberEndFunction gg{};
static_assert(!std::is_invocable_v<RangeEndT, decltype((gg))>);
assert(std::ranges::end(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::cend(gg) == &gg.x);

return true;
Expand Down
11 changes: 6 additions & 5 deletions libcxx/test/std/ranges/range.access/rbegin.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ struct RBeginFunction {
};
static_assert( std::is_invocable_v<RangeRBeginT, RBeginFunction const&>);
static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &&>);
static_assert(!std::is_invocable_v<RangeRBeginT, RBeginFunction &>);
static_assert(
std::is_invocable_v<RangeRBeginT, RBeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction const&>);
static_assert( std::is_invocable_v<RangeCRBeginT, RBeginFunction &>);

Expand Down Expand Up @@ -246,7 +247,7 @@ struct RBeginFunctionWithPrivateBeginMember {
constexpr bool testRBeginFunction() {
RBeginFunction a{};
const RBeginFunction aa{};
static_assert(!std::invocable<RangeRBeginT, decltype((a))>);
assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crbegin(a) == &a.x);
assert(std::ranges::rbegin(aa) == &aa.x);
assert(std::ranges::crbegin(aa) == &aa.x);
Expand All @@ -267,21 +268,21 @@ constexpr bool testRBeginFunction() {

RBeginFunctionReturnsEmptyPtr d{};
const RBeginFunctionReturnsEmptyPtr dd{};
static_assert(!std::invocable<RangeRBeginT, decltype((d))>);
assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crbegin(d) == &d.x);
assert(std::ranges::rbegin(dd) == &dd.x);
assert(std::ranges::crbegin(dd) == &dd.x);

RBeginFunctionWithDataMember e{};
const RBeginFunctionWithDataMember ee{};
static_assert(!std::invocable<RangeRBeginT, decltype((e))>);
assert(std::ranges::rbegin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::rbegin(ee) == &ee.x);
assert(std::ranges::crbegin(e) == &e.x);
assert(std::ranges::crbegin(ee) == &ee.x);

RBeginFunctionWithPrivateBeginMember f{};
const RBeginFunctionWithPrivateBeginMember ff{};
static_assert(!std::invocable<RangeRBeginT, decltype((f))>);
assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crbegin(f) == &f.y);
assert(std::ranges::rbegin(ff) == &ff.y);
assert(std::ranges::crbegin(ff) == &ff.y);
Expand Down
12 changes: 6 additions & 6 deletions libcxx/test/std/ranges/range.access/rend.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);

static_assert( std::is_invocable_v<RangeREndT, REndFunction const&>);
static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
static_assert(!std::is_invocable_v<RangeREndT, REndFunction &>);
static_assert(std::is_invocable_v<RangeREndT, REndFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);

Expand Down Expand Up @@ -272,7 +272,7 @@ constexpr bool testREndFunction() {
assert(std::ranges::rend(a) == &a.x);
assert(std::ranges::crend(a) == &a.x);
REndFunction aa{};
static_assert(!std::is_invocable_v<RangeREndT, decltype((aa))>);
assert(std::ranges::rend(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crend(aa) == &aa.x);

REndFunctionByValue b;
Expand All @@ -287,28 +287,28 @@ constexpr bool testREndFunction() {
assert(std::ranges::rend(d) == &d.x);
assert(std::ranges::crend(d) == &d.x);
REndFunctionReturnsEmptyPtr dd{};
static_assert(!std::is_invocable_v<RangeREndT, decltype((dd))>);
assert(std::ranges::rend(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crend(dd) == &dd.x);

const REndFunctionWithDataMember e{};
assert(std::ranges::rend(e) == &e.x);
assert(std::ranges::crend(e) == &e.x);
REndFunctionWithDataMember ee{};
static_assert(!std::is_invocable_v<RangeREndT, decltype((ee))>);
assert(std::ranges::rend(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crend(ee) == &ee.x);

const REndFunctionWithPrivateEndMember f{};
assert(std::ranges::rend(f) == &f.y);
assert(std::ranges::crend(f) == &f.y);
REndFunctionWithPrivateEndMember ff{};
static_assert(!std::is_invocable_v<RangeREndT, decltype((ff))>);
assert(std::ranges::rend(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crend(ff) == &ff.y);

const RBeginMemberEndFunction g{};
assert(std::ranges::rend(g) == &g.x);
assert(std::ranges::crend(g) == &g.x);
RBeginMemberEndFunction gg{};
static_assert(!std::is_invocable_v<RangeREndT, decltype((gg))>);
assert(std::ranges::rend(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
assert(std::ranges::crend(gg) == &gg.x);

return true;
Expand Down
3 changes: 2 additions & 1 deletion libcxx/test/std/ranges/range.access/size.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledF

static_assert( std::is_invocable_v<RangeSizeT, ImproperlyDisabledMember&>);
static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledMember&>);
static_assert(!std::is_invocable_v<RangeSizeT, ImproperlyDisabledFunction&>);
static_assert(std::is_invocable_v<RangeSizeT,
ImproperlyDisabledFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledFunction&>);

// No begin end.
Expand Down
102 changes: 102 additions & 0 deletions libcxx/test/std/ranges/robust_against_poison_pills.compile.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// <compare>, <iterator>, <ranges>

// ADL should be performed. Ordinary unqualified lookup should not be performed.

namespace ns {
struct StructWithGlobalFunctions {};
} // namespace ns

struct ConvertibleToCmpType;
ConvertibleToCmpType strong_order(const ns::StructWithGlobalFunctions&, const ns::StructWithGlobalFunctions&);
ConvertibleToCmpType weak_order(const ns::StructWithGlobalFunctions&, const ns::StructWithGlobalFunctions&);
ConvertibleToCmpType partial_order(const ns::StructWithGlobalFunctions&, const ns::StructWithGlobalFunctions&);

int&& iter_move(const ns::StructWithGlobalFunctions&);
void iter_swap(const ns::StructWithGlobalFunctions&, const ns::StructWithGlobalFunctions&);

int* begin(const ns::StructWithGlobalFunctions&);
int* end(const ns::StructWithGlobalFunctions&);
int* rbegin(const ns::StructWithGlobalFunctions&);
int* rend(const ns::StructWithGlobalFunctions&);
unsigned int size(const ns::StructWithGlobalFunctions&);

#include <compare>
#include <ranges>
#include <type_traits>

struct ConvertibleToCmpType {
operator std::strong_ordering() const;
operator std::weak_ordering() const;
operator std::partial_ordering() const;
};

struct StructWithHiddenFriends {
friend ConvertibleToCmpType strong_order(const StructWithHiddenFriends&, const StructWithHiddenFriends&);
friend ConvertibleToCmpType weak_order(const StructWithHiddenFriends&, const StructWithHiddenFriends&);
friend ConvertibleToCmpType partial_order(const StructWithHiddenFriends&, const StructWithHiddenFriends&);

friend int&& iter_move(const StructWithHiddenFriends&);
friend void iter_swap(const StructWithHiddenFriends&, const StructWithHiddenFriends&);

friend int* begin(const StructWithHiddenFriends&);
friend int* end(const StructWithHiddenFriends&);
friend int* rbegin(const StructWithHiddenFriends&);
friend int* rend(const StructWithHiddenFriends&);
friend unsigned int size(const StructWithHiddenFriends&);
};

// [cmp.alg] ADL should be performed.
static_assert(std::is_invocable_v<decltype(std::strong_order), StructWithHiddenFriends&, StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::weak_order), StructWithHiddenFriends&, StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::partial_order), StructWithHiddenFriends&, StructWithHiddenFriends&>);

// [cmp.alg] Ordinary unqualified lookup should not be performed.
static_assert(
!std::is_invocable_v<decltype(std::strong_order), ns::StructWithGlobalFunctions&, ns::StructWithGlobalFunctions&>);
static_assert(
!std::is_invocable_v<decltype(std::weak_order), ns::StructWithGlobalFunctions&, ns::StructWithGlobalFunctions&>);
static_assert(
!std::is_invocable_v<decltype(std::partial_order), ns::StructWithGlobalFunctions&, ns::StructWithGlobalFunctions&>);

// [iterator.cust] ADL should be performed.
static_assert(std::is_invocable_v<decltype(std::ranges::iter_move), StructWithHiddenFriends&>);
static_assert(
std::is_invocable_v<decltype(std::ranges::iter_swap), StructWithHiddenFriends&, StructWithHiddenFriends&>);

// [iterator.cust] Ordinary unqualified lookup should not be performed.
static_assert(!std::is_invocable_v<decltype(std::ranges::iter_move), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::iter_swap),
ns::StructWithGlobalFunctions&,
ns::StructWithGlobalFunctions&>);

// [range.access] ADL should be performed.
static_assert(std::is_invocable_v<decltype(std::ranges::begin), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::cbegin), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::end), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::cend), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::rbegin), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::crbegin), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::rend), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::crend), StructWithHiddenFriends&>);
static_assert(std::is_invocable_v<decltype(std::ranges::size), StructWithHiddenFriends&>);

// [range.access] Ordinary unqualified lookup should not be performed.
static_assert(!std::is_invocable_v<decltype(std::ranges::begin), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::cbegin), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::end), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::cend), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::rbegin), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::crbegin), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::rend), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::crend), ns::StructWithGlobalFunctions&>);
static_assert(!std::is_invocable_v<decltype(std::ranges::size), ns::StructWithGlobalFunctions&>);
13 changes: 11 additions & 2 deletions lldb/source/Commands/CommandObjectDWIMPrint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatVariadic.h"

#include <regex>

Expand Down Expand Up @@ -161,7 +160,17 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
}
}

// Second, also lastly, try `expr` as a source expression to evaluate.
// Second, try `expr` as a persistent variable.
if (expr.starts_with("$"))
if (auto *state = target.GetPersistentExpressionStateForLanguage(language))
if (auto var_sp = state->GetVariable(expr))
if (auto valobj_sp = var_sp->GetValueObject()) {
valobj_sp->Dump(result.GetOutputStream(), dump_options);
result.SetStatus(eReturnStatusSuccessFinishResult);
return;
}

// Third, and lastly, try `expr` as a source expression to evaluate.
{
auto *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
ValueObjectSP valobj_sp;
Expand Down
10 changes: 5 additions & 5 deletions lldb/source/Host/common/Alarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Alarm::Handle Alarm::Create(std::function<void()> callback) {
Handle handle = INVALID_HANDLE;

{
std::lock_guard alarm_guard(m_alarm_mutex);
std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);

// Create a new unique entry and remember its handle.
m_entries.emplace_back(callback, expiration);
Expand All @@ -59,7 +59,7 @@ bool Alarm::Restart(Handle handle) {
const TimePoint expiration = GetNextExpiration();

{
std::lock_guard alarm_guard(m_alarm_mutex);
std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);

// Find the entry corresponding to the given handle.
const auto it =
Expand All @@ -86,7 +86,7 @@ bool Alarm::Cancel(Handle handle) {
return false;

{
std::lock_guard alarm_guard(m_alarm_mutex);
std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);

const auto it =
std::find_if(m_entries.begin(), m_entries.end(),
Expand Down Expand Up @@ -126,7 +126,7 @@ void Alarm::StartAlarmThread() {
void Alarm::StopAlarmThread() {
if (m_alarm_thread.IsJoinable()) {
{
std::lock_guard alarm_guard(m_alarm_mutex);
std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
m_exit = true;
}
m_alarm_cv.notify_one();
Expand Down Expand Up @@ -154,7 +154,7 @@ lldb::thread_result_t Alarm::AlarmThread() {
//
// Below we only deal with the timeout expiring and fall through for dealing
// with the rest.
std::unique_lock alarm_lock(m_alarm_mutex);
std::unique_lock<std::mutex> alarm_lock(m_alarm_mutex);
if (next_alarm) {
if (!m_alarm_cv.wait_until(alarm_lock, *next_alarm, predicate)) {
// The timeout for the next alarm expired.
Expand Down
12 changes: 12 additions & 0 deletions lldb/test/API/commands/dwim-print/TestDWIMPrint.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,15 @@ def test_void_result(self):
self, "// break here", lldb.SBFileSpec("main.c")
)
self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"])

def test_preserves_persistent_variables(self):
"""Test dwim-print does not delete persistent variables."""
self.build()
lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("main.c")
)
self.expect("dwim-print int $i = 15")
# Run the same expression twice and verify success. This ensures the
# first command does not delete the persistent variable.
for _ in range(2):
self.expect("dwim-print $i", startstr="(int) 15")
8 changes: 4 additions & 4 deletions lldb/unittests/Host/AlarmTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ TEST(AlarmTest, Create) {
ALARM_TIMEOUT);

alarm.Create([&callbacks_actual, &m, i]() {
std::lock_guard guard(m);
std::lock_guard<std::mutex> guard(m);
callbacks_actual[i] = std::chrono::system_clock::now();
});

Expand Down Expand Up @@ -75,7 +75,7 @@ TEST(AlarmTest, Exit) {
callbacks.emplace_back(false);

handles.push_back(alarm.Create([&callbacks, &m, i]() {
std::lock_guard guard(m);
std::lock_guard<std::mutex> guard(m);
callbacks[i] = true;
}));
}
Expand All @@ -101,7 +101,7 @@ TEST(AlarmTest, Cancel) {
callbacks.emplace_back(false);

handles.push_back(alarm.Create([&callbacks, &m, i]() {
std::lock_guard guard(m);
std::lock_guard<std::mutex> guard(m);
callbacks[i] = true;
}));
}
Expand Down Expand Up @@ -137,7 +137,7 @@ TEST(AlarmTest, Restart) {
ALARM_TIMEOUT);

handles.push_back(alarm.Create([&callbacks_actual, &m, i]() {
std::lock_guard guard(m);
std::lock_guard<std::mutex> guard(m);
callbacks_actual[i] = std::chrono::system_clock::now();
}));

Expand Down
2 changes: 1 addition & 1 deletion lldb/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class SymbolFilePDBTests : public testing::Test {
EXPECT_EQ(line, entry.line);
EXPECT_EQ(address, entry.range.GetBaseAddress());

EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.file));
EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.GetFile()));
}

bool ContainsCompileUnit(const SymbolContextList &sc_list,
Expand Down
10 changes: 8 additions & 2 deletions llvm/cmake/modules/AddLLVM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -1637,8 +1637,14 @@ function(add_unittest test_suite test_name)
# The runtime benefits of LTO don't outweight the compile time costs for tests.
if(LLVM_ENABLE_LTO)
if((UNIX OR MINGW) AND LINKER_IS_LLD)
set_property(TARGET ${test_name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,--lto-O0")
if(LLVM_ENABLE_FATLTO)
# When using FatLTO, just use relocatable linking.
set_property(TARGET ${test_name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,--no-fat-lto-objects")
else()
set_property(TARGET ${test_name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,--lto-O0")
endif()
elseif(LINKER_IS_LLD_LINK)
set_property(TARGET ${test_name} APPEND_STRING PROPERTY
LINK_FLAGS " /opt:lldlto=0")
Expand Down
9 changes: 9 additions & 0 deletions llvm/cmake/modules/HandleLLVMOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ endif()
set(LLVM_ENABLE_LTO OFF CACHE STRING "Build LLVM with LTO. May be specified as Thin or Full to use a particular kind of LTO")
string(TOUPPER "${LLVM_ENABLE_LTO}" uppercase_LLVM_ENABLE_LTO)

option(LLVM_ENABLE_FATLTO "Build LLVM with -ffat-lto-objects." OFF)

# Ninja Job Pool support
# The following only works with the Ninja generator in CMake >= 3.0.
set(LLVM_PARALLEL_COMPILE_JOBS "" CACHE STRING
Expand Down Expand Up @@ -1280,6 +1282,13 @@ elseif(LLVM_ENABLE_LTO)
endif()
endif()

if(LLVM_ENABLE_FATLTO AND (FUCHSIA OR UNIX))
append("-ffat-lto-objects" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
if(NOT LINKER_IS_LLD_LINK)
append("-ffat-lto-objects" CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS)
endif()
endif()

# Set an AIX default for LLVM_EXPORT_SYMBOLS_FOR_PLUGINS based on whether we are
# doing dynamic linking (see below).
set(LLVM_EXPORT_SYMBOLS_FOR_PLUGINS_AIX_default OFF)
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/IR/IntrinsicsDirectX.td
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def int_dx_create_handle : ClangBuiltin<"__builtin_hlsl_create_handle">,
Intrinsic<[ llvm_ptr_ty ], [llvm_i8_ty], [IntrWillReturn]>;

def int_dx_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;

def int_dx_clamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_dx_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_dx_dot :
Intrinsic<[LLVMVectorElementType<0>],
[llvm_anyvector_ty, LLVMScalarOrSameVectorWidth<0, LLVMVectorElementType<0>>],
Expand Down
46 changes: 46 additions & 0 deletions llvm/include/llvm/Passes/PassBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,52 @@ class PassBuilder {
void invokePipelineEarlySimplificationEPCallbacks(ModulePassManager &MPM,
OptimizationLevel Level);

static bool checkParametrizedPassName(StringRef Name, StringRef PassName) {
if (!Name.consume_front(PassName))
return false;
// normal pass name w/o parameters == default parameters
if (Name.empty())
return true;
return Name.starts_with("<") && Name.ends_with(">");
}

/// This performs customized parsing of pass name with parameters.
///
/// We do not need parametrization of passes in textual pipeline very often,
/// yet on a rare occasion ability to specify parameters right there can be
/// useful.
///
/// \p Name - parameterized specification of a pass from a textual pipeline
/// is a string in a form of :
/// PassName '<' parameter-list '>'
///
/// Parameter list is being parsed by the parser callable argument, \p Parser,
/// It takes a string-ref of parameters and returns either StringError or a
/// parameter list in a form of a custom parameters type, all wrapped into
/// Expected<> template class.
///
template <typename ParametersParseCallableT>
static auto parsePassParameters(ParametersParseCallableT &&Parser,
StringRef Name, StringRef PassName)
-> decltype(Parser(StringRef{})) {
using ParametersT = typename decltype(Parser(StringRef{}))::value_type;

StringRef Params = Name;
if (!Params.consume_front(PassName)) {
llvm_unreachable(
"unable to strip pass name from parametrized pass specification");
}
if (!Params.empty() &&
(!Params.consume_front("<") || !Params.consume_back(">"))) {
llvm_unreachable("invalid format for parametrized pass name");
}

Expected<ParametersT> Result = Parser(Params);
assert((Result || Result.template errorIsA<StringError>()) &&
"Pass parameter parser can only return StringErrors.");
return Result;
}

private:
// O1 pass pipeline
FunctionPassManager
Expand Down
7 changes: 6 additions & 1 deletion llvm/include/llvm/TextAPI/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ class Record {
public:
Record() = default;
Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags)
: Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)) {}
: Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)),
Verified(false) {}

bool isWeakDefined() const {
return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined;
Expand Down Expand Up @@ -79,6 +80,9 @@ class Record {
bool isExported() const { return Linkage >= RecordLinkage::Rexported; }
bool isRexported() const { return Linkage == RecordLinkage::Rexported; }

bool isVerified() const { return Verified; }
void setVerify(bool V = true) { Verified = V; }

StringRef getName() const { return Name; }
SymbolFlags getFlags() const { return Flags; }

Expand All @@ -89,6 +93,7 @@ class Record {
StringRef Name;
RecordLinkage Linkage;
SymbolFlags Flags;
bool Verified;

friend class RecordsSlice;
};
Expand Down
57 changes: 6 additions & 51 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,15 +503,6 @@ static std::optional<int> parseDevirtPassName(StringRef Name) {
return Count;
}

static bool checkParametrizedPassName(StringRef Name, StringRef PassName) {
if (!Name.consume_front(PassName))
return false;
// normal pass name w/o parameters == default parameters
if (Name.empty())
return true;
return Name.starts_with("<") && Name.ends_with(">");
}

static std::optional<OptimizationLevel> parseOptLevel(StringRef S) {
return StringSwitch<std::optional<OptimizationLevel>>(S)
.Case("O0", OptimizationLevel::O0)
Expand All @@ -525,42 +516,6 @@ static std::optional<OptimizationLevel> parseOptLevel(StringRef S) {

namespace {

/// This performs customized parsing of pass name with parameters.
///
/// We do not need parametrization of passes in textual pipeline very often,
/// yet on a rare occasion ability to specify parameters right there can be
/// useful.
///
/// \p Name - parameterized specification of a pass from a textual pipeline
/// is a string in a form of :
/// PassName '<' parameter-list '>'
///
/// Parameter list is being parsed by the parser callable argument, \p Parser,
/// It takes a string-ref of parameters and returns either StringError or a
/// parameter list in a form of a custom parameters type, all wrapped into
/// Expected<> template class.
///
template <typename ParametersParseCallableT>
auto parsePassParameters(ParametersParseCallableT &&Parser, StringRef Name,
StringRef PassName) -> decltype(Parser(StringRef{})) {
using ParametersT = typename decltype(Parser(StringRef{}))::value_type;

StringRef Params = Name;
if (!Params.consume_front(PassName)) {
assert(false &&
"unable to strip pass name from parametrized pass specification");
}
if (!Params.empty() &&
(!Params.consume_front("<") || !Params.consume_back(">"))) {
assert(false && "invalid format for parametrized pass name");
}

Expected<ParametersT> Result = Parser(Params);
assert((Result || Result.template errorIsA<StringError>()) &&
"Pass parameter parser can only return StringErrors.");
return Result;
}

/// Parser of parameters for HardwareLoops pass.
Expected<HardwareLoopOptions> parseHardwareLoopOptions(StringRef Params) {
HardwareLoopOptions HardwareLoopOpts;
Expand Down Expand Up @@ -1196,7 +1151,7 @@ static bool isModulePassName(StringRef Name, CallbacksT &Callbacks) {
if (Name == NAME) \
return true;
#define MODULE_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
if (checkParametrizedPassName(Name, NAME)) \
if (PassBuilder::checkParametrizedPassName(Name, NAME)) \
return true;
#define MODULE_ANALYSIS(NAME, CREATE_PASS) \
if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \
Expand Down Expand Up @@ -1225,7 +1180,7 @@ static bool isCGSCCPassName(StringRef Name, CallbacksT &Callbacks) {
if (Name == NAME) \
return true;
#define CGSCC_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
if (checkParametrizedPassName(Name, NAME)) \
if (PassBuilder::checkParametrizedPassName(Name, NAME)) \
return true;
#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \
if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \
Expand All @@ -1252,7 +1207,7 @@ static bool isFunctionPassName(StringRef Name, CallbacksT &Callbacks) {
if (Name == NAME) \
return true;
#define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
if (checkParametrizedPassName(Name, NAME)) \
if (PassBuilder::checkParametrizedPassName(Name, NAME)) \
return true;
#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \
if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \
Expand Down Expand Up @@ -1293,7 +1248,7 @@ static bool isLoopNestPassName(StringRef Name, CallbacksT &Callbacks,
if (parseRepeatPassName(Name))
return true;

if (checkParametrizedPassName(Name, "lnicm")) {
if (PassBuilder::checkParametrizedPassName(Name, "lnicm")) {
UseMemorySSA = true;
return true;
}
Expand All @@ -1315,7 +1270,7 @@ static bool isLoopPassName(StringRef Name, CallbacksT &Callbacks,
if (parseRepeatPassName(Name))
return true;

if (checkParametrizedPassName(Name, "licm")) {
if (PassBuilder::checkParametrizedPassName(Name, "licm")) {
UseMemorySSA = true;
return true;
}
Expand All @@ -1324,7 +1279,7 @@ static bool isLoopPassName(StringRef Name, CallbacksT &Callbacks,
if (Name == NAME) \
return true;
#define LOOP_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
if (checkParametrizedPassName(Name, NAME)) \
if (PassBuilder::checkParametrizedPassName(Name, NAME)) \
return true;
#define LOOP_ANALYSIS(NAME, CREATE_PASS) \
if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/DirectX/DXIL.td
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,18 @@ def Round : DXILOpMapping<26, unary, int_round,
"Returns the input rounded to the nearest integer"
"within a floating-point type.",
[llvm_halforfloat_ty, LLVMMatchType<0>]>;
def FMax : DXILOpMapping<35, binary, int_maxnum,
"Float maximum. FMax(a,b) = a > b ? a : b">;
def FMin : DXILOpMapping<36, binary, int_minnum,
"Float minimum. FMin(a,b) = a < b ? a : b">;
def SMax : DXILOpMapping<37, binary, int_smax,
"Signed integer maximum. SMax(a,b) = a > b ? a : b">;
def SMin : DXILOpMapping<38, binary, int_smin,
"Signed integer minimum. SMin(a,b) = a < b ? a : b">;
def UMax : DXILOpMapping<39, binary, int_umax,
"Unsigned integer maximum. UMax(a,b) = a > b ? a : b">;
def UMin : DXILOpMapping<40, binary, int_umin,
"Unsigned integer minimum. UMin(a,b) = a < b ? a : b">;
def FMad : DXILOpMapping<46, tertiary, int_fmuladd,
"Floating point arithmetic multiply/add operation. fmad(m,a,b) = m * a + b.">;
def IMad : DXILOpMapping<48, tertiary, int_dx_imad,
Expand Down
49 changes: 49 additions & 0 deletions llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ static bool isIntrinsicExpansion(Function &F) {
switch (F.getIntrinsicID()) {
case Intrinsic::exp:
case Intrinsic::dx_any:
case Intrinsic::dx_clamp:
case Intrinsic::dx_uclamp:
case Intrinsic::dx_lerp:
case Intrinsic::dx_rcp:
return true;
Expand Down Expand Up @@ -132,12 +134,59 @@ static bool expandRcpIntrinsic(CallInst *Orig) {
return true;
}

static Intrinsic::ID getMaxForClamp(Type *ElemTy,
Intrinsic::ID ClampIntrinsic) {
if (ClampIntrinsic == Intrinsic::dx_uclamp)
return Intrinsic::umax;
assert(ClampIntrinsic == Intrinsic::dx_clamp);
if (ElemTy->isVectorTy())
ElemTy = ElemTy->getScalarType();
if (ElemTy->isIntegerTy())
return Intrinsic::smax;
assert(ElemTy->isFloatingPointTy());
return Intrinsic::maxnum;
}

static Intrinsic::ID getMinForClamp(Type *ElemTy,
Intrinsic::ID ClampIntrinsic) {
if (ClampIntrinsic == Intrinsic::dx_uclamp)
return Intrinsic::umin;
assert(ClampIntrinsic == Intrinsic::dx_clamp);
if (ElemTy->isVectorTy())
ElemTy = ElemTy->getScalarType();
if (ElemTy->isIntegerTy())
return Intrinsic::smin;
assert(ElemTy->isFloatingPointTy());
return Intrinsic::minnum;
}

static bool expandClampIntrinsic(CallInst *Orig, Intrinsic::ID ClampIntrinsic) {
Value *X = Orig->getOperand(0);
Value *Min = Orig->getOperand(1);
Value *Max = Orig->getOperand(2);
Type *Ty = X->getType();
IRBuilder<> Builder(Orig->getParent());
Builder.SetInsertPoint(Orig);
auto *MaxCall = Builder.CreateIntrinsic(
Ty, getMaxForClamp(Ty, ClampIntrinsic), {X, Min}, nullptr, "dx.max");
auto *MinCall =
Builder.CreateIntrinsic(Ty, getMinForClamp(Ty, ClampIntrinsic),
{MaxCall, Max}, nullptr, "dx.min");

Orig->replaceAllUsesWith(MinCall);
Orig->eraseFromParent();
return true;
}

static bool expandIntrinsic(Function &F, CallInst *Orig) {
switch (F.getIntrinsicID()) {
case Intrinsic::exp:
return expandExpIntrinsic(Orig);
case Intrinsic::dx_any:
return expandAnyIntrinsic(Orig);
case Intrinsic::dx_uclamp:
case Intrinsic::dx_clamp:
return expandClampIntrinsic(Orig, F.getIntrinsicID());
case Intrinsic::dx_lerp:
return expandLerpIntrinsic(Orig);
case Intrinsic::dx_rcp:
Expand Down
22 changes: 0 additions & 22 deletions llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,28 +1255,6 @@ void MipsTargetELFStreamer::emitDirectiveCpsetup(unsigned RegNo,
emitRRI(Mips::SD, GPReg, Mips::SP, RegOrOffset, SMLoc(), &STI);
}

#if 0
// We haven't support -mabicalls -mno-shared yet.
if (-mno-shared) {
MCSymbol *GPSym = MCA.getContext().getOrCreateSymbol("__gnu_local_gp");
const MipsMCExpr *HiExpr = MipsMCExpr::create(
MipsMCExpr::MEK_HI, MCSymbolRefExpr::create(GPSym, MCA.getContext()),
MCA.getContext());
const MipsMCExpr *LoExpr = MipsMCExpr::create(
MipsMCExpr::MEK_LO, MCSymbolRefExpr::create(GPSym, MCA.getContext()),
MCA.getContext());

// lui $gp, %hi(__gnu_local_gp)
emitRX(Mips::LUi, GPReg, MCOperand::createExpr(HiExpr), SMLoc(), &STI);

// addiu $gp, $gp, %lo(__gnu_local_gp)
emitRRX(Mips::ADDiu, GPReg, GPReg, MCOperand::createExpr(LoExpr), SMLoc(),
&STI);

return;
}
#endif

const MipsMCExpr *HiExpr = MipsMCExpr::createGpOff(
MipsMCExpr::MEK_HI, MCSymbolRefExpr::create(&Sym, MCA.getContext()),
MCA.getContext());
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/TextAPI/TextStub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,16 +1079,16 @@ Expected<FileType> TextAPIReader::canRead(MemoryBufferRef InputBuffer) {
if (!TAPIFile.ends_with("..."))
return createStringError(std::errc::not_supported, "unsupported file type");

if (TAPIFile.starts_with("--- !tapi-tbd\n"))
if (TAPIFile.starts_with("--- !tapi-tbd"))
return FileType::TBD_V4;

if (TAPIFile.starts_with("--- !tapi-tbd-v3\n"))
if (TAPIFile.starts_with("--- !tapi-tbd-v3"))
return FileType::TBD_V3;

if (TAPIFile.starts_with("--- !tapi-tbd-v2\n"))
if (TAPIFile.starts_with("--- !tapi-tbd-v2"))
return FileType::TBD_V2;

if (TAPIFile.starts_with("--- !tapi-tbd-v1\n") ||
if (TAPIFile.starts_with("--- !tapi-tbd-v1") ||
TAPIFile.starts_with("---\narchs:"))
return FileType::TBD_V1;

Expand Down
41 changes: 31 additions & 10 deletions llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ static cl::opt<bool>
cl::desc("View the CFG before DFA Jump Threading"),
cl::Hidden, cl::init(false));

static cl::opt<bool> EarlyExitHeuristic(
"dfa-early-exit-heuristic",
cl::desc("Exit early if an unpredictable value come from the same loop"),
cl::Hidden, cl::init(true));

static cl::opt<unsigned> MaxPathLength(
"dfa-max-path-length",
cl::desc("Max number of blocks searched to find a threading path"),
Expand Down Expand Up @@ -405,7 +410,7 @@ struct MainSwitch {
///
/// Also, collect select instructions to unfold.
bool isCandidate(const SwitchInst *SI) {
std::deque<Value *> Q;
std::deque<std::pair<Value *, BasicBlock *>> Q;
SmallSet<Value *, 16> SeenValues;
SelectInsts.clear();

Expand All @@ -415,25 +420,28 @@ struct MainSwitch {
return false;

// The switch must be in a loop.
if (!LI->getLoopFor(SI->getParent()))
const Loop *L = LI->getLoopFor(SI->getParent());
if (!L)
return false;

addToQueue(SICond, Q, SeenValues);
addToQueue(SICond, nullptr, Q, SeenValues);

while (!Q.empty()) {
Value *Current = Q.front();
Value *Current = Q.front().first;
BasicBlock *CurrentIncomingBB = Q.front().second;
Q.pop_front();

if (auto *Phi = dyn_cast<PHINode>(Current)) {
for (Value *Incoming : Phi->incoming_values()) {
addToQueue(Incoming, Q, SeenValues);
for (BasicBlock *IncomingBB : Phi->blocks()) {
Value *Incoming = Phi->getIncomingValueForBlock(IncomingBB);
addToQueue(Incoming, IncomingBB, Q, SeenValues);
}
LLVM_DEBUG(dbgs() << "\tphi: " << *Phi << "\n");
} else if (SelectInst *SelI = dyn_cast<SelectInst>(Current)) {
if (!isValidSelectInst(SelI))
return false;
addToQueue(SelI->getTrueValue(), Q, SeenValues);
addToQueue(SelI->getFalseValue(), Q, SeenValues);
addToQueue(SelI->getTrueValue(), CurrentIncomingBB, Q, SeenValues);
addToQueue(SelI->getFalseValue(), CurrentIncomingBB, Q, SeenValues);
LLVM_DEBUG(dbgs() << "\tselect: " << *SelI << "\n");
if (auto *SelIUse = dyn_cast<PHINode>(SelI->user_back()))
SelectInsts.push_back(SelectInstToUnfold(SelI, SelIUse));
Expand All @@ -446,18 +454,31 @@ struct MainSwitch {
// initial switch values that can be ignored (they will hit the
// unthreaded switch) but this assumption will get checked later after
// paths have been enumerated (in function getStateDefMap).

// If the unpredictable value comes from the same inner loop it is
// likely that it will also be on the enumerated paths, causing us to
// exit after we have enumerated all the paths. This heuristic save
// compile time because a search for all the paths can become expensive.
if (EarlyExitHeuristic &&
L->contains(LI->getLoopFor(CurrentIncomingBB))) {
LLVM_DEBUG(dbgs()
<< "\tExiting early due to unpredictability heuristic.\n");
return false;
}

continue;
}
}

return true;
}

void addToQueue(Value *Val, std::deque<Value *> &Q,
void addToQueue(Value *Val, BasicBlock *BB,
std::deque<std::pair<Value *, BasicBlock *>> &Q,
SmallSet<Value *, 16> &SeenValues) {
if (SeenValues.contains(Val))
return;
Q.push_back(Val);
Q.push_back({Val, BB});
SeenValues.insert(Val);
}

Expand Down
61 changes: 58 additions & 3 deletions llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
Expand Down Expand Up @@ -990,12 +991,15 @@ class LowerMatrixIntrinsics {
bool Changed = false;
SmallVector<CallInst *, 16> MaybeFusableInsts;
SmallVector<Instruction *, 16> MatrixInsts;
SmallVector<IntrinsicInst *, 16> LifetimeEnds;

// First, collect all instructions with shape information and candidates for
// fusion (currently only matrix multiplies).
ReversePostOrderTraversal<Function *> RPOT(&Func);
for (auto *BB : RPOT)
for (Instruction &I : *BB) {
if (match(&I, m_Intrinsic<Intrinsic::lifetime_end>()))
LifetimeEnds.push_back(cast<IntrinsicInst>(&I));
if (ShapeMap.find(&I) == ShapeMap.end())
continue;
if (match(&I, m_Intrinsic<Intrinsic::matrix_multiply>()))
Expand All @@ -1010,7 +1014,7 @@ class LowerMatrixIntrinsics {

// Third, try to fuse candidates.
for (CallInst *CI : MaybeFusableInsts)
LowerMatrixMultiplyFused(CI, FusedInsts);
LowerMatrixMultiplyFused(CI, FusedInsts, LifetimeEnds);

Changed = !FusedInsts.empty();

Expand Down Expand Up @@ -1856,8 +1860,10 @@ class LowerMatrixIntrinsics {
///
/// Call finalizeLowering on lowered instructions. Instructions that are
/// completely eliminated by fusion are added to \p FusedInsts.
void LowerMatrixMultiplyFused(CallInst *MatMul,
SmallPtrSetImpl<Instruction *> &FusedInsts) {
void
LowerMatrixMultiplyFused(CallInst *MatMul,
SmallPtrSetImpl<Instruction *> &FusedInsts,
SmallVector<IntrinsicInst *, 16> &LifetimeEnds) {
if (!FuseMatrix || !DT)
return;

Expand Down Expand Up @@ -1946,6 +1952,55 @@ class LowerMatrixIntrinsics {
for (Instruction *I : ToHoist)
I->moveBefore(MatMul);

// Deal with lifetime.end calls that might be between Load0/Load1 and the
// store. To avoid introducing loads to dead objects (i.e. after the
// lifetime has been termined by @llvm.lifetime.end), either sink them
// after the store if in the same block, or remove the lifetime.end marker
// otherwise. This might pessimize further optimizations, by extending the
// lifetime of the object until the function returns, but should be
// conservatively correct.
MemoryLocation Load0Loc = MemoryLocation::get(LoadOp0);
MemoryLocation Load1Loc = MemoryLocation::get(LoadOp1);
BasicBlock *StoreParent = Store->getParent();
bool FusableOpsInSameBlock = LoadOp0->getParent() == StoreParent &&
LoadOp1->getParent() == StoreParent;
for (unsigned Idx = 0; Idx != LifetimeEnds.size();) {
IntrinsicInst *End = LifetimeEnds[Idx];
auto Inc = make_scope_exit([&Idx]() { Idx++; });
// If the lifetime.end is guaranteed to be before the loads or after the
// store, it won't interfere with fusion.
if (DT->dominates(End, LoadOp0) && DT->dominates(End, LoadOp1))
continue;
if (DT->dominates(Store, End))
continue;
// If all fusable ops are in the same block and the lifetime.end is in a
// different block, it won't interfere with fusion.
if (FusableOpsInSameBlock && End->getParent() != StoreParent)
continue;

// If the loads don't alias the lifetime.end, it won't interfere with
// fusion.
MemoryLocation EndLoc = MemoryLocation::getForArgument(End, 1, nullptr);
if (!EndLoc.Ptr)
continue;
if (AA->isNoAlias(Load0Loc, EndLoc) && AA->isNoAlias(Load1Loc, EndLoc))
continue;

// If both lifetime.end and the store are in the same block, extend the
// lifetime until after the store, so the new lifetime covers the loads
// we introduce later.
if (End->getParent() == StoreParent) {
End->moveAfter(Store);
continue;
}

// Otherwise remove the conflicting lifetime.end marker.
ToRemove.push_back(End);
std::swap(LifetimeEnds[Idx], LifetimeEnds.back());
LifetimeEnds.pop_back();
Inc.release();
}

emitSIMDTiling(MatMul, LoadOp0, LoadOp1, Store, FusedInsts);
return;
}
Expand Down
74 changes: 74 additions & 0 deletions llvm/test/CodeGen/DirectX/clamp-vec.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
; RUN: opt -S -dxil-intrinsic-expansion < %s | FileCheck %s

; Make sure dxil operation function calls for clamp are generated for float/int/uint vectors.

; CHECK-LABEL: clamp_half3
define noundef <3 x half> @clamp_half3(<3 x half> noundef %a, <3 x half> noundef %b, <3 x half> noundef %c) {
entry:
; CHECK: call <3 x half> @llvm.maxnum.v3f16(<3 x half> %a, <3 x half> %b)
; CHECK: call <3 x half> @llvm.minnum.v3f16(<3 x half> %{{.*}}, <3 x half> %c)
%dx.clamp = call <3 x half> @llvm.dx.clamp.v3f16(<3 x half> %a, <3 x half> %b, <3 x half> %c)
ret <3 x half> %dx.clamp
}

; CHECK-LABEL: clamp_float4
define noundef <4 x float> @clamp_float4(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
entry:
; CHECK: call <4 x float> @llvm.maxnum.v4f32(<4 x float> %a, <4 x float> %b)
; CHECK: call <4 x float> @llvm.minnum.v4f32(<4 x float> %{{.*}}, <4 x float> %c)
%dx.clamp = call <4 x float> @llvm.dx.clamp.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c)
ret <4 x float> %dx.clamp
}

; CHECK-LABEL: clamp_double2
define noundef <2 x double> @clamp_double2(<2 x double> noundef %a, <2 x double> noundef %b, <2 x double> noundef %c) {
entry:
; CHECK: call <2 x double> @llvm.maxnum.v2f64(<2 x double> %a, <2 x double> %b)
; CHECK: call <2 x double> @llvm.minnum.v2f64(<2 x double> %{{.*}}, <2 x double> %c)
%dx.clamp = call <2 x double> @llvm.dx.clamp.v2f64(<2 x double> %a, <2 x double> %b, <2 x double> %c)
ret <2 x double> %dx.clamp
}

; CHECK-LABEL: clamp_int4
define noundef <4 x i32> @clamp_int4(<4 x i32> noundef %a, <4 x i32> noundef %b, <4 x i32> noundef %c) {
entry:
; CHECK: call <4 x i32> @llvm.smax.v4i32(<4 x i32> %a, <4 x i32> %b)
; CHECK: call <4 x i32> @llvm.smin.v4i32(<4 x i32> %{{.*}}, <4 x i32> %c)
%dx.clamp = call <4 x i32> @llvm.dx.clamp.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c)
ret <4 x i32> %dx.clamp
}

; CHECK-LABEL: clamp_uint16_t3
define noundef <3 x i16> @clamp_uint16_t3(<3 x i16> noundef %a, <3 x i16> noundef %b, <3 x i16> noundef %c) {
entry:
; CHECK: call <3 x i16> @llvm.umax.v3i16(<3 x i16> %a, <3 x i16> %b)
; CHECK: call <3 x i16> @llvm.umin.v3i16(<3 x i16> %{{.*}}, <3 x i16> %c)
%dx.clamp = call <3 x i16> @llvm.dx.uclamp.v3i16(<3 x i16> %a, <3 x i16> %b, <3 x i16> %c)
ret <3 x i16> %dx.clamp
}

; CHECK-LABEL: clamp_uint4
define noundef <4 x i32> @clamp_uint4(<4 x i32> noundef %a, <4 x i32> noundef %b, <4 x i32> noundef %c) {
entry:
; CHECK: call <4 x i32> @llvm.umax.v4i32(<4 x i32> %a, <4 x i32> %b)
; CHECK: call <4 x i32> @llvm.umin.v4i32(<4 x i32> %{{.*}}, <4 x i32> %c)
%dx.clamp = call <4 x i32> @llvm.dx.uclamp.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c)
ret <4 x i32> %dx.clamp
}

; CHECK-LABEL: clamp_uint64_t4
define noundef <2 x i64> @clamp_uint64_t4(<2 x i64> noundef %a, <2 x i64> noundef %b, <2 x i64> noundef %c) {
entry:
; CHECK: call <2 x i64> @llvm.umax.v2i64(<2 x i64> %a, <2 x i64> %b)
; CHECK: call <2 x i64> @llvm.umin.v2i64(<2 x i64> %{{.*}}, <2 x i64> %c)
%dx.clamp = call <2 x i64> @llvm.dx.uclamp.v2i64(<2 x i64> %a, <2 x i64> %b, <2 x i64> %c)
ret <2 x i64> %dx.clamp
}

declare <3 x half> @llvm.dx.clamp.v3f16(<3 x half>, <3 x half>, <3 x half>)
declare <4 x float> @llvm.dx.clamp.v4f32(<4 x float>, <4 x float>, <4 x float>)
declare <2 x double> @llvm.dx.clamp.v2f64(<2 x double>, <2 x double>, <2 x double>)
declare <4 x i32> @llvm.dx.clamp.v4i32(<4 x i32>, <4 x i32>, <4 x i32>)
declare <3 x i16> @llvm.dx.uclamp.v3i32(<3 x i16>, <3 x i32>, <3 x i16>)
declare <4 x i32> @llvm.dx.uclamp.v4i32(<4 x i32>, <4 x i32>, <4 x i32>)
declare <2 x i64> @llvm.dx.uclamp.v2i64(<2 x i64>, <2 x i64>, <2 x i64>)
94 changes: 94 additions & 0 deletions llvm/test/CodeGen/DirectX/clamp.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for clamp/uclamp are generated for half/float/double/i16/i32/i64.

; CHECK-LABEL:test_clamp_i16
define noundef i16 @test_clamp_i16(i16 noundef %a, i16 noundef %b, i16 noundef %c) {
entry:
; CHECK: call i16 @dx.op.binary.i16(i32 37, i16 %{{.*}}, i16 %{{.*}})
; CHECK: call i16 @dx.op.binary.i16(i32 38, i16 %{{.*}}, i16 %{{.*}})
%0 = call i16 @llvm.dx.clamp.i16(i16 %a, i16 %b, i16 %c)
ret i16 %0
}

; CHECK-LABEL:test_clamp_i32
define noundef i32 @test_clamp_i32(i32 noundef %a, i32 noundef %b, i32 noundef %c) {
entry:
; CHECK: call i32 @dx.op.binary.i32(i32 37, i32 %{{.*}}, i32 %{{.*}})
; CHECK: call i32 @dx.op.binary.i32(i32 38, i32 %{{.*}}, i32 %{{.*}})
%0 = call i32 @llvm.dx.clamp.i32(i32 %a, i32 %b, i32 %c)
ret i32 %0
}

; CHECK-LABEL:test_clamp_i64
define noundef i64 @test_clamp_i64(i64 noundef %a, i64 noundef %b, i64 noundef %c) {
entry:
; CHECK: call i64 @dx.op.binary.i64(i32 37, i64 %a, i64 %b)
; CHECK: call i64 @dx.op.binary.i64(i32 38, i64 %{{.*}}, i64 %c)
%0 = call i64 @llvm.dx.clamp.i64(i64 %a, i64 %b, i64 %c)
ret i64 %0
}

; CHECK-LABEL:test_clamp_half
define noundef half @test_clamp_half(half noundef %a, half noundef %b, half noundef %c) {
entry:
; CHECK: call half @dx.op.binary.f16(i32 35, half %{{.*}}, half %{{.*}})
; CHECK: call half @dx.op.binary.f16(i32 36, half %{{.*}}, half %{{.*}})
%0 = call half @llvm.dx.clamp.f16(half %a, half %b, half %c)
ret half %0
}

; CHECK-LABEL:test_clamp_float
define noundef float @test_clamp_float(float noundef %a, float noundef %b, float noundef %c) {
entry:
; CHECK: call float @dx.op.binary.f32(i32 35, float %{{.*}}, float %{{.*}})
; CHECK: call float @dx.op.binary.f32(i32 36, float %{{.*}}, float %{{.*}})
%0 = call float @llvm.dx.clamp.f32(float %a, float %b, float %c)
ret float %0
}

; CHECK-LABEL:test_clamp_double
define noundef double @test_clamp_double(double noundef %a, double noundef %b, double noundef %c) {
entry:
; CHECK: call double @dx.op.binary.f64(i32 35, double %{{.*}}, double %{{.*}})
; CHECK: call double @dx.op.binary.f64(i32 36, double %{{.*}}, double %{{.*}})
%0 = call double @llvm.dx.clamp.f64(double %a, double %b, double %c)
ret double %0
}

; CHECK-LABEL:test_uclamp_i16
define noundef i16 @test_uclamp_i16(i16 noundef %a, i16 noundef %b, i16 noundef %c) {
entry:
; CHECK: call i16 @dx.op.binary.i16(i32 39, i16 %{{.*}}, i16 %{{.*}})
; CHECK: call i16 @dx.op.binary.i16(i32 40, i16 %{{.*}}, i16 %{{.*}})
%0 = call i16 @llvm.dx.uclamp.i16(i16 %a, i16 %b, i16 %c)
ret i16 %0
}

; CHECK-LABEL:test_uclamp_i32
define noundef i32 @test_uclamp_i32(i32 noundef %a, i32 noundef %b, i32 noundef %c) {
entry:
; CHECK: call i32 @dx.op.binary.i32(i32 39, i32 %{{.*}}, i32 %{{.*}})
; CHECK: call i32 @dx.op.binary.i32(i32 40, i32 %{{.*}}, i32 %{{.*}})
%0 = call i32 @llvm.dx.uclamp.i32(i32 %a, i32 %b, i32 %c)
ret i32 %0
}

; CHECK-LABEL:test_uclamp_i64
define noundef i64 @test_uclamp_i64(i64 noundef %a, i64 noundef %b, i64 noundef %c) {
entry:
; CHECK: call i64 @dx.op.binary.i64(i32 39, i64 %a, i64 %b)
; CHECK: call i64 @dx.op.binary.i64(i32 40, i64 %{{.*}}, i64 %c)
%0 = call i64 @llvm.dx.uclamp.i64(i64 %a, i64 %b, i64 %c)
ret i64 %0
}

declare half @llvm.dx.clamp.f16(half, half, half)
declare float @llvm.dx.clamp.f32(float, float, float)
declare double @llvm.dx.clamp.f64(double, double, double)
declare i16 @llvm.dx.clamp.i16(i16, i16, i16)
declare i32 @llvm.dx.clamp.i32(i32, i32, i32)
declare i64 @llvm.dx.clamp.i64(i64, i64, i64)
declare i16 @llvm.dx.uclamp.i16(i16, i16, i16)
declare i32 @llvm.dx.uclamp.i32(i32, i32, i32)
declare i64 @llvm.dx.uclamp.i64(i64, i64, i64)
31 changes: 31 additions & 0 deletions llvm/test/CodeGen/DirectX/fmax.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for fmax are generated for half/float/double.

; CHECK-LABEL:test_fmax_half
define noundef half @test_fmax_half(half noundef %a, half noundef %b) {
entry:
; CHECK: call half @dx.op.binary.f16(i32 35, half %{{.*}}, half %{{.*}})
%0 = call half @llvm.maxnum.f16(half %a, half %b)
ret half %0
}

; CHECK-LABEL:test_fmax_float
define noundef float @test_fmax_float(float noundef %a, float noundef %b) {
entry:
; CHECK: call float @dx.op.binary.f32(i32 35, float %{{.*}}, float %{{.*}})
%0 = call float @llvm.maxnum.f32(float %a, float %b)
ret float %0
}

; CHECK-LABEL:test_fmax_double
define noundef double @test_fmax_double(double noundef %a, double noundef %b) {
entry:
; CHECK: call double @dx.op.binary.f64(i32 35, double %{{.*}}, double %{{.*}})
%0 = call double @llvm.maxnum.f64(double %a, double %b)
ret double %0
}

declare half @llvm.maxnum.f16(half, half)
declare float @llvm.maxnum.f32(float, float)
declare double @llvm.maxnum.f64(double, double)
31 changes: 31 additions & 0 deletions llvm/test/CodeGen/DirectX/fmin.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for fmin are generated for half/float/double.

; CHECK-LABEL:test_fmin_half
define noundef half @test_fmin_half(half noundef %a, half noundef %b) {
entry:
; CHECK: call half @dx.op.binary.f16(i32 36, half %{{.*}}, half %{{.*}})
%0 = call half @llvm.minnum.f16(half %a, half %b)
ret half %0
}

; CHECK-LABEL:test_fmin_float
define noundef float @test_fmin_float(float noundef %a, float noundef %b) {
entry:
; CHECK: call float @dx.op.binary.f32(i32 36, float %{{.*}}, float %{{.*}})
%0 = call float @llvm.minnum.f32(float %a, float %b)
ret float %0
}

; CHECK-LABEL:test_fmin_double
define noundef double @test_fmin_double(double noundef %a, double noundef %b) {
entry:
; CHECK: call double @dx.op.binary.f64(i32 36, double %{{.*}}, double %{{.*}})
%0 = call double @llvm.minnum.f64(double %a, double %b)
ret double %0
}

declare half @llvm.minnum.f16(half, half)
declare float @llvm.minnum.f32(float, float)
declare double @llvm.minnum.f64(double, double)
31 changes: 31 additions & 0 deletions llvm/test/CodeGen/DirectX/smax.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for smax are generated for i16/i32/i64.

; CHECK-LABEL:test_smax_i16
define noundef i16 @test_smax_i16(i16 noundef %a, i16 noundef %b) {
entry:
; CHECK: call i16 @dx.op.binary.i16(i32 37, i16 %{{.*}}, i16 %{{.*}})
%0 = call i16 @llvm.smax.i16(i16 %a, i16 %b)
ret i16 %0
}

; CHECK-LABEL:test_smax_i32
define noundef i32 @test_smax_i32(i32 noundef %a, i32 noundef %b) {
entry:
; CHECK: call i32 @dx.op.binary.i32(i32 37, i32 %{{.*}}, i32 %{{.*}})
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
ret i32 %0
}

; CHECK-LABEL:test_smax_i64
define noundef i64 @test_smax_i64(i64 noundef %a, i64 noundef %b) {
entry:
; CHECK: call i64 @dx.op.binary.i64(i32 37, i64 %{{.*}}, i64 %{{.*}})
%0 = call i64 @llvm.smax.i64(i64 %a, i64 %b)
ret i64 %0
}

declare i16 @llvm.smax.i16(i16, i16)
declare i32 @llvm.smax.i32(i32, i32)
declare i64 @llvm.smax.i64(i64, i64)
31 changes: 31 additions & 0 deletions llvm/test/CodeGen/DirectX/smin.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for smin are generated for i16/i32/i64.

; CHECK-LABEL:test_smin_i16
define noundef i16 @test_smin_i16(i16 noundef %a, i16 noundef %b) {
entry:
; CHECK: call i16 @dx.op.binary.i16(i32 38, i16 %{{.*}}, i16 %{{.*}})
%0 = call i16 @llvm.smin.i16(i16 %a, i16 %b)
ret i16 %0
}

; CHECK-LABEL:test_smin_i32
define noundef i32 @test_smin_i32(i32 noundef %a, i32 noundef %b) {
entry:
; CHECK: call i32 @dx.op.binary.i32(i32 38, i32 %{{.*}}, i32 %{{.*}})
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
ret i32 %0
}

; CHECK-LABEL:test_smin_i64
define noundef i64 @test_smin_i64(i64 noundef %a, i64 noundef %b) {
entry:
; CHECK: call i64 @dx.op.binary.i64(i32 38, i64 %{{.*}}, i64 %{{.*}})
%0 = call i64 @llvm.smin.i64(i64 %a, i64 %b)
ret i64 %0
}

declare i16 @llvm.smin.i16(i16, i16)
declare i32 @llvm.smin.i32(i32, i32)
declare i64 @llvm.smin.i64(i64, i64)
29 changes: 15 additions & 14 deletions llvm/test/CodeGen/DirectX/umax.ll
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for umax are generated for i32/i64.
; Make sure dxil operation function calls for umax are generated for i16/i32/i64.

target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-pc-shadermodel6.7-library"
; CHECK-LABEL:test_umax_i16
define noundef i16 @test_umax_i16(i16 noundef %a, i16 noundef %b) {
entry:
; CHECK: call i16 @dx.op.binary.i16(i32 39, i16 %{{.*}}, i16 %{{.*}})
%0 = call i16 @llvm.umax.i16(i16 %a, i16 %b)
ret i16 %0
}

; CHECK-LABEL:test_umax_i32
; Function Attrs: noinline nounwind optnone
define noundef i32 @test_umax_i32(i32 noundef %a, i32 noundef %b) #0 {
define noundef i32 @test_umax_i32(i32 noundef %a, i32 noundef %b) {
entry:
; CHECK:call i32 @dx.op.binary.i32(i32 39, i32 %{{.*}}, i32 %{{.*}})
; CHECK: call i32 @dx.op.binary.i32(i32 39, i32 %{{.*}}, i32 %{{.*}})
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
ret i32 %0
}

; CHECK-LABEL:test_umax_i64
define noundef i64 @test_umax_i64(i64 noundef %a, i64 noundef %b) #0 {
define noundef i64 @test_umax_i64(i64 noundef %a, i64 noundef %b) {
entry:
; CHECK:call i64 @dx.op.binary.i64(i32 39, i64 %{{.*}}, i64 %{{.*}})
; CHECK: call i64 @dx.op.binary.i64(i32 39, i64 %{{.*}}, i64 %{{.*}})
%0 = call i64 @llvm.umax.i64(i64 %a, i64 %b)
ret i64 %0
}

; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.umax.i32(i32, i32) #1
declare i64 @llvm.umax.i64(i64, i64) #1

attributes #0 = { noinline nounwind }
attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn }
declare i16 @llvm.umax.i16(i16, i16)
declare i32 @llvm.umax.i32(i32, i32)
declare i64 @llvm.umax.i64(i64, i64)
31 changes: 31 additions & 0 deletions llvm/test/CodeGen/DirectX/umin.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s

; Make sure dxil operation function calls for umin are generated for i16/i32/i64.

; CHECK-LABEL:test_umin_i16
define noundef i16 @test_umin_i16(i16 noundef %a, i16 noundef %b) {
entry:
; CHECK: call i16 @dx.op.binary.i16(i32 40, i16 %{{.*}}, i16 %{{.*}})
%0 = call i16 @llvm.umin.i16(i16 %a, i16 %b)
ret i16 %0
}

; CHECK-LABEL:test_umin_i32
define noundef i32 @test_umin_i32(i32 noundef %a, i32 noundef %b) {
entry:
; CHECK: call i32 @dx.op.binary.i32(i32 40, i32 %{{.*}}, i32 %{{.*}})
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
ret i32 %0
}

; CHECK-LABEL:test_umin_i64
define noundef i64 @test_umin_i64(i64 noundef %a, i64 noundef %b) {
entry:
; CHECK: call i64 @dx.op.binary.i64(i32 40, i64 %{{.*}}, i64 %{{.*}})
%0 = call i64 @llvm.umin.i64(i64 %a, i64 %b)
ret i64 %0
}

declare i16 @llvm.umin.i16(i16, i16)
declare i32 @llvm.umin.i32(i32, i32)
declare i64 @llvm.umin.i64(i64, i64)
2 changes: 1 addition & 1 deletion llvm/test/Transforms/DFAJumpThreading/dfa-unfold-select.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=dfa-jump-threading %s | FileCheck %s
; RUN: opt -S -passes=dfa-jump-threading -dfa-early-exit-heuristic=false %s | FileCheck %s

; These tests check if selects are unfolded properly for jump threading
; opportunities. There are three different patterns to consider:
Expand Down
124 changes: 124 additions & 0 deletions llvm/test/Transforms/DFAJumpThreading/unpredictable-heuristic.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
; REQUIRES: asserts
; RUN: opt -S -passes=dfa-jump-threading %s -debug-only=dfa-jump-threading 2>&1 | FileCheck %s

; CHECK-COUNT-3: Exiting early due to unpredictability heuristic.

@.str.1 = private unnamed_addr constant [3 x i8] c"10\00", align 1
@.str.2 = private unnamed_addr constant [3 x i8] c"30\00", align 1
@.str.3 = private unnamed_addr constant [3 x i8] c"20\00", align 1
@.str.4 = private unnamed_addr constant [3 x i8] c"40\00", align 1

define void @test1(i32 noundef %num, i32 noundef %num2) {
entry:
br label %while.body

while.body: ; preds = %entry, %sw.epilog
%num.addr.0 = phi i32 [ %num, %entry ], [ %num.addr.1, %sw.epilog ]
switch i32 %num.addr.0, label %sw.default [
i32 10, label %sw.bb
i32 30, label %sw.bb1
i32 20, label %sw.bb2
i32 40, label %sw.bb3
]

sw.bb: ; preds = %while.body
%call.i = tail call i32 @bar(ptr noundef nonnull @.str.1)
br label %sw.epilog

sw.bb1: ; preds = %while.body
%call.i4 = tail call i32 @bar(ptr noundef nonnull @.str.2)
br label %sw.epilog

sw.bb2: ; preds = %while.body
%call.i5 = tail call i32 @bar(ptr noundef nonnull @.str.3)
br label %sw.epilog

sw.bb3: ; preds = %while.body
%call.i6 = tail call i32 @bar(ptr noundef nonnull @.str.4)
%call = tail call noundef i32 @foo()
%add = add nsw i32 %call, %num2
br label %sw.epilog

sw.default: ; preds = %while.body
ret void

sw.epilog: ; preds = %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb
%num.addr.1 = phi i32 [ %add, %sw.bb3 ], [ 40, %sw.bb2 ], [ 20, %sw.bb1 ], [ 30, %sw.bb ]
br label %while.body
}


define void @test2(i32 noundef %num, i32 noundef %num2) {
entry:
br label %while.body

while.body: ; preds = %entry, %sw.epilog
%num.addr.0 = phi i32 [ %num, %entry ], [ %num.addr.1, %sw.epilog ]
switch i32 %num.addr.0, label %sw.default [
i32 10, label %sw.epilog
i32 30, label %sw.bb1
i32 20, label %sw.bb2
i32 40, label %sw.bb3
]

sw.bb1: ; preds = %while.body
br label %sw.epilog

sw.bb2: ; preds = %while.body
br label %sw.epilog

sw.bb3: ; preds = %while.body
br label %sw.epilog

sw.default: ; preds = %while.body
ret void

sw.epilog: ; preds = %while.body, %sw.bb3, %sw.bb2, %sw.bb1
%.str.4.sink = phi ptr [ @.str.4, %sw.bb3 ], [ @.str.3, %sw.bb2 ], [ @.str.2, %sw.bb1 ], [ @.str.1, %while.body ]
%num.addr.1 = phi i32 [ %num2, %sw.bb3 ], [ 40, %sw.bb2 ], [ 20, %sw.bb1 ], [ 30, %while.body ]
%call.i6 = tail call i32 @bar(ptr noundef nonnull %.str.4.sink)
br label %while.body
}


define void @test3(i32 noundef %num, i32 noundef %num2) {
entry:
%add = add nsw i32 %num2, 40
br label %while.body

while.body: ; preds = %entry, %sw.epilog
%num.addr.0 = phi i32 [ %num, %entry ], [ %num.addr.1, %sw.epilog ]
switch i32 %num.addr.0, label %sw.default [
i32 10, label %sw.bb
i32 30, label %sw.bb1
i32 20, label %sw.bb2
i32 40, label %sw.bb3
]

sw.bb: ; preds = %while.body
%call.i = tail call i32 @bar(ptr noundef nonnull @.str.1)
br label %sw.epilog

sw.bb1: ; preds = %while.body
%call.i5 = tail call i32 @bar(ptr noundef nonnull @.str.2)
br label %sw.epilog

sw.bb2: ; preds = %while.body
%call.i6 = tail call i32 @bar(ptr noundef nonnull @.str.3)
br label %sw.epilog

sw.bb3: ; preds = %while.body
%call.i7 = tail call i32 @bar(ptr noundef nonnull @.str.4)
br label %sw.epilog

sw.default: ; preds = %while.body
ret void

sw.epilog: ; preds = %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb
%num.addr.1 = phi i32 [ %add, %sw.bb3 ], [ 40, %sw.bb2 ], [ 20, %sw.bb1 ], [ 30, %sw.bb ]
br label %while.body
}


declare noundef i32 @foo()
declare noundef i32 @bar(ptr nocapture noundef readonly)
Loading