diff options
| author | Vitaly Buka <vitalybuka@google.com> | 2024-07-26 11:43:08 -0700 |
|---|---|---|
| committer | Vitaly Buka <vitalybuka@google.com> | 2024-07-26 11:43:08 -0700 |
| commit | c6d8bf4b0543aa8c51e31f3fb7490809145651da (patch) | |
| tree | bf046e889802a7d06b171cfbc03562f790325032 | |
| parent | d683d378998c85c12d7f0549944f807bb44c7b76 (diff) | |
| parent | f8cd4c505fd9c0be329630b6b82e3a3ff53107cc (diff) | |
[𝘀𝗽𝗿] changes introduced through rebaseusers/vitalybuka/spr/main.instcombineasan-dont-speculate-loads-before-select-ptr-1
Created using spr 1.3.4
[skip ci]
36 files changed, 1279 insertions, 270 deletions
diff --git a/.github/workflows/release-asset-audit.yml b/.github/workflows/release-asset-audit.yml index fd42bc67b999..018c5d542f32 100644 --- a/.github/workflows/release-asset-audit.yml +++ b/.github/workflows/release-asset-audit.yml @@ -40,7 +40,7 @@ jobs: var fs = require('fs'); var body = '' if (fs.existsSync('./comment')) { - body = JSON.parse(fs.readFileSync('./comment')) + "\n\n"; + body = fs.readFileSync('./comment') + "\n\n"; } body = body + `\n\nhttps://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 286f319d41a2..4f1a916aad9d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -133,6 +133,8 @@ Improvements to Clang's diagnostics - Clang now diagnoses undefined behavior in constant expressions more consistently. This includes invalid shifts, and signed overflow in arithmetic. +- -Wdangling-assignment-gsl is enabled by default. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index beee2432fdb0..e768151ce23c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10131,7 +10131,7 @@ def warn_dangling_lifetime_pointer : Warning< InGroup<DanglingGsl>; def warn_dangling_lifetime_pointer_assignment : Warning<"object backing the " "pointer %0 will be destroyed at the end of the full-expression">, - InGroup<DanglingAssignmentGsl>, DefaultIgnore; + InGroup<DanglingAssignmentGsl>; def warn_new_dangling_initializer_list : Warning< "array backing " "%select{initializer list subobject of the allocated object|" diff --git a/libc/cmake/modules/LLVMLibCLibraryRules.cmake b/libc/cmake/modules/LLVMLibCLibraryRules.cmake index e677b4cd2c28..ad931d445720 100644 --- a/libc/cmake/modules/LLVMLibCLibraryRules.cmake +++ b/libc/cmake/modules/LLVMLibCLibraryRules.cmake @@ -111,19 +111,9 @@ function(add_bitcode_entrypoint_library target_name base_target_name) list(APPEND objects ${object}) endforeach() - set(output ${CMAKE_CURRENT_BINARY_DIR}/${target_name}.bc) - add_custom_command( - OUTPUT ${output} - COMMAND ${LIBC_LLVM_LINK} ${objects} -o ${output} - DEPENDS ${all_deps} ${base_target_name} - COMMENT "Linking LLVM-IR bitcode for ${base_target_name}" - COMMAND_EXPAND_LISTS - ) - add_custom_target(${target_name} DEPENDS ${output} ${all_deps}) - set_target_properties(${target_name} PROPERTIES TARGET_OBJECT ${output}) - if(TARGET llvm-link) - add_dependencies(${target_name} llvm-link) - endif() + add_executable(${target_name} ${objects}) + target_link_options(${target_name} PRIVATE + "-r" "-nostdlib" "-flto" "-Wl,--lto-emit-llvm" "-march= ") endfunction(add_bitcode_entrypoint_library) # A rule to build a library from a collection of entrypoint objects. diff --git a/libc/cmake/modules/prepare_libc_gpu_build.cmake b/libc/cmake/modules/prepare_libc_gpu_build.cmake index e2a0908023a0..f3537f2634e5 100644 --- a/libc/cmake/modules/prepare_libc_gpu_build.cmake +++ b/libc/cmake/modules/prepare_libc_gpu_build.cmake @@ -21,36 +21,10 @@ if(LIBC_TARGET_TRIPLE) set(CMAKE_REQUIRED_FLAGS "--target=${LIBC_TARGET_TRIPLE}") endif() if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nogpulib") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nogpulib -nostdlib") elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX) set(CMAKE_REQUIRED_FLAGS - "${CMAKE_REQUIRED_FLAGS} -flto -c -Wno-unused-command-line-argument") -endif() - -# Identify the program used to package multiple images into a single binary. -get_filename_component(compiler_path ${CMAKE_CXX_COMPILER} DIRECTORY) -if(TARGET clang-offload-packager) - get_target_property(LIBC_CLANG_OFFLOAD_PACKAGER clang-offload-packager LOCATION) -else() - find_program(LIBC_CLANG_OFFLOAD_PACKAGER - NAMES clang-offload-packager NO_DEFAULT_PATH - PATHS ${LLVM_BINARY_DIR}/bin ${compiler_path}) - if(NOT LIBC_CLANG_OFFLOAD_PACKAGER) - message(FATAL_ERROR "Cannot find the 'clang-offload-packager' for the GPU " - "build") - endif() -endif() - -# Identify llvm-link program so we can merge the output IR into a single blob. -if(TARGET llvm-link) - get_target_property(LIBC_LLVM_LINK llvm-link LOCATION) -else() - find_program(LIBC_LLVM_LINK - NAMES llvm-link NO_DEFAULT_PATH - PATHS ${LLVM_BINARY_DIR}/bin ${compiler_path}) - if(NOT LIBC_LLVM_LINK) - message(FATAL_ERROR "Cannot find 'llvm-link' for the GPU build") - endif() + "${CMAKE_REQUIRED_FLAGS} -flto -c -Wno-unused-command-line-argument -nostdlib") endif() # Optionally set up a job pool to limit the number of GPU tests run in parallel. diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt index 4b7cfc4b76e2..ce0b07fb6cb4 100644 --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -51,7 +51,7 @@ foreach(archive IN ZIP_LISTS PROPERTIES OUTPUT_NAME ${archive_1}.bc ) - list(APPEND added_gpu_bitcode_targets ${archive_1}bitcode) + list(APPEND added_bitcode_targets ${archive_1}bitcode) endif() endforeach() @@ -61,24 +61,13 @@ install( COMPONENT libc ) -if(LIBC_TARGET_OS_IS_GPU) - set(gpu_install_dir lib${LLVM_LIBDIR_SUFFIX}) - if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR) - set(gpu_install_dir lib${LLVM_LIBDIR_SUFFIX}/${LLVM_HOST_TRIPLE}) - endif() - install( - TARGETS ${added_gpu_archive_targets} - ARCHIVE DESTINATION ${gpu_install_dir} - COMPONENT libc +foreach(file ${added_bitcode_targets}) + install(FILES $<TARGET_FILE:${file}> + DESTINATION ${LIBC_INSTALL_LIBRARY_DIR} + RENAME $<TARGET_PROPERTY:${file},OUTPUT_NAME> + COMPONENT libc ) - foreach(file ${added_gpu_bitcode_targets}) - install(FILES $<TARGET_PROPERTY:${file},TARGET_OBJECT> - DESTINATION ${LIBC_INSTALL_LIBRARY_DIR} - RENAME $<TARGET_PROPERTY:${file},OUTPUT_NAME> - COMPONENT libc - ) - endforeach() -endif() +endforeach() if(NOT LIBC_TARGET_OS_IS_BAREMETAL) # For now we will disable libc-startup installation for baremetal. The @@ -93,6 +82,7 @@ endif() add_custom_target(install-libc DEPENDS ${added_archive_targets} + ${added_bitcode_targets} ${startup_target} ${header_install_target} COMMAND "${CMAKE_COMMAND}" @@ -100,6 +90,7 @@ add_custom_target(install-libc -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") add_custom_target(install-libc-stripped DEPENDS ${added_archive_targets} + ${added_bitcode_targets} ${startup_target} ${header_install_target} COMMAND "${CMAKE_COMMAND}" diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h index 156f1961151c..2849b82e1a3d 100644 --- a/libcxx/include/__atomic/atomic_ref.h +++ b/libcxx/include/__atomic/atomic_ref.h @@ -42,6 +42,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 +// These types are required to make __atomic_is_always_lock_free work across GCC and Clang. +// The purpose of this trick is to make sure that we provide an object with the correct alignment +// to __atomic_is_always_lock_free, since that answer depends on the alignment. +template <size_t _Alignment> +struct __alignment_checker_type { + alignas(_Alignment) char __data; +}; + +template <size_t _Alignment> +struct __get_aligner_instance { + static constexpr __alignment_checker_type<_Alignment> __instance{}; +}; + template <class _Tp> struct __atomic_ref_base { protected: @@ -105,7 +118,7 @@ public: // that the pointer is going to be aligned properly at runtime because that is a (checked) precondition // of atomic_ref's constructor. static constexpr bool is_always_lock_free = - __atomic_always_lock_free(sizeof(_Tp), reinterpret_cast<void*>(-required_alignment)); + __atomic_always_lock_free(sizeof(_Tp), &__get_aligner_instance<required_alignment>::__instance); _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return __atomic_is_lock_free(sizeof(_Tp), __ptr_); } diff --git a/libcxx/test/std/atomics/atomics.lockfree/is_always_lock_free.cpp b/libcxx/test/std/atomics/atomics.lockfree/is_always_lock_free.cpp new file mode 100644 index 000000000000..2dc7f5c76541 --- /dev/null +++ b/libcxx/test/std/atomics/atomics.lockfree/is_always_lock_free.cpp @@ -0,0 +1,165 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// <atomic> +// +// template <class T> +// class atomic; +// +// static constexpr bool is_always_lock_free; + +#include <atomic> +#include <cassert> +#include <cstddef> + +#include "test_macros.h" +#include "atomic_helpers.h" + +template <typename T> +void check_always_lock_free(std::atomic<T> const& a) { + using InfoT = LockFreeStatusInfo<T>; + + constexpr std::same_as<const bool> decltype(auto) is_always_lock_free = std::atomic<T>::is_always_lock_free; + + // If we know the status of T for sure, validate the exact result of the function. + if constexpr (InfoT::status_known) { + constexpr LockFreeStatus known_status = InfoT::value; + if constexpr (known_status == LockFreeStatus::always) { + static_assert(is_always_lock_free, "is_always_lock_free is inconsistent with known lock-free status"); + assert(a.is_lock_free() && "is_lock_free() is inconsistent with known lock-free status"); + } else if constexpr (known_status == LockFreeStatus::never) { + static_assert(!is_always_lock_free, "is_always_lock_free is inconsistent with known lock-free status"); + assert(!a.is_lock_free() && "is_lock_free() is inconsistent with known lock-free status"); + } else { + assert(a.is_lock_free() || !a.is_lock_free()); // This is kinda dumb, but we might as well call the function once. + } + } + + // In all cases, also sanity-check it based on the implication always-lock-free => lock-free. + if (is_always_lock_free) { + std::same_as<bool> decltype(auto) is_lock_free = a.is_lock_free(); + assert(is_lock_free); + } + ASSERT_NOEXCEPT(a.is_lock_free()); +} + +#define CHECK_ALWAYS_LOCK_FREE(T) \ + do { \ + typedef T type; \ + type obj{}; \ + std::atomic<type> a(obj); \ + check_always_lock_free(a); \ + } while (0) + +void test() { + char c = 'x'; + check_always_lock_free(std::atomic<char>(c)); + + int i = 0; + check_always_lock_free(std::atomic<int>(i)); + + float f = 0.f; + check_always_lock_free(std::atomic<float>(f)); + + int* p = &i; + check_always_lock_free(std::atomic<int*>(p)); + + CHECK_ALWAYS_LOCK_FREE(bool); + CHECK_ALWAYS_LOCK_FREE(char); + CHECK_ALWAYS_LOCK_FREE(signed char); + CHECK_ALWAYS_LOCK_FREE(unsigned char); +#if TEST_STD_VER > 17 && defined(__cpp_char8_t) + CHECK_ALWAYS_LOCK_FREE(char8_t); +#endif + CHECK_ALWAYS_LOCK_FREE(char16_t); + CHECK_ALWAYS_LOCK_FREE(char32_t); + CHECK_ALWAYS_LOCK_FREE(wchar_t); + CHECK_ALWAYS_LOCK_FREE(short); + CHECK_ALWAYS_LOCK_FREE(unsigned short); + CHECK_ALWAYS_LOCK_FREE(int); + CHECK_ALWAYS_LOCK_FREE(unsigned int); + CHECK_ALWAYS_LOCK_FREE(long); + CHECK_ALWAYS_LOCK_FREE(unsigned long); + CHECK_ALWAYS_LOCK_FREE(long long); + CHECK_ALWAYS_LOCK_FREE(unsigned long long); + CHECK_ALWAYS_LOCK_FREE(std::nullptr_t); + CHECK_ALWAYS_LOCK_FREE(void*); + CHECK_ALWAYS_LOCK_FREE(float); + CHECK_ALWAYS_LOCK_FREE(double); + CHECK_ALWAYS_LOCK_FREE(long double); +#if __has_attribute(vector_size) && defined(_LIBCPP_VERSION) + CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(1 * sizeof(int))))); + CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(2 * sizeof(int))))); + CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(4 * sizeof(int))))); + CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(16 * sizeof(int))))); + CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(32 * sizeof(int))))); + CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(1 * sizeof(float))))); + CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(2 * sizeof(float))))); + CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(4 * sizeof(float))))); + CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(16 * sizeof(float))))); + CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(32 * sizeof(float))))); + CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(1 * sizeof(double))))); + CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(2 * sizeof(double))))); + CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(4 * sizeof(double))))); + CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(16 * sizeof(double))))); + CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(32 * sizeof(double))))); +#endif // __has_attribute(vector_size) && defined(_LIBCPP_VERSION) + CHECK_ALWAYS_LOCK_FREE(struct Empty{}); + CHECK_ALWAYS_LOCK_FREE(struct OneInt { int i; }); + CHECK_ALWAYS_LOCK_FREE(struct IntArr2 { int i[2]; }); + CHECK_ALWAYS_LOCK_FREE(struct FloatArr3 { float i[3]; }); + CHECK_ALWAYS_LOCK_FREE(struct LLIArr2 { long long int i[2]; }); + CHECK_ALWAYS_LOCK_FREE(struct LLIArr4 { long long int i[4]; }); + CHECK_ALWAYS_LOCK_FREE(struct LLIArr8 { long long int i[8]; }); + CHECK_ALWAYS_LOCK_FREE(struct LLIArr16 { long long int i[16]; }); + CHECK_ALWAYS_LOCK_FREE(struct Padding { + char c; /* padding */ + long long int i; + }); + CHECK_ALWAYS_LOCK_FREE(union IntFloat { + int i; + float f; + }); + CHECK_ALWAYS_LOCK_FREE(enum class CharEnumClass : char{foo}); + + // C macro and static constexpr must be consistent. + enum class CharEnumClass : char { foo }; + static_assert(std::atomic<bool>::is_always_lock_free == (2 == ATOMIC_BOOL_LOCK_FREE), ""); + static_assert(std::atomic<char>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); + static_assert(std::atomic<CharEnumClass>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); + static_assert(std::atomic<signed char>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); + static_assert(std::atomic<unsigned char>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); +#if TEST_STD_VER > 17 && defined(__cpp_char8_t) + static_assert(std::atomic<char8_t>::is_always_lock_free == (2 == ATOMIC_CHAR8_T_LOCK_FREE), ""); +#endif + static_assert(std::atomic<char16_t>::is_always_lock_free == (2 == ATOMIC_CHAR16_T_LOCK_FREE), ""); + static_assert(std::atomic<char32_t>::is_always_lock_free == (2 == ATOMIC_CHAR32_T_LOCK_FREE), ""); + static_assert(std::atomic<wchar_t>::is_always_lock_free == (2 == ATOMIC_WCHAR_T_LOCK_FREE), ""); + static_assert(std::atomic<short>::is_always_lock_free == (2 == ATOMIC_SHORT_LOCK_FREE), ""); + static_assert(std::atomic<unsigned short>::is_always_lock_free == (2 == ATOMIC_SHORT_LOCK_FREE), ""); + static_assert(std::atomic<int>::is_always_lock_free == (2 == ATOMIC_INT_LOCK_FREE), ""); + static_assert(std::atomic<unsigned int>::is_always_lock_free == (2 == ATOMIC_INT_LOCK_FREE), ""); + static_assert(std::atomic<long>::is_always_lock_free == (2 == ATOMIC_LONG_LOCK_FREE), ""); + static_assert(std::atomic<unsigned long>::is_always_lock_free == (2 == ATOMIC_LONG_LOCK_FREE), ""); + static_assert(std::atomic<long long>::is_always_lock_free == (2 == ATOMIC_LLONG_LOCK_FREE), ""); + static_assert(std::atomic<unsigned long long>::is_always_lock_free == (2 == ATOMIC_LLONG_LOCK_FREE), ""); + static_assert(std::atomic<void*>::is_always_lock_free == (2 == ATOMIC_POINTER_LOCK_FREE), ""); + static_assert(std::atomic<std::nullptr_t>::is_always_lock_free == (2 == ATOMIC_POINTER_LOCK_FREE), ""); + +#if TEST_STD_VER >= 20 + static_assert(std::atomic_signed_lock_free::is_always_lock_free, ""); + static_assert(std::atomic_unsigned_lock_free::is_always_lock_free, ""); +#endif +} + +int main(int, char**) { + test(); + return 0; +} diff --git a/libcxx/test/std/atomics/atomics.lockfree/isalwayslockfree.pass.cpp b/libcxx/test/std/atomics/atomics.lockfree/isalwayslockfree.pass.cpp deleted file mode 100644 index 6d6e6477bc25..000000000000 --- a/libcxx/test/std/atomics/atomics.lockfree/isalwayslockfree.pass.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 - -// <atomic> - -// static constexpr bool is_always_lock_free; - -#include <atomic> -#include <cassert> -#include <cstddef> - -#include "test_macros.h" - -template <typename T> -void checkAlwaysLockFree() { - if (std::atomic<T>::is_always_lock_free) { - assert(std::atomic<T>().is_lock_free()); - } -} - -void run() -{ -// structs and unions can't be defined in the template invocation. -// Work around this with a typedef. -#define CHECK_ALWAYS_LOCK_FREE(T) \ - do { \ - typedef T type; \ - checkAlwaysLockFree<type>(); \ - } while (0) - - CHECK_ALWAYS_LOCK_FREE(bool); - CHECK_ALWAYS_LOCK_FREE(char); - CHECK_ALWAYS_LOCK_FREE(signed char); - CHECK_ALWAYS_LOCK_FREE(unsigned char); -#if TEST_STD_VER > 17 && defined(__cpp_char8_t) - CHECK_ALWAYS_LOCK_FREE(char8_t); -#endif - CHECK_ALWAYS_LOCK_FREE(char16_t); - CHECK_ALWAYS_LOCK_FREE(char32_t); - CHECK_ALWAYS_LOCK_FREE(wchar_t); - CHECK_ALWAYS_LOCK_FREE(short); - CHECK_ALWAYS_LOCK_FREE(unsigned short); - CHECK_ALWAYS_LOCK_FREE(int); - CHECK_ALWAYS_LOCK_FREE(unsigned int); - CHECK_ALWAYS_LOCK_FREE(long); - CHECK_ALWAYS_LOCK_FREE(unsigned long); - CHECK_ALWAYS_LOCK_FREE(long long); - CHECK_ALWAYS_LOCK_FREE(unsigned long long); - CHECK_ALWAYS_LOCK_FREE(std::nullptr_t); - CHECK_ALWAYS_LOCK_FREE(void*); - CHECK_ALWAYS_LOCK_FREE(float); - CHECK_ALWAYS_LOCK_FREE(double); - CHECK_ALWAYS_LOCK_FREE(long double); -#if __has_attribute(vector_size) && defined(_LIBCPP_VERSION) - CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(1 * sizeof(int))))); - CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(2 * sizeof(int))))); - CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(4 * sizeof(int))))); - CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(16 * sizeof(int))))); - CHECK_ALWAYS_LOCK_FREE(int __attribute__((vector_size(32 * sizeof(int))))); - CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(1 * sizeof(float))))); - CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(2 * sizeof(float))))); - CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(4 * sizeof(float))))); - CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(16 * sizeof(float))))); - CHECK_ALWAYS_LOCK_FREE(float __attribute__((vector_size(32 * sizeof(float))))); - CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(1 * sizeof(double))))); - CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(2 * sizeof(double))))); - CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(4 * sizeof(double))))); - CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(16 * sizeof(double))))); - CHECK_ALWAYS_LOCK_FREE(double __attribute__((vector_size(32 * sizeof(double))))); -#endif // __has_attribute(vector_size) && defined(_LIBCPP_VERSION) - CHECK_ALWAYS_LOCK_FREE(struct Empty {}); - CHECK_ALWAYS_LOCK_FREE(struct OneInt { int i; }); - CHECK_ALWAYS_LOCK_FREE(struct IntArr2 { int i[2]; }); - CHECK_ALWAYS_LOCK_FREE(struct FloatArr3 { float i[3]; }); - CHECK_ALWAYS_LOCK_FREE(struct LLIArr2 { long long int i[2]; }); - CHECK_ALWAYS_LOCK_FREE(struct LLIArr4 { long long int i[4]; }); - CHECK_ALWAYS_LOCK_FREE(struct LLIArr8 { long long int i[8]; }); - CHECK_ALWAYS_LOCK_FREE(struct LLIArr16 { long long int i[16]; }); - CHECK_ALWAYS_LOCK_FREE(struct Padding { char c; /* padding */ long long int i; }); - CHECK_ALWAYS_LOCK_FREE(union IntFloat { int i; float f; }); - CHECK_ALWAYS_LOCK_FREE(enum class CharEnumClass : char { foo }); - - // C macro and static constexpr must be consistent. - enum class CharEnumClass : char { foo }; - static_assert(std::atomic<bool>::is_always_lock_free == (2 == ATOMIC_BOOL_LOCK_FREE), ""); - static_assert(std::atomic<char>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); - static_assert(std::atomic<CharEnumClass>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); - static_assert(std::atomic<signed char>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); - static_assert(std::atomic<unsigned char>::is_always_lock_free == (2 == ATOMIC_CHAR_LOCK_FREE), ""); -#if TEST_STD_VER > 17 && defined(__cpp_char8_t) - static_assert(std::atomic<char8_t>::is_always_lock_free == (2 == ATOMIC_CHAR8_T_LOCK_FREE), ""); -#endif - static_assert(std::atomic<char16_t>::is_always_lock_free == (2 == ATOMIC_CHAR16_T_LOCK_FREE), ""); - static_assert(std::atomic<char32_t>::is_always_lock_free == (2 == ATOMIC_CHAR32_T_LOCK_FREE), ""); - static_assert(std::atomic<wchar_t>::is_always_lock_free == (2 == ATOMIC_WCHAR_T_LOCK_FREE), ""); - static_assert(std::atomic<short>::is_always_lock_free == (2 == ATOMIC_SHORT_LOCK_FREE), ""); - static_assert(std::atomic<unsigned short>::is_always_lock_free == (2 == ATOMIC_SHORT_LOCK_FREE), ""); - static_assert(std::atomic<int>::is_always_lock_free == (2 == ATOMIC_INT_LOCK_FREE), ""); - static_assert(std::atomic<unsigned int>::is_always_lock_free == (2 == ATOMIC_INT_LOCK_FREE), ""); - static_assert(std::atomic<long>::is_always_lock_free == (2 == ATOMIC_LONG_LOCK_FREE), ""); - static_assert(std::atomic<unsigned long>::is_always_lock_free == (2 == ATOMIC_LONG_LOCK_FREE), ""); - static_assert(std::atomic<long long>::is_always_lock_free == (2 == ATOMIC_LLONG_LOCK_FREE), ""); - static_assert(std::atomic<unsigned long long>::is_always_lock_free == (2 == ATOMIC_LLONG_LOCK_FREE), ""); - static_assert(std::atomic<void*>::is_always_lock_free == (2 == ATOMIC_POINTER_LOCK_FREE), ""); - static_assert(std::atomic<std::nullptr_t>::is_always_lock_free == (2 == ATOMIC_POINTER_LOCK_FREE), ""); - -#if TEST_STD_VER >= 20 - static_assert(std::atomic_signed_lock_free::is_always_lock_free, ""); - static_assert(std::atomic_unsigned_lock_free::is_always_lock_free, ""); -#endif -} - -int main(int, char**) { run(); return 0; } diff --git a/libcxx/test/std/atomics/atomics.ref/is_always_lock_free.pass.cpp b/libcxx/test/std/atomics/atomics.ref/is_always_lock_free.pass.cpp index 94f65e3b4b66..acdbf63a24d8 100644 --- a/libcxx/test/std/atomics/atomics.ref/is_always_lock_free.pass.cpp +++ b/libcxx/test/std/atomics/atomics.ref/is_always_lock_free.pass.cpp @@ -9,7 +9,10 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // <atomic> - +// +// template <class T> +// class atomic_ref; +// // static constexpr bool is_always_lock_free; // bool is_lock_free() const noexcept; @@ -18,10 +21,29 @@ #include <concepts> #include "test_macros.h" +#include "atomic_helpers.h" template <typename T> -void check_always_lock_free(std::atomic_ref<T> const a) { - std::same_as<const bool> decltype(auto) is_always_lock_free = std::atomic_ref<T>::is_always_lock_free; +void check_always_lock_free(std::atomic_ref<T> const& a) { + using InfoT = LockFreeStatusInfo<T>; + + constexpr std::same_as<const bool> decltype(auto) is_always_lock_free = std::atomic_ref<T>::is_always_lock_free; + + // If we know the status of T for sure, validate the exact result of the function. + if constexpr (InfoT::status_known) { + constexpr LockFreeStatus known_status = InfoT::value; + if constexpr (known_status == LockFreeStatus::always) { + static_assert(is_always_lock_free, "is_always_lock_free is inconsistent with known lock-free status"); + assert(a.is_lock_free() && "is_lock_free() is inconsistent with known lock-free status"); + } else if constexpr (known_status == LockFreeStatus::never) { + static_assert(!is_always_lock_free, "is_always_lock_free is inconsistent with known lock-free status"); + assert(!a.is_lock_free() && "is_lock_free() is inconsistent with known lock-free status"); + } else { + assert(a.is_lock_free() || !a.is_lock_free()); // This is kinda dumb, but we might as well call the function once. + } + } + + // In all cases, also sanity-check it based on the implication always-lock-free => lock-free. if (is_always_lock_free) { std::same_as<bool> decltype(auto) is_lock_free = a.is_lock_free(); assert(is_lock_free); @@ -33,10 +55,14 @@ void check_always_lock_free(std::atomic_ref<T> const a) { do { \ typedef T type; \ type obj{}; \ - check_always_lock_free(std::atomic_ref<type>(obj)); \ + std::atomic_ref<type> a(obj); \ + check_always_lock_free(a); \ } while (0) void test() { + char c = 'x'; + check_always_lock_free(std::atomic_ref<char>(c)); + int i = 0; check_always_lock_free(std::atomic_ref<int>(i)); diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_double.hex.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_double.hex.pass.cpp index 21efa978abdc..946c26398fcb 100644 --- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_double.hex.pass.cpp +++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_double.hex.pass.cpp @@ -12,10 +12,7 @@ // iter_type put(iter_type s, ios_base& iob, char_type fill, double v) const; -// With the Microsoft UCRT, printf("%a", 0.0) produces "0x0.0000000000000p+0" -// while other C runtimes produce just "0x0p+0". -// https://developercommunity.visualstudio.com/t/Printf-formatting-of-float-as-hex-prints/1660844 -// XFAIL: msvc +// XFAIL: win32-broken-printf-a-precision // XFAIL: LIBCXX-AIX-FIXME diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.hex.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.hex.pass.cpp index c97c9a0c4036..a195c34e5f8e 100644 --- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.hex.pass.cpp +++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long_double.hex.pass.cpp @@ -12,10 +12,7 @@ // iter_type put(iter_type s, ios_base& iob, char_type fill, long double v) const; -// With the Microsoft UCRT, printf("%a", 0.0) produces "0x0.0000000000000p+0" -// while other C runtimes produce just "0x0p+0". -// https://developercommunity.visualstudio.com/t/Printf-formatting-of-float-as-hex-prints/1660844 -// XFAIL: msvc +// XFAIL: win32-broken-printf-a-precision // XFAIL: LIBCXX-AIX-FIXME diff --git a/libcxx/test/support/atomic_helpers.h b/libcxx/test/support/atomic_helpers.h index 0266a0961067..d2f2b751cb47 100644 --- a/libcxx/test/support/atomic_helpers.h +++ b/libcxx/test/support/atomic_helpers.h @@ -11,9 +11,112 @@ #include <cassert> #include <cstdint> +#include <cstddef> +#include <type_traits> #include "test_macros.h" +#if defined(TEST_COMPILER_CLANG) +# define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE +# define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE +# define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE +# define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE +# define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE +# define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE +#elif defined(TEST_COMPILER_GCC) +# define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE +# define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE +# define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE +# define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE +# define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE +# define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE +#elif TEST_COMPILER_MSVC +// This is lifted from STL/stl/inc/atomic on github for the purposes of +// keeping the tests compiling for MSVC's STL. It's not a perfect solution +// but at least the tests will keep running. +// +// Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free. +template <class T, size_t Size = sizeof(T)> +constexpr bool msvc_is_lock_free_macro_value() { + return (Size <= 8 && (Size & Size - 1) == 0) ? 2 : 0; +} +# define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>() +# define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>() +# define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>() +# define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>() +# define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>() +# define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>() +#else +# error "Unknown compiler" +#endif + +#ifdef TEST_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wc++11-extensions" +#endif + +enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 }; + +// We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required +// for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type +// aligned to at least its size is going to be atomic if there exists atomic operations for that size at all, +// which is true on most platforms. This technically reduces our test coverage in the sense that if a type has +// an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report +// that we don't know whether it is lockfree or not. +#define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T)) + +template <class T> +struct LockFreeStatusInfo { + static const LockFreeStatus value = LockFreeStatus( + COMPARE_TYPES(T, char) + ? TEST_ATOMIC_CHAR_LOCK_FREE + : (COMPARE_TYPES(T, short) + ? TEST_ATOMIC_SHORT_LOCK_FREE + : (COMPARE_TYPES(T, int) + ? TEST_ATOMIC_INT_LOCK_FREE + : (COMPARE_TYPES(T, long) + ? TEST_ATOMIC_LONG_LOCK_FREE + : (COMPARE_TYPES(T, long long) + ? TEST_ATOMIC_LLONG_LOCK_FREE + : (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1)))))); + + static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown; +}; + +#undef COMPARE_TYPES + +// This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test. +#if TEST_STD_VER >= 11 +static_assert(LockFreeStatusInfo<char>::status_known, ""); +static_assert(LockFreeStatusInfo<short>::status_known, ""); +static_assert(LockFreeStatusInfo<int>::status_known, ""); +static_assert(LockFreeStatusInfo<long>::status_known, ""); +static_assert(LockFreeStatusInfo<void*>::status_known, ""); + +// long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is +// 8 bytes. In that case, atomics may or may not be lockfree based on their address. +static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, ""); + +// Those should always be lock free: hardcode some expected values to make sure our tests are actually +// testing something meaningful. +static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, ""); +static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, ""); +static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, ""); +#endif + +// These macros are somewhat suprising to use, since they take the values 0, 1, or 2. +// To make the tests clearer, get rid of them in preference of LockFreeStatusInfo. +#undef TEST_ATOMIC_CHAR_LOCK_FREE +#undef TEST_ATOMIC_SHORT_LOCK_FREE +#undef TEST_ATOMIC_INT_LOCK_FREE +#undef TEST_ATOMIC_LONG_LOCK_FREE +#undef TEST_ATOMIC_LLONG_LOCK_FREE +#undef TEST_ATOMIC_POINTER_LOCK_FREE + +#ifdef TEST_COMPILER_CLANG +# pragma clang diagnostic pop +#endif + struct UserAtomicType { int i; diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py index e978875d543f..97cdb0349885 100644 --- a/libcxx/utils/libcxx/test/features.py +++ b/libcxx/utils/libcxx/test/features.py @@ -263,6 +263,26 @@ DEFAULT_FEATURES = [ """, ), ), + # Check for a Windows UCRT bug (not fixed upstream yet). + # With UCRT, printf("%a", 0.0) produces "0x0.0000000000000p+0", + # while other C runtimes produce just "0x0p+0". + # https://developercommunity.visualstudio.com/t/Printf-formatting-of-float-as-hex-prints/1660844 + Feature( + name="win32-broken-printf-a-precision", + when=lambda cfg: "_WIN32" in compilerMacros(cfg) + and not programSucceeds( + cfg, + """ + #include <stdio.h> + #include <string.h> + int main(int, char**) { + char buf[100]; + snprintf(buf, sizeof(buf), "%a", 0.0); + return strcmp(buf, "0x0p+0"); + } + """, + ), + ), # Check for Glibc < 2.27, where the ru_RU.UTF-8 locale had # mon_decimal_point == ".", which our tests don't handle. Feature( diff --git a/libcxxabi/test/test_demangle.pass.cpp b/libcxxabi/test/test_demangle.pass.cpp index fe5598991b83..ab783cf9d96f 100644 --- a/libcxxabi/test/test_demangle.pass.cpp +++ b/libcxxabi/test/test_demangle.pass.cpp @@ -17,6 +17,8 @@ // 80-bit format, and this demangling test is failing on it. // XFAIL: LIBCXX-ANDROID-FIXME && target={{i686|x86_64}}-{{.+}}-android{{.*}} +// XFAIL: win32-broken-printf-a-precision + #include "support/timer.h" #include <algorithm> #include <cassert> diff --git a/lldb/packages/Python/lldbsuite/test/lldbplatformutil.py b/lldb/packages/Python/lldbsuite/test/lldbplatformutil.py index e3c6fd1a9956..602e15d207e9 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbplatformutil.py +++ b/lldb/packages/Python/lldbsuite/test/lldbplatformutil.py @@ -266,17 +266,13 @@ def getCompiler(): return module.getCompiler() -def getCompilerBinary(): - """Returns the compiler binary the test suite is running with.""" - return getCompiler().split()[0] - - def getCompilerVersion(): """Returns a string that represents the compiler version. Supports: llvm, clang. """ - compiler = getCompilerBinary() - version_output = subprocess.check_output([compiler, "--version"], errors="replace") + version_output = subprocess.check_output( + [getCompiler(), "--version"], errors="replace" + ) m = re.search("version ([0-9.]+)", version_output) if m: return m.group(1) diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index 5e50b0c14588..f97c41d867e7 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -1379,10 +1379,6 @@ class Base(unittest.TestCase): """Returns the compiler in effect the test suite is running with.""" return lldbplatformutil.getCompiler() - def getCompilerBinary(self): - """Returns the compiler binary the test suite is running with.""" - return lldbplatformutil.getCompilerBinary() - def getCompilerVersion(self): """Returns a string that represents the compiler version. Supports: llvm, clang. diff --git a/llvm/include/llvm/Analysis/Loads.h b/llvm/include/llvm/Analysis/Loads.h index 33e817828b75..1f01ff7027fa 100644 --- a/llvm/include/llvm/Analysis/Loads.h +++ b/llvm/include/llvm/Analysis/Loads.h @@ -69,8 +69,7 @@ bool isDereferenceableAndAlignedPointer(const Value *V, Align Alignment, /// quick local scan of the basic block containing ScanFrom, to determine if /// the address is already accessed. bool isSafeToLoadUnconditionally(Value *V, Align Alignment, const APInt &Size, - const DataLayout &DL, - Instruction *ScanFrom = nullptr, + const DataLayout &DL, Instruction *ScanFrom, AssumptionCache *AC = nullptr, const DominatorTree *DT = nullptr, const TargetLibraryInfo *TLI = nullptr); @@ -100,12 +99,18 @@ bool isDereferenceableReadOnlyLoop(Loop *L, ScalarEvolution *SE, /// quick local scan of the basic block containing ScanFrom, to determine if /// the address is already accessed. bool isSafeToLoadUnconditionally(Value *V, Type *Ty, Align Alignment, - const DataLayout &DL, - Instruction *ScanFrom = nullptr, + const DataLayout &DL, Instruction *ScanFrom, AssumptionCache *AC = nullptr, const DominatorTree *DT = nullptr, const TargetLibraryInfo *TLI = nullptr); +/// Return true if speculation of the given load must be suppressed to avoid +/// ordering or interfering with an active sanitizer. If not suppressed, +/// dereferenceability and alignment must be proven separately. Note: This +/// is only needed for raw reasoning; if you use the interface below +/// (isSafeToSpeculativelyExecute), this is handled internally. +bool mustSuppressSpeculation(const LoadInst &LI); + /// The default number of maximum instructions to scan in the block, used by /// FindAvailableLoadedValue(). extern cl::opt<unsigned> DefMaxInstsToScan; diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h index 5ef6e4348390..96fa16970584 100644 --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -792,13 +792,6 @@ bool onlyUsedByLifetimeMarkers(const Value *V); /// droppable instructions. bool onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V); -/// Return true if speculation of the given load must be suppressed to avoid -/// ordering or interfering with an active sanitizer. If not suppressed, -/// dereferenceability and alignment must be proven separately. Note: This -/// is only needed for raw reasoning; if you use the interface below -/// (isSafeToSpeculativelyExecute), this is handled internally. -bool mustSuppressSpeculation(const LoadInst &LI); - /// Return true if the instruction does not have any effects besides /// calculating the result and does not have undefined behavior. /// diff --git a/llvm/include/llvm/SandboxIR/SandboxIR.h b/llvm/include/llvm/SandboxIR/SandboxIR.h index 6c04c92e3e70..2678ee0f4f90 100644 --- a/llvm/include/llvm/SandboxIR/SandboxIR.h +++ b/llvm/include/llvm/SandboxIR/SandboxIR.h @@ -18,13 +18,19 @@ // // namespace sandboxir { // -// +- Argument +- BinaryOperator -// | | -// Value -+- BasicBlock +- BranchInst -// | | -// +- Function +- Constant +- CastInst -// | | | -// +- User ------+- Instruction -+- CallInst +// Value -+- Argument +// | +// +- BasicBlock +// | +// +- User ------+- Constant ------ Function +// | +// +- Instruction -+- BinaryOperator +// | +// +- BranchInst +// | +// +- CastInst +// | +// +- CallBase ----- CallInst // | // +- CmpInst // | @@ -82,6 +88,8 @@ class ReturnInst; class StoreInst; class User; class Value; +class CallBase; +class CallInst; /// Iterator for the `Use` edges of a User's operands. /// \Returns the operand `Use` when dereferenced. @@ -103,12 +111,20 @@ public: OperandUseIterator() = default; value_type operator*() const; OperandUseIterator &operator++(); + OperandUseIterator operator++(int) { + auto Copy = *this; + this->operator++(); + return Copy; + } bool operator==(const OperandUseIterator &Other) const { return Use == Other.Use; } bool operator!=(const OperandUseIterator &Other) const { return !(*this == Other); } + OperandUseIterator operator+(unsigned Num) const; + OperandUseIterator operator-(unsigned Num) const; + int operator-(const OperandUseIterator &Other) const; }; /// Iterator for the `Use` edges of a Value's users. @@ -135,6 +151,7 @@ public: bool operator!=(const UserUseIterator &Other) const { return !(*this == Other); } + const sandboxir::Use &getUse() const { return Use; } }; /// A SandboxIR Value has users. This is the base class. @@ -184,6 +201,8 @@ protected: friend class LoadInst; // For getting `Val`. friend class StoreInst; // For getting `Val`. friend class ReturnInst; // For getting `Val`. + friend class CallBase; // For getting `Val`. + friend class CallInst; // For getting `Val`. /// All values point to the context. Context &Ctx; @@ -417,7 +436,10 @@ public: class Constant : public sandboxir::User { Constant(llvm::Constant *C, sandboxir::Context &SBCtx) : sandboxir::User(ClassID::Constant, C, SBCtx) {} - friend class Context; // For constructor. + Constant(ClassID ID, llvm::Constant *C, sandboxir::Context &SBCtx) + : sandboxir::User(ID, C, SBCtx) {} + friend class Function; // For constructor + friend class Context; // For constructor. Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final { return getOperandUseDefault(OpIdx, Verify); } @@ -435,7 +457,7 @@ public: return getUseOperandNoDefault(Use); } #ifndef NDEBUG - void verify() const final { + void verify() const override { assert(isa<llvm::Constant>(Val) && "Expected Constant!"); } friend raw_ostream &operator<<(raw_ostream &OS, @@ -518,6 +540,7 @@ protected: friend class LoadInst; // For getTopmostLLVMInstruction(). friend class StoreInst; // For getTopmostLLVMInstruction(). friend class ReturnInst; // For getTopmostLLVMInstruction(). + friend class CallInst; // For getTopmostLLVMInstruction(). /// \Returns the LLVM IR Instructions that this SandboxIR maps to in program /// order. @@ -835,6 +858,177 @@ public: #endif }; +class CallBase : public Instruction { + CallBase(ClassID ID, Opcode Opc, llvm::Instruction *I, Context &Ctx) + : Instruction(ID, Opc, I, Ctx) {} + friend class CallInst; // For constructor. + +public: + static bool classof(const Value *From) { + auto Opc = From->getSubclassID(); + return Opc == Instruction::ClassID::Call || + Opc == Instruction::ClassID::Invoke || + Opc == Instruction::ClassID::CallBr; + } + + FunctionType *getFunctionType() const { + return cast<llvm::CallBase>(Val)->getFunctionType(); + } + + op_iterator data_operands_begin() { return op_begin(); } + const_op_iterator data_operands_begin() const { + return const_cast<CallBase *>(this)->data_operands_begin(); + } + op_iterator data_operands_end() { + auto *LLVMCB = cast<llvm::CallBase>(Val); + auto Dist = LLVMCB->data_operands_end() - LLVMCB->data_operands_begin(); + return op_begin() + Dist; + } + const_op_iterator data_operands_end() const { + auto *LLVMCB = cast<llvm::CallBase>(Val); + auto Dist = LLVMCB->data_operands_end() - LLVMCB->data_operands_begin(); + return op_begin() + Dist; + } + iterator_range<op_iterator> data_ops() { + return make_range(data_operands_begin(), data_operands_end()); + } + iterator_range<const_op_iterator> data_ops() const { + return make_range(data_operands_begin(), data_operands_end()); + } + bool data_operands_empty() const { + return data_operands_end() == data_operands_begin(); + } + unsigned data_operands_size() const { + return std::distance(data_operands_begin(), data_operands_end()); + } + bool isDataOperand(Use U) const { + assert(this == U.getUser() && + "Only valid to query with a use of this instruction!"); + return cast<llvm::CallBase>(Val)->isDataOperand(U.LLVMUse); + } + unsigned getDataOperandNo(Use U) const { + assert(isDataOperand(U) && "Data operand # out of range!"); + return cast<llvm::CallBase>(Val)->getDataOperandNo(U.LLVMUse); + } + + /// Return the total number operands (not operand bundles) used by + /// every operand bundle in this OperandBundleUser. + unsigned getNumTotalBundleOperands() const { + return cast<llvm::CallBase>(Val)->getNumTotalBundleOperands(); + } + + op_iterator arg_begin() { return op_begin(); } + const_op_iterator arg_begin() const { return op_begin(); } + op_iterator arg_end() { + return data_operands_end() - getNumTotalBundleOperands(); + } + const_op_iterator arg_end() const { + return const_cast<CallBase *>(this)->arg_end(); + } + iterator_range<op_iterator> args() { + return make_range(arg_begin(), arg_end()); + } + iterator_range<const_op_iterator> args() const { + return make_range(arg_begin(), arg_end()); + } + bool arg_empty() const { return arg_end() == arg_begin(); } + unsigned arg_size() const { return arg_end() - arg_begin(); } + + Value *getArgOperand(unsigned OpIdx) const { + assert(OpIdx < arg_size() && "Out of bounds!"); + return getOperand(OpIdx); + } + void setArgOperand(unsigned OpIdx, Value *NewOp) { + assert(OpIdx < arg_size() && "Out of bounds!"); + setOperand(OpIdx, NewOp); + } + + Use getArgOperandUse(unsigned Idx) const { + assert(Idx < arg_size() && "Out of bounds!"); + return getOperandUse(Idx); + } + Use getArgOperandUse(unsigned Idx) { + assert(Idx < arg_size() && "Out of bounds!"); + return getOperandUse(Idx); + } + + bool isArgOperand(Use U) const { + return cast<llvm::CallBase>(Val)->isArgOperand(U.LLVMUse); + } + unsigned getArgOperandNo(Use U) const { + return cast<llvm::CallBase>(Val)->getArgOperandNo(U.LLVMUse); + } + bool hasArgument(const Value *V) const { return is_contained(args(), V); } + + Value *getCalledOperand() const; + Use getCalledOperandUse() const; + + Function *getCalledFunction() const; + bool isIndirectCall() const { + return cast<llvm::CallBase>(Val)->isIndirectCall(); + } + bool isCallee(Use U) const { + return cast<llvm::CallBase>(Val)->isCallee(U.LLVMUse); + } + Function *getCaller(); + const Function *getCaller() const { + return const_cast<CallBase *>(this)->getCaller(); + } + bool isMustTailCall() const { + return cast<llvm::CallBase>(Val)->isMustTailCall(); + } + bool isTailCall() const { return cast<llvm::CallBase>(Val)->isTailCall(); } + Intrinsic::ID getIntrinsicID() const { + return cast<llvm::CallBase>(Val)->getIntrinsicID(); + } + void setCalledOperand(Value *V) { getCalledOperandUse().set(V); } + void setCalledFunction(Function *F); + CallingConv::ID getCallingConv() const { + return cast<llvm::CallBase>(Val)->getCallingConv(); + } + bool isInlineAsm() const { return cast<llvm::CallBase>(Val)->isInlineAsm(); } +}; + +class CallInst final : public CallBase { + /// Use Context::createCallInst(). Don't call the + /// constructor directly. + CallInst(llvm::Instruction *I, Context &Ctx) + : CallBase(ClassID::Call, Opcode::Call, I, Ctx) {} + friend class Context; // For accessing the constructor in + // create*() + Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final { + return getOperandUseDefault(OpIdx, Verify); + } + SmallVector<llvm::Instruction *, 1> getLLVMInstrs() const final { + return {cast<llvm::Instruction>(Val)}; + } + +public: + static CallInst *create(FunctionType *FTy, Value *Func, + ArrayRef<Value *> Args, BBIterator WhereIt, + BasicBlock *WhereBB, Context &Ctx, + const Twine &NameStr = ""); + static CallInst *create(FunctionType *FTy, Value *Func, + ArrayRef<Value *> Args, Instruction *InsertBefore, + Context &Ctx, const Twine &NameStr = ""); + static CallInst *create(FunctionType *FTy, Value *Func, + ArrayRef<Value *> Args, BasicBlock *InsertAtEnd, + Context &Ctx, const Twine &NameStr = ""); + + static bool classof(const Value *From) { + return From->getSubclassID() == ClassID::Call; + } + unsigned getUseOperandNo(const Use &Use) const final { + return getUseOperandNoDefault(Use); + } + unsigned getNumOfIRInstrs() const final { return 1u; } +#ifndef NDEBUG + void verify() const final {} + void dump(raw_ostream &OS) const override; + LLVM_DUMP_METHOD void dump() const override; +#endif +}; + /// An LLLVM Instruction that has no SandboxIR equivalent class gets mapped to /// an OpaqueInstr. class OpaqueInst : public sandboxir::Instruction { @@ -983,6 +1177,8 @@ protected: friend StoreInst; // For createStoreInst() ReturnInst *createReturnInst(llvm::ReturnInst *I); friend ReturnInst; // For createReturnInst() + CallInst *createCallInst(llvm::CallInst *I); + friend CallInst; // For createCallInst() public: Context(LLVMContext &LLVMCtx) @@ -1010,7 +1206,7 @@ public: size_t getNumValues() const { return LLVMValueToValueMap.size(); } }; -class Function : public sandboxir::Value { +class Function : public Constant { /// Helper for mapped_iterator. struct LLVMBBToBB { Context &Ctx; @@ -1021,7 +1217,7 @@ class Function : public sandboxir::Value { }; /// Use Context::createFunction() instead. Function(llvm::Function *F, sandboxir::Context &Ctx) - : sandboxir::Value(ClassID::Function, F, Ctx) {} + : Constant(ClassID::Function, F, Ctx) {} friend class Context; // For constructor. public: @@ -1047,6 +1243,9 @@ public: LLVMBBToBB BBGetter(Ctx); return iterator(cast<llvm::Function>(Val)->end(), BBGetter); } + FunctionType *getFunctionType() const { + return cast<llvm::Function>(Val)->getFunctionType(); + } #ifndef NDEBUG void verify() const final { diff --git a/llvm/include/llvm/SandboxIR/SandboxIRValues.def b/llvm/include/llvm/SandboxIR/SandboxIRValues.def index f3d616774b3f..5f6fc84fc2e0 100644 --- a/llvm/include/llvm/SandboxIR/SandboxIRValues.def +++ b/llvm/include/llvm/SandboxIR/SandboxIRValues.def @@ -30,6 +30,9 @@ DEF_INSTR(Br, OP(Br), BranchInst) DEF_INSTR(Load, OP(Load), LoadInst) DEF_INSTR(Store, OP(Store), StoreInst) DEF_INSTR(Ret, OP(Ret), ReturnInst) +DEF_INSTR(Call, OP(Call), CallInst) +DEF_INSTR(Invoke, OP(Invoke), InvokeInst) +DEF_INSTR(CallBr, OP(CallBr), CallBrInst) #ifdef DEF_VALUE #undef DEF_VALUE diff --git a/llvm/include/llvm/SandboxIR/Use.h b/llvm/include/llvm/SandboxIR/Use.h index 03cbfe6cb044..d30eb9059429 100644 --- a/llvm/include/llvm/SandboxIR/Use.h +++ b/llvm/include/llvm/SandboxIR/Use.h @@ -21,6 +21,7 @@ namespace llvm::sandboxir { class Context; class Value; class User; +class CallBase; /// Represents a Def-use/Use-def edge in SandboxIR. /// NOTE: Unlike llvm::Use, this is not an integral part of the use-def chains. @@ -40,6 +41,7 @@ class Use { friend class User; // For constructor friend class OperandUseIterator; // For constructor friend class UserUseIterator; // For accessing members + friend class CallBase; // For LLVMUse public: operator Value *() const { return get(); } diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp index 61c6aa5e5a3e..1704f0db4c59 100644 --- a/llvm/lib/Analysis/Loads.cpp +++ b/llvm/lib/Analysis/Loads.cpp @@ -345,6 +345,19 @@ bool llvm::isDereferenceableAndAlignedInLoop(LoadInst *LI, Loop *L, HeaderFirstNonPHI, AC, &DT); } +static bool suppressSpeculativeLoadForSanitizers(const Instruction &CtxI) { + const Function &F = *CtxI.getFunction(); + // Speculative load may create a race that did not exist in the source. + return F.hasFnAttribute(Attribute::SanitizeThread) || + // Speculative load may load data from dirty regions. + F.hasFnAttribute(Attribute::SanitizeAddress) || + F.hasFnAttribute(Attribute::SanitizeHWAddress); +} + +bool llvm::mustSuppressSpeculation(const LoadInst &LI) { + return !LI.isUnordered() || suppressSpeculativeLoadForSanitizers(LI); +} + /// Check if executing a load of this pointer value cannot trap. /// /// If DT and ScanFrom are specified this method performs context-sensitive diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index bfd26fadd237..497f6eafd22d 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -6798,17 +6798,6 @@ bool llvm::onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V) { V, /* AllowLifetime */ true, /* AllowDroppable */ true); } -bool llvm::mustSuppressSpeculation(const LoadInst &LI) { - if (!LI.isUnordered()) - return true; - const Function &F = *LI.getFunction(); - // Speculative load may create a race that did not exist in the source. - return F.hasFnAttribute(Attribute::SanitizeThread) || - // Speculative load may load data from dirty regions. - F.hasFnAttribute(Attribute::SanitizeAddress) || - F.hasFnAttribute(Attribute::SanitizeHWAddress); -} - bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst, const Instruction *CtxI, AssumptionCache *AC, diff --git a/llvm/lib/SandboxIR/SandboxIR.cpp b/llvm/lib/SandboxIR/SandboxIR.cpp index ceadb34f53ea..da482765c7d1 100644 --- a/llvm/lib/SandboxIR/SandboxIR.cpp +++ b/llvm/lib/SandboxIR/SandboxIR.cpp @@ -16,7 +16,12 @@ using namespace llvm::sandboxir; Value *Use::get() const { return Ctx->getValue(LLVMUse->get()); } -void Use::set(Value *V) { LLVMUse->set(V->Val); } +void Use::set(Value *V) { + auto &Tracker = Ctx->getTracker(); + if (Tracker.isTracking()) + Tracker.track(std::make_unique<UseSet>(*this, Tracker)); + LLVMUse->set(V->Val); +} unsigned Use::getOperandNo() const { return Usr->getUseOperandNo(*this); } @@ -84,6 +89,25 @@ UserUseIterator &UserUseIterator::operator++() { return *this; } +OperandUseIterator OperandUseIterator::operator+(unsigned Num) const { + sandboxir::Use U = Use.getUser()->getOperandUseInternal( + Use.getOperandNo() + Num, /*Verify=*/true); + return OperandUseIterator(U); +} + +OperandUseIterator OperandUseIterator::operator-(unsigned Num) const { + assert(Use.getOperandNo() >= Num && "Out of bounds!"); + sandboxir::Use U = Use.getUser()->getOperandUseInternal( + Use.getOperandNo() - Num, /*Verify=*/true); + return OperandUseIterator(U); +} + +int OperandUseIterator::operator-(const OperandUseIterator &Other) const { + int ThisOpNo = Use.getOperandNo(); + int OtherOpNo = Other.Use.getOperandNo(); + return ThisOpNo - OtherOpNo; +} + Value::Value(ClassID SubclassID, llvm::Value *Val, Context &Ctx) : SubclassID(SubclassID), Val(Val), Ctx(Ctx) { #ifndef NDEBUG @@ -713,6 +737,78 @@ void ReturnInst::dump() const { dump(dbgs()); dbgs() << "\n"; } +#endif // NDEBUG + +Value *CallBase::getCalledOperand() const { + return Ctx.getValue(cast<llvm::CallBase>(Val)->getCalledOperand()); +} + +Use CallBase::getCalledOperandUse() const { + llvm::Use *LLVMUse = &cast<llvm::CallBase>(Val)->getCalledOperandUse(); + return Use(LLVMUse, cast<User>(Ctx.getValue(LLVMUse->getUser())), Ctx); +} + +Function *CallBase::getCalledFunction() const { + return cast_or_null<Function>( + Ctx.getValue(cast<llvm::CallBase>(Val)->getCalledFunction())); +} +Function *CallBase::getCaller() { + return cast<Function>(Ctx.getValue(cast<llvm::CallBase>(Val)->getCaller())); +} + +void CallBase::setCalledFunction(Function *F) { + // F's function type is private, so we rely on `setCalledFunction()` to update + // it. But even though we are calling `setCalledFunction()` we also need to + // track this change at the SandboxIR level, which is why we call + // `setCalledOperand()` here. + // Note: This may break if `setCalledFunction()` early returns if `F` + // is already set, but we do have a unit test for it. + setCalledOperand(F); + cast<llvm::CallBase>(Val)->setCalledFunction(F->getFunctionType(), + cast<llvm::Function>(F->Val)); +} + +CallInst *CallInst::create(FunctionType *FTy, Value *Func, + ArrayRef<Value *> Args, BasicBlock::iterator WhereIt, + BasicBlock *WhereBB, Context &Ctx, + const Twine &NameStr) { + auto &Builder = Ctx.getLLVMIRBuilder(); + if (WhereIt != WhereBB->end()) + Builder.SetInsertPoint((*WhereIt).getTopmostLLVMInstruction()); + else + Builder.SetInsertPoint(cast<llvm::BasicBlock>(WhereBB->Val)); + SmallVector<llvm::Value *> LLVMArgs; + LLVMArgs.reserve(Args.size()); + for (Value *Arg : Args) + LLVMArgs.push_back(Arg->Val); + llvm::CallInst *NewCI = Builder.CreateCall(FTy, Func->Val, LLVMArgs, NameStr); + return Ctx.createCallInst(NewCI); +} + +CallInst *CallInst::create(FunctionType *FTy, Value *Func, + ArrayRef<Value *> Args, Instruction *InsertBefore, + Context &Ctx, const Twine &NameStr) { + return CallInst::create(FTy, Func, Args, InsertBefore->getIterator(), + InsertBefore->getParent(), Ctx, NameStr); +} + +CallInst *CallInst::create(FunctionType *FTy, Value *Func, + ArrayRef<Value *> Args, BasicBlock *InsertAtEnd, + Context &Ctx, const Twine &NameStr) { + return CallInst::create(FTy, Func, Args, InsertAtEnd->end(), InsertAtEnd, Ctx, + NameStr); +} + +#ifndef NDEBUG +void CallInst::dump(raw_ostream &OS) const { + dumpCommonPrefix(OS); + dumpCommonSuffix(OS); +} + +void CallInst::dump() const { + dump(dbgs()); + dbgs() << "\n"; +} void OpaqueInst::dump(raw_ostream &OS) const { dumpCommonPrefix(OS); @@ -819,7 +915,10 @@ Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) { return It->second.get(); if (auto *C = dyn_cast<llvm::Constant>(LLVMV)) { - It->second = std::unique_ptr<Constant>(new Constant(C, *this)); + if (auto *F = dyn_cast<llvm::Function>(LLVMV)) + It->second = std::unique_ptr<Function>(new Function(F, *this)); + else + It->second = std::unique_ptr<Constant>(new Constant(C, *this)); auto *NewC = It->second.get(); for (llvm::Value *COp : C->operands()) getOrCreateValueInternal(COp, C); @@ -864,6 +963,11 @@ Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) { It->second = std::unique_ptr<ReturnInst>(new ReturnInst(LLVMRet, *this)); return It->second.get(); } + case llvm::Instruction::Call: { + auto *LLVMCall = cast<llvm::CallInst>(LLVMV); + It->second = std::unique_ptr<CallInst>(new CallInst(LLVMCall, *this)); + return It->second.get(); + } default: break; } @@ -907,6 +1011,11 @@ ReturnInst *Context::createReturnInst(llvm::ReturnInst *I) { return cast<ReturnInst>(registerValue(std::move(NewPtr))); } +CallInst *Context::createCallInst(llvm::CallInst *I) { + auto NewPtr = std::unique_ptr<CallInst>(new CallInst(I, *this)); + return cast<CallInst>(registerValue(std::move(NewPtr))); +} + Value *Context::getValue(llvm::Value *V) const { auto It = LLVMValueToValueMap.find(V); if (It != LLVMValueToValueMap.end()) @@ -917,13 +1026,13 @@ Value *Context::getValue(llvm::Value *V) const { Function *Context::createFunction(llvm::Function *F) { assert(getValue(F) == nullptr && "Already exists!"); auto NewFPtr = std::unique_ptr<Function>(new Function(F, *this)); + auto *SBF = cast<Function>(registerValue(std::move(NewFPtr))); // Create arguments. for (auto &Arg : F->args()) getOrCreateArgument(&Arg); // Create BBs. for (auto &BB : *F) createBasicBlock(&BB); - auto *SBF = cast<Function>(registerValue(std::move(NewFPtr))); return SBF; } diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index a0e63bf12400..812874ff3c17 100644 --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -1528,11 +1528,7 @@ static void emitRemark(const Function &F, OptimizationRemarkEmitter &ORE, bool HWAddressSanitizer::selectiveInstrumentationShouldSkip( Function &F, FunctionAnalysisManager &FAM) const { - bool Skip = [&]() { - if (ClRandomSkipRate.getNumOccurrences()) { - std::bernoulli_distribution D(ClRandomSkipRate); - return !D(*Rng); - } + auto SkipHot = [&]() { if (!ClHotPercentileCutoff.getNumOccurrences()) return false; auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F); @@ -1544,7 +1540,16 @@ bool HWAddressSanitizer::selectiveInstrumentationShouldSkip( } return PSI->isFunctionHotInCallGraphNthPercentile( ClHotPercentileCutoff, &F, FAM.getResult<BlockFrequencyAnalysis>(F)); - }(); + }; + + auto SkipRandom = [&]() { + if (!ClRandomSkipRate.getNumOccurrences()) + return false; + std::bernoulli_distribution D(ClRandomSkipRate); + return !D(*Rng); + }; + + bool Skip = SkipRandom() || SkipHot(); emitRemark(F, FAM.getResult<OptimizationRemarkEmitterAnalysis>(F), Skip); return Skip; } diff --git a/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp b/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp index 0115809e939e..19cf7dc7e754 100644 --- a/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp +++ b/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp @@ -76,13 +76,25 @@ static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI, SmallVector<std::pair<IntrinsicInst *, bool>, 16> ReplaceWithValue; std::unique_ptr<RandomNumberGenerator> Rng; - auto ShouldRemove = [&](bool IsHot) { - if (!RandomRate.getNumOccurrences()) - return IsHot; + auto GetRng = [&]() -> RandomNumberGenerator & { if (!Rng) Rng = F.getParent()->createRNG(F.getName()); - std::bernoulli_distribution D(RandomRate); - return !D(*Rng); + return *Rng; + }; + + auto ShouldRemoveHot = [&](const BasicBlock &BB) { + return HotPercentileCutoff.getNumOccurrences() && PSI && + PSI->isHotCountNthPercentile( + HotPercentileCutoff, BFI.getBlockProfileCount(&BB).value_or(0)); + }; + + auto ShouldRemoveRandom = [&]() { + return RandomRate.getNumOccurrences() && + !std::bernoulli_distribution(RandomRate)(GetRng()); + }; + + auto ShouldRemove = [&](const BasicBlock &BB) { + return ShouldRemoveRandom() || ShouldRemoveHot(BB); }; for (BasicBlock &BB : F) { @@ -96,13 +108,7 @@ static bool removeUbsanTraps(Function &F, const BlockFrequencyInfo &BFI, case Intrinsic::allow_runtime_check: { ++NumChecksTotal; - bool IsHot = false; - if (PSI) { - uint64_t Count = BFI.getBlockProfileCount(&BB).value_or(0); - IsHot = PSI->isHotCountNthPercentile(HotPercentileCutoff, Count); - } - - bool ToRemove = ShouldRemove(IsHot); + bool ToRemove = ShouldRemove(BB); ReplaceWithValue.push_back({ II, ToRemove, diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-constant.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-constant.mir index 390541ae7684..27d4698f8f6e 100644 --- a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-constant.mir +++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-constant.mir @@ -407,6 +407,197 @@ body: | ... --- +name: constant_s_p0 +legalized: true +regBankSelected: true +tracksRegLiveness: true + +body: | + bb.0: + ; WAVE64-LABEL: name: constant_s_p0 + ; WAVE64: [[S_MOV_B:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 0 + ; WAVE64-NEXT: [[S_MOV_B1:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 1 + ; WAVE64-NEXT: [[S_MOV_B2:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -1 + ; WAVE64-NEXT: [[S_MOV_B3:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -54 + ; WAVE64-NEXT: [[S_MOV_B4:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 27 + ; WAVE64-NEXT: [[S_MOV_B5:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 4294967295 + ; WAVE64-NEXT: [[S_MOV_B32_:%[0-9]+]]:sreg_32 = S_MOV_B32 0 + ; WAVE64-NEXT: [[S_MOV_B32_1:%[0-9]+]]:sreg_32 = S_MOV_B32 1 + ; WAVE64-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_]], %subreg.sub0, [[S_MOV_B32_1]], %subreg.sub1 + ; WAVE64-NEXT: [[S_MOV_B32_2:%[0-9]+]]:sreg_32 = S_MOV_B32 23255 + ; WAVE64-NEXT: [[S_MOV_B32_3:%[0-9]+]]:sreg_32 = S_MOV_B32 -16 + ; WAVE64-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_2]], %subreg.sub0, [[S_MOV_B32_3]], %subreg.sub1 + ; WAVE64-NEXT: S_ENDPGM 0, implicit [[S_MOV_B]], implicit [[S_MOV_B1]], implicit [[S_MOV_B2]], implicit [[S_MOV_B3]], implicit [[S_MOV_B4]], implicit [[S_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + ; + ; WAVE32-LABEL: name: constant_s_p0 + ; WAVE32: [[S_MOV_B:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 0 + ; WAVE32-NEXT: [[S_MOV_B1:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 1 + ; WAVE32-NEXT: [[S_MOV_B2:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -1 + ; WAVE32-NEXT: [[S_MOV_B3:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -54 + ; WAVE32-NEXT: [[S_MOV_B4:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 27 + ; WAVE32-NEXT: [[S_MOV_B5:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 4294967295 + ; WAVE32-NEXT: [[S_MOV_B32_:%[0-9]+]]:sreg_32 = S_MOV_B32 0 + ; WAVE32-NEXT: [[S_MOV_B32_1:%[0-9]+]]:sreg_32 = S_MOV_B32 1 + ; WAVE32-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_]], %subreg.sub0, [[S_MOV_B32_1]], %subreg.sub1 + ; WAVE32-NEXT: [[S_MOV_B32_2:%[0-9]+]]:sreg_32 = S_MOV_B32 23255 + ; WAVE32-NEXT: [[S_MOV_B32_3:%[0-9]+]]:sreg_32 = S_MOV_B32 -16 + ; WAVE32-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_2]], %subreg.sub0, [[S_MOV_B32_3]], %subreg.sub1 + ; WAVE32-NEXT: S_ENDPGM 0, implicit [[S_MOV_B]], implicit [[S_MOV_B1]], implicit [[S_MOV_B2]], implicit [[S_MOV_B3]], implicit [[S_MOV_B4]], implicit [[S_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + %0:sgpr(p0) = G_CONSTANT i64 0 + %1:sgpr(p0) = G_CONSTANT i64 1 + %2:sgpr(p0) = G_CONSTANT i64 -1 + %3:sgpr(p0) = G_CONSTANT i64 -54 + %4:sgpr(p0) = G_CONSTANT i64 27 + %5:sgpr(p0) = G_CONSTANT i64 4294967295 + %6:sgpr(p0) = G_CONSTANT i64 4294967296 + %7:sgpr(p0) = G_CONSTANT i64 18446744004990098135 + S_ENDPGM 0, implicit %0 , implicit %1 , implicit %2, implicit %3, implicit %4, implicit %5, implicit %6, implicit %7 +... + +--- +name: constant_v_p0 +legalized: true +regBankSelected: true +tracksRegLiveness: true + +body: | + bb.0: + ; WAVE64-LABEL: name: constant_v_p0 + ; WAVE64: [[V_MOV_B:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 0, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B1:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 1, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B2:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -1, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B3:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -54, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B4:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 27, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B5:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 4294967295, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 0, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B32_e32_1:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 1, implicit $exec + ; WAVE64-NEXT: [[REG_SEQUENCE:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_]], %subreg.sub0, [[V_MOV_B32_e32_1]], %subreg.sub1 + ; WAVE64-NEXT: [[V_MOV_B32_e32_2:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 23255, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B32_e32_3:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 -16, implicit $exec + ; WAVE64-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_2]], %subreg.sub0, [[V_MOV_B32_e32_3]], %subreg.sub1 + ; WAVE64-NEXT: S_ENDPGM 0, implicit [[V_MOV_B]], implicit [[V_MOV_B1]], implicit [[V_MOV_B2]], implicit [[V_MOV_B3]], implicit [[V_MOV_B4]], implicit [[V_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + ; + ; WAVE32-LABEL: name: constant_v_p0 + ; WAVE32: [[V_MOV_B:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 0, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B1:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 1, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B2:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -1, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B3:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -54, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B4:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 27, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B5:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 4294967295, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 0, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B32_e32_1:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 1, implicit $exec + ; WAVE32-NEXT: [[REG_SEQUENCE:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_]], %subreg.sub0, [[V_MOV_B32_e32_1]], %subreg.sub1 + ; WAVE32-NEXT: [[V_MOV_B32_e32_2:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 23255, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B32_e32_3:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 -16, implicit $exec + ; WAVE32-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_2]], %subreg.sub0, [[V_MOV_B32_e32_3]], %subreg.sub1 + ; WAVE32-NEXT: S_ENDPGM 0, implicit [[V_MOV_B]], implicit [[V_MOV_B1]], implicit [[V_MOV_B2]], implicit [[V_MOV_B3]], implicit [[V_MOV_B4]], implicit [[V_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + %0:vgpr(p0) = G_CONSTANT i64 0 + %1:vgpr(p0) = G_CONSTANT i64 1 + %2:vgpr(p0) = G_CONSTANT i64 -1 + %3:vgpr(p0) = G_CONSTANT i64 -54 + %4:vgpr(p0) = G_CONSTANT i64 27 + %5:vgpr(p0) = G_CONSTANT i64 4294967295 + %6:vgpr(p0) = G_CONSTANT i64 4294967296 + %7:vgpr(p0) = G_CONSTANT i64 18446744004990098135 + S_ENDPGM 0, implicit %0 , implicit %1 , implicit %2, implicit %3, implicit %4, implicit %5, implicit %6, implicit %7 +... +--- +name: constant_s_p4 +legalized: true +regBankSelected: true +tracksRegLiveness: true + +body: | + bb.0: + ; WAVE64-LABEL: name: constant_s_p4 + ; WAVE64: [[S_MOV_B:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 0 + ; WAVE64-NEXT: [[S_MOV_B1:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 1 + ; WAVE64-NEXT: [[S_MOV_B2:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -1 + ; WAVE64-NEXT: [[S_MOV_B3:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -54 + ; WAVE64-NEXT: [[S_MOV_B4:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 27 + ; WAVE64-NEXT: [[S_MOV_B5:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 4294967295 + ; WAVE64-NEXT: [[S_MOV_B32_:%[0-9]+]]:sreg_32 = S_MOV_B32 0 + ; WAVE64-NEXT: [[S_MOV_B32_1:%[0-9]+]]:sreg_32 = S_MOV_B32 1 + ; WAVE64-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_]], %subreg.sub0, [[S_MOV_B32_1]], %subreg.sub1 + ; WAVE64-NEXT: [[S_MOV_B32_2:%[0-9]+]]:sreg_32 = S_MOV_B32 23255 + ; WAVE64-NEXT: [[S_MOV_B32_3:%[0-9]+]]:sreg_32 = S_MOV_B32 -16 + ; WAVE64-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_2]], %subreg.sub0, [[S_MOV_B32_3]], %subreg.sub1 + ; WAVE64-NEXT: S_ENDPGM 0, implicit [[S_MOV_B]], implicit [[S_MOV_B1]], implicit [[S_MOV_B2]], implicit [[S_MOV_B3]], implicit [[S_MOV_B4]], implicit [[S_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + ; + ; WAVE32-LABEL: name: constant_s_p4 + ; WAVE32: [[S_MOV_B:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 0 + ; WAVE32-NEXT: [[S_MOV_B1:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 1 + ; WAVE32-NEXT: [[S_MOV_B2:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -1 + ; WAVE32-NEXT: [[S_MOV_B3:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO -54 + ; WAVE32-NEXT: [[S_MOV_B4:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 27 + ; WAVE32-NEXT: [[S_MOV_B5:%[0-9]+]]:sreg_64 = S_MOV_B64_IMM_PSEUDO 4294967295 + ; WAVE32-NEXT: [[S_MOV_B32_:%[0-9]+]]:sreg_32 = S_MOV_B32 0 + ; WAVE32-NEXT: [[S_MOV_B32_1:%[0-9]+]]:sreg_32 = S_MOV_B32 1 + ; WAVE32-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_]], %subreg.sub0, [[S_MOV_B32_1]], %subreg.sub1 + ; WAVE32-NEXT: [[S_MOV_B32_2:%[0-9]+]]:sreg_32 = S_MOV_B32 23255 + ; WAVE32-NEXT: [[S_MOV_B32_3:%[0-9]+]]:sreg_32 = S_MOV_B32 -16 + ; WAVE32-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:sreg_64 = REG_SEQUENCE [[S_MOV_B32_2]], %subreg.sub0, [[S_MOV_B32_3]], %subreg.sub1 + ; WAVE32-NEXT: S_ENDPGM 0, implicit [[S_MOV_B]], implicit [[S_MOV_B1]], implicit [[S_MOV_B2]], implicit [[S_MOV_B3]], implicit [[S_MOV_B4]], implicit [[S_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + %0:sgpr(p4) = G_CONSTANT i64 0 + %1:sgpr(p4) = G_CONSTANT i64 1 + %2:sgpr(p4) = G_CONSTANT i64 -1 + %3:sgpr(p4) = G_CONSTANT i64 -54 + %4:sgpr(p4) = G_CONSTANT i64 27 + %5:sgpr(p4) = G_CONSTANT i64 4294967295 + %6:sgpr(p4) = G_CONSTANT i64 4294967296 + %7:sgpr(p4) = G_CONSTANT i64 18446744004990098135 + S_ENDPGM 0, implicit %0 , implicit %1 , implicit %2, implicit %3, implicit %4, implicit %5, implicit %6, implicit %7 +... + +--- +name: constant_v_p4 +legalized: true +regBankSelected: true +tracksRegLiveness: true + +body: | + bb.0: + ; WAVE64-LABEL: name: constant_v_p4 + ; WAVE64: [[V_MOV_B:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 0, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B1:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 1, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B2:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -1, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B3:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -54, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B4:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 27, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B5:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 4294967295, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 0, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B32_e32_1:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 1, implicit $exec + ; WAVE64-NEXT: [[REG_SEQUENCE:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_]], %subreg.sub0, [[V_MOV_B32_e32_1]], %subreg.sub1 + ; WAVE64-NEXT: [[V_MOV_B32_e32_2:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 23255, implicit $exec + ; WAVE64-NEXT: [[V_MOV_B32_e32_3:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 -16, implicit $exec + ; WAVE64-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_2]], %subreg.sub0, [[V_MOV_B32_e32_3]], %subreg.sub1 + ; WAVE64-NEXT: S_ENDPGM 0, implicit [[V_MOV_B]], implicit [[V_MOV_B1]], implicit [[V_MOV_B2]], implicit [[V_MOV_B3]], implicit [[V_MOV_B4]], implicit [[V_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + ; + ; WAVE32-LABEL: name: constant_v_p4 + ; WAVE32: [[V_MOV_B:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 0, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B1:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 1, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B2:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -1, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B3:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO -54, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B4:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 27, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B5:%[0-9]+]]:vreg_64 = V_MOV_B64_PSEUDO 4294967295, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 0, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B32_e32_1:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 1, implicit $exec + ; WAVE32-NEXT: [[REG_SEQUENCE:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_]], %subreg.sub0, [[V_MOV_B32_e32_1]], %subreg.sub1 + ; WAVE32-NEXT: [[V_MOV_B32_e32_2:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 23255, implicit $exec + ; WAVE32-NEXT: [[V_MOV_B32_e32_3:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 -16, implicit $exec + ; WAVE32-NEXT: [[REG_SEQUENCE1:%[0-9]+]]:vreg_64 = REG_SEQUENCE [[V_MOV_B32_e32_2]], %subreg.sub0, [[V_MOV_B32_e32_3]], %subreg.sub1 + ; WAVE32-NEXT: S_ENDPGM 0, implicit [[V_MOV_B]], implicit [[V_MOV_B1]], implicit [[V_MOV_B2]], implicit [[V_MOV_B3]], implicit [[V_MOV_B4]], implicit [[V_MOV_B5]], implicit [[REG_SEQUENCE]], implicit [[REG_SEQUENCE1]] + %0:vgpr(p4) = G_CONSTANT i64 0 + %1:vgpr(p4) = G_CONSTANT i64 1 + %2:vgpr(p4) = G_CONSTANT i64 -1 + %3:vgpr(p4) = G_CONSTANT i64 -54 + %4:vgpr(p4) = G_CONSTANT i64 27 + %5:vgpr(p4) = G_CONSTANT i64 4294967295 + %6:vgpr(p4) = G_CONSTANT i64 4294967296 + %7:vgpr(p4) = G_CONSTANT i64 18446744004990098135 + S_ENDPGM 0, implicit %0 , implicit %1 , implicit %2, implicit %3, implicit %4, implicit %5, implicit %6, implicit %7 +... + +--- name: constant_s_p999 legalized: true regBankSelected: true diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/pgo-opt-out.ll b/llvm/test/Instrumentation/HWAddressSanitizer/pgo-opt-out.ll index 01eda4e35d7c..0ef94235fd51 100644 --- a/llvm/test/Instrumentation/HWAddressSanitizer/pgo-opt-out.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/pgo-opt-out.ll @@ -2,6 +2,8 @@ ; RUN: opt < %s -passes='require<profile-summary>,hwasan' -pass-remarks=hwasan -pass-remarks-missed=hwasan -S -hwasan-percentile-cutoff-hot=990000 2>&1 | FileCheck %s --check-prefix=NONE ; RUN: opt < %s -passes='require<profile-summary>,hwasan' -pass-remarks=hwasan -pass-remarks-missed=hwasan -S -hwasan-random-rate=1.0 2>&1 | FileCheck %s --check-prefix=ALL ; RUN: opt < %s -passes='require<profile-summary>,hwasan' -pass-remarks=hwasan -pass-remarks-missed=hwasan -S -hwasan-random-rate=0.0 2>&1 | FileCheck %s --check-prefix=NONE +; RUN: opt < %s -passes='require<profile-summary>,hwasan' -pass-remarks=hwasan -pass-remarks-missed=hwasan -S -hwasan-random-rate=1.0 -hwasan-percentile-cutoff-hot=990000 2>&1 | FileCheck %s --check-prefix=NONE +; RUN: opt < %s -passes='require<profile-summary>,hwasan' -pass-remarks=hwasan -pass-remarks-missed=hwasan -S -hwasan-random-rate=0.0 -hwasan-percentile-cutoff-hot=700000 2>&1 | FileCheck %s --check-prefix=NONE ; ALL: remark: <unknown>:0:0: Sanitized: F=sanitize ; ALL: @sanitized diff --git a/llvm/test/Transforms/lower-builtin-allow-check.ll b/llvm/test/Transforms/lower-builtin-allow-check.ll index 2f6fa96ffd9c..bcd9722d2b28 100644 --- a/llvm/test/Transforms/lower-builtin-allow-check.ll +++ b/llvm/test/Transforms/lower-builtin-allow-check.ll @@ -309,7 +309,7 @@ define dso_local noundef i32 @veryHot(ptr noundef readonly %0) !prof !39 { ; ALL70-LABEL: define dso_local noundef i32 @veryHot( ; ALL70-SAME: ptr noundef readonly [[TMP0:%.*]]) !prof [[PROF17:![0-9]+]] { ; ALL70-NEXT: [[CHK:%.*]] = icmp eq ptr [[TMP0]], null -; ALL70-NEXT: [[HOT:%.*]] = xor i1 true, true +; ALL70-NEXT: [[HOT:%.*]] = xor i1 false, true ; ALL70-NEXT: [[TMP2:%.*]] = or i1 [[CHK]], [[HOT]] ; ALL70-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP4:%.*]] ; ALL70: 3: diff --git a/llvm/unittests/SandboxIR/SandboxIRTest.cpp b/llvm/unittests/SandboxIR/SandboxIRTest.cpp index c600103fe10c..05ec42c952eb 100644 --- a/llvm/unittests/SandboxIR/SandboxIRTest.cpp +++ b/llvm/unittests/SandboxIR/SandboxIRTest.cpp @@ -90,7 +90,7 @@ define void @foo(i32 %v1) { EXPECT_FALSE(isa<sandboxir::Instruction>(Const0)); EXPECT_TRUE(isa<sandboxir::Instruction>(OpaqueI)); - EXPECT_FALSE(isa<sandboxir::User>(F)); + EXPECT_TRUE(isa<sandboxir::User>(F)); EXPECT_FALSE(isa<sandboxir::User>(Arg0)); EXPECT_FALSE(isa<sandboxir::User>(BB)); EXPECT_TRUE(isa<sandboxir::User>(AddI)); @@ -180,8 +180,8 @@ define i32 @foo(i32 %v0, i32 %v1) { BS << "\n"; I0->getOperandUse(0).dump(BS); EXPECT_EQ(Buff, R"IR( -Def: i32 %v0 ; SB1. (Argument) -User: %add0 = add i32 %v0, %v1 ; SB4. (Opaque) +Def: i32 %v0 ; SB2. (Argument) +User: %add0 = add i32 %v0, %v1 ; SB5. (Opaque) OperandNo: 0 )IR"); #endif // NDEBUG @@ -398,10 +398,10 @@ bb1: EXPECT_EQ(Buff, R"IR( void @foo(i32 %arg0, i32 %arg1) { bb0: - br label %bb1 ; SB3. (Br) + br label %bb1 ; SB4. (Br) bb1: - ret void ; SB5. (Ret) + ret void ; SB6. (Ret) } )IR"); } @@ -466,7 +466,7 @@ bb1: BB0.dump(BS); EXPECT_EQ(Buff, R"IR( bb0: - br label %bb1 ; SB2. (Br) + br label %bb1 ; SB3. (Br) )IR"); } #endif // NDEBUG @@ -836,3 +836,203 @@ define i8 @foo(i8 %val) { sandboxir::ReturnInst::create(Val, /*InsertAtEnd=*/BB, Ctx)); EXPECT_EQ(NewRet4->getReturnValue(), Val); } + +TEST_F(SandboxIRTest, CallBase) { + parseIR(C, R"IR( +declare void @bar1(i8) +declare void @bar2() +declare void @bar3() +declare void @variadic(ptr, ...) + +define i8 @foo(i8 %arg0, i32 %arg1, ptr %indirectFoo) { + %call = call i8 @foo(i8 %arg0, i32 %arg1) + call void @bar1(i8 %arg0) + call void @bar2() + call void %indirectFoo() + call void @bar2() noreturn + tail call fastcc void @bar2() + call void (ptr, ...) @variadic(ptr %indirectFoo, i32 1) + ret i8 %call +} +)IR"); + llvm::Function &LLVMF = *M->getFunction("foo"); + unsigned ArgIdx = 0; + llvm::Argument *LLVMArg0 = LLVMF.getArg(ArgIdx++); + llvm::Argument *LLVMArg1 = LLVMF.getArg(ArgIdx++); + llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); + SmallVector<llvm::CallBase *, 8> LLVMCalls; + auto LLVMIt = LLVMBB->begin(); + while (isa<llvm::CallBase>(&*LLVMIt)) + LLVMCalls.push_back(cast<llvm::CallBase>(&*LLVMIt++)); + + sandboxir::Context Ctx(C); + sandboxir::Function &F = *Ctx.createFunction(&LLVMF); + + for (llvm::CallBase *LLVMCall : LLVMCalls) { + // Check classof(Instruction *). + auto *Call = cast<sandboxir::CallBase>(Ctx.getValue(LLVMCall)); + // Check classof(Value *). + EXPECT_TRUE(isa<sandboxir::CallBase>((sandboxir::Value *)Call)); + // Check getFunctionType(). + EXPECT_EQ(Call->getFunctionType(), LLVMCall->getFunctionType()); + // Check data_ops(). + EXPECT_EQ(range_size(Call->data_ops()), range_size(LLVMCall->data_ops())); + auto DataOpIt = Call->data_operands_begin(); + for (llvm::Use &LLVMUse : LLVMCall->data_ops()) { + Value *LLVMOp = LLVMUse.get(); + sandboxir::Use Use = *DataOpIt++; + EXPECT_EQ(Ctx.getValue(LLVMOp), Use.get()); + // Check isDataOperand(). + EXPECT_EQ(Call->isDataOperand(Use), LLVMCall->isDataOperand(&LLVMUse)); + // Check getDataOperandNo(). + EXPECT_EQ(Call->getDataOperandNo(Use), + LLVMCall->getDataOperandNo(&LLVMUse)); + // Check isArgOperand(). + EXPECT_EQ(Call->isArgOperand(Use), LLVMCall->isArgOperand(&LLVMUse)); + // Check isCallee(). + EXPECT_EQ(Call->isCallee(Use), LLVMCall->isCallee(&LLVMUse)); + } + // Check data_operands_empty(). + EXPECT_EQ(Call->data_operands_empty(), LLVMCall->data_operands_empty()); + // Check data_operands_size(). + EXPECT_EQ(Call->data_operands_size(), LLVMCall->data_operands_size()); + // Check getNumTotalBundleOperands(). + EXPECT_EQ(Call->getNumTotalBundleOperands(), + LLVMCall->getNumTotalBundleOperands()); + // Check args(). + EXPECT_EQ(range_size(Call->args()), range_size(LLVMCall->args())); + auto ArgIt = Call->arg_begin(); + for (llvm::Use &LLVMUse : LLVMCall->args()) { + Value *LLVMArg = LLVMUse.get(); + sandboxir::Use Use = *ArgIt++; + EXPECT_EQ(Ctx.getValue(LLVMArg), Use.get()); + } + // Check arg_empty(). + EXPECT_EQ(Call->arg_empty(), LLVMCall->arg_empty()); + // Check arg_size(). + EXPECT_EQ(Call->arg_size(), LLVMCall->arg_size()); + for (unsigned ArgIdx = 0, E = Call->arg_size(); ArgIdx != E; ++ArgIdx) { + // Check getArgOperand(). + EXPECT_EQ(Call->getArgOperand(ArgIdx), + Ctx.getValue(LLVMCall->getArgOperand(ArgIdx))); + // Check getArgOperandUse(). + sandboxir::Use Use = Call->getArgOperandUse(ArgIdx); + llvm::Use &LLVMUse = LLVMCall->getArgOperandUse(ArgIdx); + EXPECT_EQ(Use.get(), Ctx.getValue(LLVMUse.get())); + // Check getArgOperandNo(). + EXPECT_EQ(Call->getArgOperandNo(Use), + LLVMCall->getArgOperandNo(&LLVMUse)); + } + // Check hasArgument(). + SmallVector<llvm::Value *> TestArgs( + {LLVMArg0, LLVMArg1, &LLVMF, LLVMBB, LLVMCall}); + for (llvm::Value *LLVMV : TestArgs) { + sandboxir::Value *V = Ctx.getValue(LLVMV); + EXPECT_EQ(Call->hasArgument(V), LLVMCall->hasArgument(LLVMV)); + } + // Check getCalledOperand(). + EXPECT_EQ(Call->getCalledOperand(), + Ctx.getValue(LLVMCall->getCalledOperand())); + // Check getCalledOperandUse(). + EXPECT_EQ(Call->getCalledOperandUse().get(), + Ctx.getValue(LLVMCall->getCalledOperandUse())); + // Check getCalledFunction(). + if (LLVMCall->getCalledFunction() == nullptr) + EXPECT_EQ(Call->getCalledFunction(), nullptr); + else { + auto *LLVMCF = cast<llvm::Function>(LLVMCall->getCalledFunction()); + (void)LLVMCF; + EXPECT_EQ(Call->getCalledFunction(), + cast<sandboxir::Function>( + Ctx.getValue(LLVMCall->getCalledFunction()))); + } + // Check isIndirectCall(). + EXPECT_EQ(Call->isIndirectCall(), LLVMCall->isIndirectCall()); + // Check getCaller(). + EXPECT_EQ(Call->getCaller(), Ctx.getValue(LLVMCall->getCaller())); + // Check isMustTailCall(). + EXPECT_EQ(Call->isMustTailCall(), LLVMCall->isMustTailCall()); + // Check isTailCall(). + EXPECT_EQ(Call->isTailCall(), LLVMCall->isTailCall()); + // Check getIntrinsicID(). + EXPECT_EQ(Call->getIntrinsicID(), LLVMCall->getIntrinsicID()); + // Check getCallingConv(). + EXPECT_EQ(Call->getCallingConv(), LLVMCall->getCallingConv()); + // Check isInlineAsm(). + EXPECT_EQ(Call->isInlineAsm(), LLVMCall->isInlineAsm()); + } + + auto *Arg0 = F.getArg(0); + auto *Arg1 = F.getArg(1); + auto *BB = &*F.begin(); + auto It = BB->begin(); + auto *Call0 = cast<sandboxir::CallBase>(&*It++); + [[maybe_unused]] auto *Call1 = cast<sandboxir::CallBase>(&*It++); + auto *Call2 = cast<sandboxir::CallBase>(&*It++); + // Check setArgOperand + Call0->setArgOperand(0, Arg1); + EXPECT_EQ(Call0->getArgOperand(0), Arg1); + Call0->setArgOperand(0, Arg0); + EXPECT_EQ(Call0->getArgOperand(0), Arg0); + + auto *Bar3F = Ctx.createFunction(M->getFunction("bar3")); + + // Check setCalledOperand + auto *SvOp = Call0->getCalledOperand(); + Call0->setCalledOperand(Bar3F); + EXPECT_EQ(Call0->getCalledOperand(), Bar3F); + Call0->setCalledOperand(SvOp); + // Check setCalledFunction + Call2->setCalledFunction(Bar3F); + EXPECT_EQ(Call2->getCalledFunction(), Bar3F); +} + +TEST_F(SandboxIRTest, CallInst) { + parseIR(C, R"IR( +define i8 @foo(i8 %arg) { + %call = call i8 @foo(i8 %arg) + ret i8 %call +} +)IR"); + Function &LLVMF = *M->getFunction("foo"); + sandboxir::Context Ctx(C); + auto &F = *Ctx.createFunction(&LLVMF); + unsigned ArgIdx = 0; + auto *Arg0 = F.getArg(ArgIdx++); + auto *BB = &*F.begin(); + auto It = BB->begin(); + auto *Call = cast<sandboxir::CallInst>(&*It++); + auto *Ret = cast<sandboxir::ReturnInst>(&*It++); + EXPECT_EQ(Call->getNumOperands(), 2u); + EXPECT_EQ(Ret->getOpcode(), sandboxir::Instruction::Opcode::Ret); + FunctionType *FTy = F.getFunctionType(); + SmallVector<sandboxir::Value *, 1> Args; + Args.push_back(Arg0); + { + // Check create() WhereIt. + auto *Call = cast<sandboxir::CallInst>(sandboxir::CallInst::create( + FTy, &F, Args, /*WhereIt=*/Ret->getIterator(), BB, Ctx)); + EXPECT_EQ(Call->getNextNode(), Ret); + EXPECT_EQ(Call->getCalledFunction(), &F); + EXPECT_EQ(range_size(Call->args()), 1u); + EXPECT_EQ(Call->getArgOperand(0), Arg0); + } + { + // Check create() InsertBefore. + auto *Call = cast<sandboxir::CallInst>( + sandboxir::CallInst::create(FTy, &F, Args, /*InsertBefore=*/Ret, Ctx)); + EXPECT_EQ(Call->getNextNode(), Ret); + EXPECT_EQ(Call->getCalledFunction(), &F); + EXPECT_EQ(range_size(Call->args()), 1u); + EXPECT_EQ(Call->getArgOperand(0), Arg0); + } + { + // Check create() InsertAtEnd. + auto *Call = cast<sandboxir::CallInst>( + sandboxir::CallInst::create(FTy, &F, Args, /*InsertAtEnd=*/BB, Ctx)); + EXPECT_EQ(Call->getPrevNode(), Ret); + EXPECT_EQ(Call->getCalledFunction(), &F); + EXPECT_EQ(range_size(Call->args()), 1u); + EXPECT_EQ(Call->getArgOperand(0), Arg0); + } +} diff --git a/llvm/unittests/SandboxIR/TrackerTest.cpp b/llvm/unittests/SandboxIR/TrackerTest.cpp index dd9dcd543236..5111d5f38798 100644 --- a/llvm/unittests/SandboxIR/TrackerTest.cpp +++ b/llvm/unittests/SandboxIR/TrackerTest.cpp @@ -69,6 +69,34 @@ define void @foo(ptr %ptr) { EXPECT_EQ(Ld->getOperand(0), Gep0); } +TEST_F(TrackerTest, SetUse) { + parseIR(C, R"IR( +define void @foo(ptr %ptr, i8 %arg) { + %ld = load i8, ptr %ptr + %add = add i8 %ld, %arg + ret void +} +)IR"); + Function &LLVMF = *M->getFunction("foo"); + sandboxir::Context Ctx(C); + auto *F = Ctx.createFunction(&LLVMF); + unsigned ArgIdx = 0; + auto *Arg0 = F->getArg(ArgIdx++); + auto *BB = &*F->begin(); + auto &Tracker = Ctx.getTracker(); + Tracker.save(); + auto It = BB->begin(); + auto *Ld = &*It++; + auto *Add = &*It++; + + Ctx.save(); + sandboxir::Use Use = Add->getOperandUse(0); + Use.set(Arg0); + EXPECT_EQ(Add->getOperand(0), Arg0); + Ctx.revert(); + EXPECT_EQ(Add->getOperand(0), Ld); +} + TEST_F(TrackerTest, SwapOperands) { parseIR(C, R"IR( define void @foo(i1 %cond) { @@ -413,3 +441,50 @@ define i32 @foo(i32 %arg) { EXPECT_EQ(&*It++, Ret); EXPECT_EQ(It, BB->end()); } + +TEST_F(TrackerTest, CallBaseSetters) { + parseIR(C, R"IR( +declare void @bar1(i8) +declare void @bar2(i8) + +define void @foo(i8 %arg0, i8 %arg1) { + call void @bar1(i8 %arg0) + ret void +} +)IR"); + Function &LLVMF = *M->getFunction("foo"); + sandboxir::Context Ctx(C); + + auto *F = Ctx.createFunction(&LLVMF); + unsigned ArgIdx = 0; + auto *Arg0 = F->getArg(ArgIdx++); + auto *Arg1 = F->getArg(ArgIdx++); + auto *BB = &*F->begin(); + auto It = BB->begin(); + auto *Call = cast<sandboxir::CallBase>(&*It++); + [[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++); + + // Check setArgOperand(). + Ctx.save(); + Call->setArgOperand(0, Arg1); + EXPECT_EQ(Call->getArgOperand(0), Arg1); + Ctx.revert(); + EXPECT_EQ(Call->getArgOperand(0), Arg0); + + auto *Bar1F = Call->getCalledFunction(); + auto *Bar2F = Ctx.createFunction(M->getFunction("bar2")); + + // Check setCalledOperand(). + Ctx.save(); + Call->setCalledOperand(Bar2F); + EXPECT_EQ(Call->getCalledOperand(), Bar2F); + Ctx.revert(); + EXPECT_EQ(Call->getCalledOperand(), Bar1F); + + // Check setCalledFunction(). + Ctx.save(); + Call->setCalledFunction(Bar2F); + EXPECT_EQ(Call->getCalledFunction(), Bar2F); + Ctx.revert(); + EXPECT_EQ(Call->getCalledFunction(), Bar1F); +} diff --git a/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp b/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp index 527fbe5cf628..890706bf1bb2 100644 --- a/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp +++ b/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp @@ -906,6 +906,43 @@ struct VectorReductionToFPDotProd final } }; +struct VectorStepOpConvert final : OpConversionPattern<vector::StepOp> { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(vector::StepOp stepOp, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>(); + Type dstType = typeConverter.convertType(stepOp.getType()); + if (!dstType) + return failure(); + + Location loc = stepOp.getLoc(); + int64_t numElements = stepOp.getType().getNumElements(); + auto intType = + rewriter.getIntegerType(typeConverter.getIndexTypeBitwidth()); + + // Input vectors of size 1 are converted to scalars by the type converter. + // We just create a constant in this case. + if (numElements == 1) { + Value zero = spirv::ConstantOp::getZero(intType, loc, rewriter); + rewriter.replaceOp(stepOp, zero); + return success(); + } + + SmallVector<Value> source; + source.reserve(numElements); + for (int64_t i = 0; i < numElements; ++i) { + Attribute intAttr = rewriter.getIntegerAttr(intType, i); + Value constOp = rewriter.create<spirv::ConstantOp>(loc, intType, intAttr); + source.push_back(constOp); + } + rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(stepOp, dstType, + source); + return success(); + } +}; + } // namespace #define CL_INT_MAX_MIN_OPS \ spirv::CLUMaxOp, spirv::CLUMinOp, spirv::CLSMaxOp, spirv::CLSMinOp @@ -929,8 +966,9 @@ void mlir::populateVectorToSPIRVPatterns(SPIRVTypeConverter &typeConverter, VectorReductionFloatMinMax<GL_FLOAT_MAX_MIN_OPS>, VectorShapeCast, VectorInsertStridedSliceOpConvert, VectorShuffleOpConvert, VectorInterleaveOpConvert, VectorDeinterleaveOpConvert, - VectorSplatPattern, VectorLoadOpConverter, VectorStoreOpConverter>( - typeConverter, patterns.getContext(), PatternBenefit(1)); + VectorSplatPattern, VectorLoadOpConverter, VectorStoreOpConverter, + VectorStepOpConvert>(typeConverter, patterns.getContext(), + PatternBenefit(1)); // Make sure that the more specialized dot product pattern has higher benefit // than the generic one that extracts all elements. diff --git a/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir b/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir index edad20874993..dd0ed77470a2 100644 --- a/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir +++ b/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir @@ -794,6 +794,32 @@ func.func @shape_cast_size1_vector(%arg0 : vector<f32>) -> vector<1xf32> { // ----- +// CHECK-LABEL: @step() +// CHECK: %[[CST0:.*]] = spirv.Constant 0 : i32 +// CHECK: %[[CST1:.*]] = spirv.Constant 1 : i32 +// CHECK: %[[CST2:.*]] = spirv.Constant 2 : i32 +// CHECK: %[[CST3:.*]] = spirv.Constant 3 : i32 +// CHECK: %[[CONSTRUCT:.*]] = spirv.CompositeConstruct %[[CST0]], %[[CST1]], %[[CST2]], %[[CST3]] : (i32, i32, i32, i32) -> vector<4xi32> +// CHECK: %[[CAST:.*]] = builtin.unrealized_conversion_cast %[[CONSTRUCT]] : vector<4xi32> to vector<4xindex> +// CHECK: return %[[CAST]] : vector<4xindex> +func.func @step() -> vector<4xindex> { + %0 = vector.step : vector<4xindex> + return %0 : vector<4xindex> +} + +// ----- + +// CHECK-LABEL: @step_size1() +// CHECK: %[[CST0:.*]] = spirv.Constant 0 : i32 +// CHECK: %[[CAST:.*]] = builtin.unrealized_conversion_cast %[[CST0]] : i32 to vector<1xindex> +// CHECK: return %[[CAST]] : vector<1xindex> +func.func @step_size1() -> vector<1xindex> { + %0 = vector.step : vector<1xindex> + return %0 : vector<1xindex> +} + +// ----- + module attributes { spirv.target_env = #spirv.target_env< #spirv.vce<v1.0, [Shader], [SPV_KHR_storage_buffer_storage_class]>, #spirv.resource_limits<>> diff --git a/polly/lib/Analysis/ScopBuilder.cpp b/polly/lib/Analysis/ScopBuilder.cpp index d594823410f5..0b9a1a916e1c 100644 --- a/polly/lib/Analysis/ScopBuilder.cpp +++ b/polly/lib/Analysis/ScopBuilder.cpp @@ -2770,7 +2770,7 @@ isl::set ScopBuilder::getNonHoistableCtx(MemoryAccess *Access, auto &DL = scop->getFunction().getDataLayout(); if (isSafeToLoadUnconditionally(LI->getPointerOperand(), LI->getType(), - LI->getAlign(), DL)) { + LI->getAlign(), DL, nullptr)) { SafeToLoad = isl::set::universe(AccessRelation.get_space().range()); } else if (BB != LI->getParent()) { // Skip accesses in non-affine subregions as they might not be executed diff --git a/polly/lib/Analysis/ScopDetection.cpp b/polly/lib/Analysis/ScopDetection.cpp index eab7bd83e6a4..79db3965de02 100644 --- a/polly/lib/Analysis/ScopDetection.cpp +++ b/polly/lib/Analysis/ScopDetection.cpp @@ -490,7 +490,8 @@ bool ScopDetection::onlyValidRequiredInvariantLoads( for (auto NonAffineRegion : Context.NonAffineSubRegionSet) { if (isSafeToLoadUnconditionally(Load->getPointerOperand(), - Load->getType(), Load->getAlign(), DL)) + Load->getType(), Load->getAlign(), DL, + nullptr)) continue; if (NonAffineRegion->contains(Load) && |
