diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp index 6b33303ba553b7..b809b2935ee9eb 100644 --- a/bolt/lib/Core/DIEBuilder.cpp +++ b/bolt/lib/Core/DIEBuilder.cpp @@ -202,7 +202,7 @@ void DIEBuilder::buildTypeUnits(const bool Init) { true); } } - unsigned int CUNum = getCUNum(DwarfContext, IsDWO); + const unsigned int CUNum = getCUNum(DwarfContext, IsDWO); getState().CloneUnitCtxMap.resize(CUNum); DWARFContext::unit_iterator_range CU4TURanges = IsDWO ? DwarfContext->dwo_types_section_units() diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index 3140d1a838afce..018b92fdc11f55 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -177,6 +177,7 @@ class AttributeCommonInfo { IsRegularKeywordAttribute); } const IdentifierInfo *getAttrName() const { return AttrName; } + void setAttrName(const IdentifierInfo *AttrNameII) { AttrName = AttrNameII; } SourceLocation getLoc() const { return AttrRange.getBegin(); } SourceRange getRange() const { return AttrRange; } void setRange(SourceRange R) { AttrRange = R; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 3dc33c10af11ed..f1f335118f37a4 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -9073,6 +9073,7 @@ class AttrImporter { ToAttr = FromAttr->clone(Importer.getToContext()); ToAttr->setRange(ToRange); + ToAttr->setAttrName(Importer.Import(FromAttr->getAttrName())); } // Get the result of the previous import attempt (can be used only once). diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 902b740a5106c0..4dd7510bf8ddf8 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -12,6 +12,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Testing/CommandLineArgs.h" #include "llvm/Support/SmallVectorMemoryBuffer.h" #include "clang/AST/DeclContextInternals.h" @@ -7379,11 +7380,12 @@ struct ImportAttributes : public ASTImporterOptionSpecificTestBase { } template - void importAttr(const char *Code, AT *&FromAttr, AT *&ToAttr) { + void importAttr(const char *Code, AT *&FromAttr, AT *&ToAttr, + TestLanguage Lang = Lang_CXX11) { static_assert(std::is_base_of::value, "AT should be an Attr"); static_assert(std::is_base_of::value, "DT should be a Decl"); - Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cc"); + Decl *FromTU = getTuDecl(Code, Lang, "input.cc"); DT *FromD = FirstDeclMatcher
().match(FromTU, namedDecl(hasName("test"))); ASSERT_TRUE(FromD); @@ -7669,6 +7671,13 @@ TEST_P(ImportAttributes, ImportLocksExcluded) { checkImportVariadicArg(FromAttr->args(), ToAttr->args()); } +TEST_P(ImportAttributes, ImportC99NoThrowAttr) { + NoThrowAttr *FromAttr, *ToAttr; + importAttr("void test () __attribute__ ((__nothrow__));", + FromAttr, ToAttr, Lang_C99); + checkImported(FromAttr->getAttrName(), ToAttr->getAttrName()); +} + template auto ExtendWithOptions(const T &Values, const std::vector &Args) { auto Copy = Values; diff --git a/libc/cmake/modules/LLVMLibCFlagRules.cmake b/libc/cmake/modules/LLVMLibCFlagRules.cmake index a1d3dc4b567aa3..37ffe708fb7548 100644 --- a/libc/cmake/modules/LLVMLibCFlagRules.cmake +++ b/libc/cmake/modules/LLVMLibCFlagRules.cmake @@ -132,6 +132,8 @@ endfunction(get_fq_dep_list_without_flag) # Special flags set(FMA_OPT_FLAG "FMA_OPT") set(ROUND_OPT_FLAG "ROUND_OPT") +# SSE2 is the baseline for x86_64, so we add a negative flag to disable it if needed. +set(DISABLE_SSE2_OPT_FLAG "DISABLE_SSE2_OPT") # Skip FMA_OPT flag for targets that don't support fma. if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86 AND (LIBC_CPU_FEATURES MATCHES "FMA")) OR @@ -143,3 +145,8 @@ endif() if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86 AND (LIBC_CPU_FEATURES MATCHES "SSE4_2"))) set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE) endif() + +# Skip DISABLE_SSE2_OPT flag for targets that don't support SSE2. +if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86 AND (LIBC_CPU_FEATURES MATCHES "SSE2"))) + set(SKIP_FLAG_EXPANSION_DISABLE_SSE2_OPT TRUE) +endif() diff --git a/libc/cmake/modules/LLVMLibCObjectRules.cmake b/libc/cmake/modules/LLVMLibCObjectRules.cmake index d5df27a2dcb434..6d94ce97f0b689 100644 --- a/libc/cmake/modules/LLVMLibCObjectRules.cmake +++ b/libc/cmake/modules/LLVMLibCObjectRules.cmake @@ -18,6 +18,14 @@ function(_get_common_compile_options output_var flags) set(ADD_SSE4_2_FLAG TRUE) endif() + list(FIND flags ${DISABLE_SSE2_OPT_FLAG} no_sse2) + if(${no_sse2} LESS 0) + list(FIND flags "${DISABLE_SSE2_OPT_FLAG}__ONLY" no_sse2) + endif() + if((${no_sse2} GREATER -1) AND (LIBC_CPU_FEATURES MATCHES "SSE2")) + set(DISABLE_SSE2_FLAG TRUE) + endif() + set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${ARGN}) if(LLVM_COMPILER_IS_GCC_COMPATIBLE) list(APPEND compile_options "-fpie") @@ -58,12 +66,18 @@ function(_get_common_compile_options output_var flags) if(ADD_SSE4_2_FLAG) list(APPEND compile_options "-msse4.2") endif() + if(DISABLE_SSE2_FLAG) + list(APPEND compile_options "-mno-sse2") + endif() elseif(MSVC) list(APPEND compile_options "/EHs-c-") list(APPEND compile_options "/GR-") if(ADD_FMA_FLAG) list(APPEND compile_options "/arch:AVX2") endif() + if(DISABLE_SSE2_FLAG) + list(APPEND compile_options "/arch:SSE") + endif() endif() if (LIBC_TARGET_ARCHITECTURE_IS_GPU) list(APPEND compile_options "-nogpulib") diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 284feb7b99096e..ecefa5884adb3e 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -130,6 +130,14 @@ set(TARGET_LIBC_ENTRYPOINTS #libc.src.stdio.scanf #libc.src.stdio.fscanf + # search.h entrypoints + libc.src.search.hcreate + libc.src.search.hcreate_r + libc.src.search.hsearch + libc.src.search.hsearch_r + libc.src.search.hdestroy + libc.src.search.hdestroy_r + # sys/mman.h entrypoints libc.src.sys.mman.madvise libc.src.sys.mman.mmap diff --git a/libc/config/linux/aarch64/headers.txt b/libc/config/linux/aarch64/headers.txt index c47e05c924fd94..cfca5959b5ffa5 100644 --- a/libc/config/linux/aarch64/headers.txt +++ b/libc/config/linux/aarch64/headers.txt @@ -12,6 +12,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.stdlib libc.include.string libc.include.strings + libc.include.search libc.include.sys_mman libc.include.sys_socket libc.include.sys_syscall diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td index 377763b97cfd95..726e58f376eaa7 100644 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -248,3 +248,7 @@ def TermiosAPI : PublicAPI<"termios.h"> { def SetJmpAPI : PublicAPI<"setjmp.h"> { let Types = ["jmp_buf"]; } + +def SearchAPI : PublicAPI<"search.h"> { + let Types = ["ACTION", "ENTRY", "struct hsearch_data"]; +} diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 27c0b8e5b3a3aa..ee701c04b2e2a8 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -89,6 +89,14 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.strtoul libc.src.stdlib.strtoull + # search.h entrypoints + libc.src.search.hcreate + libc.src.search.hcreate_r + libc.src.search.hsearch + libc.src.search.hsearch_r + libc.src.search.hdestroy + libc.src.search.hdestroy_r + # sys/mman.h entrypoints libc.src.sys.mman.mmap libc.src.sys.mman.munmap diff --git a/libc/config/linux/arm/headers.txt b/libc/config/linux/arm/headers.txt index fe7c88e922e07e..bd08d8f8fa437f 100644 --- a/libc/config/linux/arm/headers.txt +++ b/libc/config/linux/arm/headers.txt @@ -7,4 +7,5 @@ set(TARGET_PUBLIC_HEADERS libc.include.stdlib libc.include.string libc.include.strings + libc.include.search ) diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index a5f0c91e32d081..1ccb40108bd850 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -136,6 +136,14 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdio.scanf libc.src.stdio.fscanf + # search.h entrypoints + libc.src.search.hcreate + libc.src.search.hcreate_r + libc.src.search.hsearch + libc.src.search.hsearch_r + libc.src.search.hdestroy + libc.src.search.hdestroy_r + # sys/mman.h entrypoints libc.src.sys.mman.madvise libc.src.sys.mman.mmap diff --git a/libc/config/linux/riscv/headers.txt b/libc/config/linux/riscv/headers.txt index 24247ee5819f94..3e2b1630f1695e 100644 --- a/libc/config/linux/riscv/headers.txt +++ b/libc/config/linux/riscv/headers.txt @@ -17,6 +17,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.stdlib libc.include.string libc.include.strings + libc.include.search libc.include.termios libc.include.threads libc.include.time diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 63aa7473115a08..43266e0e5b66e6 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -497,6 +497,14 @@ if(LLVM_LIBC_FULL_BUILD) libc.src.spawn.posix_spawn_file_actions_destroy libc.src.spawn.posix_spawn_file_actions_init + # search.h entrypoints + libc.src.search.hcreate + libc.src.search.hcreate_r + libc.src.search.hsearch + libc.src.search.hsearch_r + libc.src.search.hdestroy + libc.src.search.hdestroy_r + # threads.h entrypoints libc.src.threads.call_once libc.src.threads.cnd_broadcast diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt index 24247ee5819f94..3e2b1630f1695e 100644 --- a/libc/config/linux/x86_64/headers.txt +++ b/libc/config/linux/x86_64/headers.txt @@ -17,6 +17,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.stdlib libc.include.string libc.include.strings + libc.include.search libc.include.termios libc.include.threads libc.include.time diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 9d170603ffa45c..429c0f1f12866a 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -133,6 +133,18 @@ add_gen_header( .llvm-libc-types.size_t ) +add_gen_header( + search + DEF_FILE search.h.def + GEN_HDR search.h + DEPENDS + .llvm_libc_common_h + .llvm-libc-types.ACTION + .llvm-libc-types.ENTRY + .llvm-libc-types.struct_hsearch_data + .llvm-libc-types.size_t +) + add_gen_header( time DEF_FILE time.h.def diff --git a/libc/include/llvm-libc-types/ACTION.h b/libc/include/llvm-libc-types/ACTION.h new file mode 100644 index 00000000000000..7181a59b177d6b --- /dev/null +++ b/libc/include/llvm-libc-types/ACTION.h @@ -0,0 +1,14 @@ +//===-- Definition of ACTION type -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_ACTION_H__ +#define __LLVM_LIBC_TYPES_ACTION_H__ + +typedef enum { FIND, ENTER } ACTION; + +#endif // __LLVM_LIBC_TYPES_ACTION_H__ diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index 3c0cc7bbc71dac..225ad780c4d01f 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -91,3 +91,6 @@ add_header(wint_t HDR wint_t.h) add_header(sa_family_t HDR sa_family_t.h) add_header(struct_sockaddr HDR struct_sockaddr.h) add_header(rpc_opcodes_t HDR rpc_opcodes_t.h) +add_header(ACTION HDR ACTION.h) +add_header(ENTRY HDR ENTRY.h) +add_header(struct_hsearch_data HDR struct_hsearch_data.h) diff --git a/libc/include/llvm-libc-types/ENTRY.h b/libc/include/llvm-libc-types/ENTRY.h new file mode 100644 index 00000000000000..0ccb5938207acc --- /dev/null +++ b/libc/include/llvm-libc-types/ENTRY.h @@ -0,0 +1,17 @@ +//===-- Definition of ENTRY type ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_ENTRY_H__ +#define __LLVM_LIBC_TYPES_ENTRY_H__ + +typedef struct { + char *key; + void *data; +} ENTRY; + +#endif // __LLVM_LIBC_TYPES_ENTRY_H__ diff --git a/libc/include/llvm-libc-types/struct_hsearch_data.h b/libc/include/llvm-libc-types/struct_hsearch_data.h new file mode 100644 index 00000000000000..7e2a7232fce535 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_hsearch_data.h @@ -0,0 +1,17 @@ +//===-- Definition of type struct hsearch_data ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_STRUCT_HSEARCH_DATA_H__ +#define __LLVM_LIBC_TYPES_STRUCT_HSEARCH_DATA_H__ + +struct hsearch_data { + void *__opaque; + unsigned int __unused[2]; +}; + +#endif // __LLVM_LIBC_TYPES_STRUCT_HSEARCH_DATA_H__ diff --git a/libc/include/search.h.def b/libc/include/search.h.def new file mode 100644 index 00000000000000..3435c1f8ad048e --- /dev/null +++ b/libc/include/search.h.def @@ -0,0 +1,18 @@ +//===-- POSIX header search.h ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SEARCH_H +#define LLVM_LIBC_SEARCH_H + +#include <__llvm-libc-common.h> +#define __need_size_t +#include + +%%public_api() + +#endif // LLVM_LIBC_SEARCH_H diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td index dfb12419d14005..cb0407c84d4e21 100644 --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -3,6 +3,8 @@ def CpuSetPtr : PtrType; def ConstCpuSetPtr : ConstType; def QSortRCompareT : NamedType<"__qsortrcompare_t">; +def StructHsearchData : NamedType<"struct hsearch_data">; +def StructHsearchDataPtr : PtrType; def GnuExtensions : StandardSpec<"GNUExtensions"> { NamedType CookieIOFunctionsT = NamedType<"cookie_io_functions_t">; @@ -54,7 +56,6 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> { >, ] >; - HeaderSpec String = HeaderSpec< "string.h", [], // Macros @@ -89,6 +90,42 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> { ] >; + HeaderSpec Search = HeaderSpec< + "search.h", + [], // Macros + [ + StructHsearchData + ], + [], // Enumerations + [ + FunctionSpec< + "hcreate_r", + RetValSpec, + [ + ArgSpec, + ArgSpec + ] + >, + FunctionSpec< + "hdestroy_r", + RetValSpec, + [ + ArgSpec + ] + >, + FunctionSpec< + "hsearch_r", + RetValSpec, + [ + ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec + ] + >, + ] + >; + HeaderSpec FEnv = HeaderSpec< "fenv.h", [], // Macros @@ -243,6 +280,7 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> { StdIO, StdLib, String, + Search, UniStd, ]; } diff --git a/libc/spec/posix.td b/libc/spec/posix.td index a367cf2a6935c0..c7acf6d25a2d87 100644 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -1269,6 +1269,38 @@ def POSIX : StandardSpec<"POSIX"> { ] >; + HeaderSpec Search = HeaderSpec< + "search.h", + [], // Macros + [ + ActionType, + EntryType + ], // Types + [], // Enumerations + [ + FunctionSpec< + "hcreate", + RetValSpec, + [ + ArgSpec + ] + >, + FunctionSpec< + "hdestroy", + RetValSpec, + [] // Args + >, + FunctionSpec< + "hsearch", + RetValSpec, + [ + ArgSpec, + ArgSpec + ] + >, + ] + >; + HeaderSpec Termios = HeaderSpec< "termios.h", [ @@ -1414,6 +1446,7 @@ def POSIX : StandardSpec<"POSIX"> { Time, Termios, UniStd, - String + String, + Search, ]; } diff --git a/libc/spec/spec.td b/libc/spec/spec.td index b0d5511a4f087e..9b689b5eb502a9 100644 --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -140,6 +140,11 @@ def SuSecondsT : NamedType<"suseconds_t">; //added because __assert_fail needs it. def UnsignedType : NamedType<"unsigned">; +def ActionType : NamedType<"ACTION">; +def EntryType : NamedType<"ENTRY">; +def EntryTypePtr : PtrType; +def EntryTypePtrPtr : PtrType; + class Macro { string Name = name; } diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt index 88838eecc53c9a..3ab62a4f667d26 100644 --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -35,3 +35,4 @@ add_subdirectory(signal) add_subdirectory(spawn) add_subdirectory(threads) add_subdirectory(time) +add_subdirectory(search) diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt index b939fae3be791d..a76b22960f5a50 100644 --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -32,6 +32,7 @@ add_header_library( bit.h DEPENDS libc.src.__support.macros.attributes + libc.src.__support.CPP.type_traits ) add_header_library( @@ -230,6 +231,28 @@ add_header_library( libc.src.__support.OSUtil.osutil ) +add_header_library( + hash + HDRS + hash.h + DEPENDS + .bit + .uint128 + libc.src.__support.macros.attributes +) + +add_header_library( + memory_size + HDRS + memory_size.h + DEPENDS + libc.src.__support.CPP.type_traits + libc.src.__support.CPP.limits + libc.src.__support.macros.optimization + libc.src.__support.macros.attributes + libc.src.__support.macros.config +) + add_subdirectory(FPUtil) add_subdirectory(OSUtil) add_subdirectory(StringUtil) @@ -241,3 +264,5 @@ add_subdirectory(RPC) add_subdirectory(threads) add_subdirectory(File) + +add_subdirectory(HashTable) diff --git a/libc/src/__support/HashTable/CMakeLists.txt b/libc/src/__support/HashTable/CMakeLists.txt new file mode 100644 index 00000000000000..238d460dacd428 --- /dev/null +++ b/libc/src/__support/HashTable/CMakeLists.txt @@ -0,0 +1,52 @@ +# TODO: `DISABLE_SSE2_OPT` does not quite work yet. +# We will investigate a better way of feature flag control. +add_header_library( + bitmask + HDRS + bitmask.h + FLAGS + DISABLE_SSE2_OPT + DEPENDS + libc.src.__support.bit + libc.src.__support.macros.properties.cpu_features +) + +list(FIND TARGET_ENTRYPOINT_NAME_LIST getrandom getrandom_index) +if (NOT ${getrandom_index} EQUAL -1) + message(STATUS "Using getrandom for hashtable randomness") + set(randomness_compile_flags -DLIBC_HASHTABLE_USE_GETRANDOM) + set(randomness_extra_depends + libc.src.sys.random.getrandom libc.src.errno.errno) +endif() + + +add_header_library( + table + HDRS + table.h + DEPENDS + .bitmask + libc.src.__support.memory_size + libc.src.__support.bit + libc.src.__support.CPP.type_traits + libc.src.__support.CPP.new + libc.src.__support.macros.attributes + libc.src.__support.macros.optimization + libc.src.__support.hash + libc.src.string.memset + libc.src.string.strcmp + libc.src.string.strlen + libc.include.llvm-libc-types.ENTRY +) + +add_header_library( + randomness + HDRS + randomness.h + COMPILE_OPTIONS + ${randomness_compile_flags} + DEPENDS + libc.src.__support.hash + libc.src.__support.common + ${randomness_extra_depends} +) diff --git a/libc/src/__support/HashTable/bitmask.h b/libc/src/__support/HashTable/bitmask.h new file mode 100644 index 00000000000000..761125feb951d9 --- /dev/null +++ b/libc/src/__support/HashTable/bitmask.h @@ -0,0 +1,94 @@ +//===-- HashTable BitMasks --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_HASHTABLE_BITMASK_H +#define LLVM_LIBC_SRC___SUPPORT_HASHTABLE_BITMASK_H + +#include "src/__support/bit.h" +#include "src/__support/macros/properties/cpu_features.h" +#include // size_t +#include // uint8_t, uint64_t + +namespace LIBC_NAMESPACE { +namespace internal { + +// Implementations of the bitmask. +// The backend word type may vary depending on different microarchitectures. +// For example, with X86 SSE2, the bitmask is just the 16bit unsigned integer +// corresponding to lanes in a SIMD register. +// +// Notice that this implementation is simplified from traditional swisstable: +// since we do not support deletion, we only need to care about if the highest +// bit is set or not: +// ============================= +// | Slot Status | Bitmask | +// ============================= +// | Available | 0b1xxx'xxxx | +// | Occupied | 0b0xxx'xxxx | +// ============================= +template struct BitMaskAdaptor { + // A masked constant whose bits are all set. + LIBC_INLINE_VAR constexpr static T MASK = WORD_MASK; + // A stride in the bitmask may use multiple bits. + LIBC_INLINE_VAR constexpr static size_t STRIDE = WORD_STRIDE; + + T word; + + // Check if any bit is set inside the word. + LIBC_INLINE constexpr bool any_bit_set() const { return word != 0; } + + // Count trailing zeros with respect to stride. (Assume the bitmask is none + // zero.) + LIBC_INLINE constexpr size_t lowest_set_bit_nonzero() const { + return unsafe_ctz(word) / WORD_STRIDE; + } +}; + +// Not all bitmasks are iterable --- only those who has only MSB set in each +// lane. Hence, we make the types nomially different to distinguish them. +template struct IteratableBitMaskAdaptor : public BitMask { + // Use the bitmask as an iterator. Update the state and return current lowest + // set bit. To make the bitmask iterable, each stride must contain 0 or exact + // 1 set bit. + LIBC_INLINE void remove_lowest_bit() { + // Remove the last set bit inside the word: + // word = 011110100 (original value) + // word - 1 = 011110011 (invert all bits up to the last set bit) + // word & (word - 1) = 011110000 (value with the last bit cleared) + this->word = this->word & (this->word - 1); + } + using value_type = size_t; + using iterator = BitMask; + using const_iterator = BitMask; + LIBC_INLINE size_t operator*() const { + return this->lowest_set_bit_nonzero(); + } + LIBC_INLINE IteratableBitMaskAdaptor &operator++() { + this->remove_lowest_bit(); + return *this; + } + LIBC_INLINE IteratableBitMaskAdaptor begin() { return *this; } + LIBC_INLINE IteratableBitMaskAdaptor end() { return {0}; } + LIBC_INLINE bool operator==(const IteratableBitMaskAdaptor &other) { + return this->word == other.word; + } + LIBC_INLINE bool operator!=(const IteratableBitMaskAdaptor &other) { + return this->word != other.word; + } +}; + +} // namespace internal +} // namespace LIBC_NAMESPACE + +#if defined(LIBC_TARGET_CPU_HAS_SSE2) +#include "sse2/bitmask_impl.inc" +#else +#include "generic/bitmask_impl.inc" +#endif + +#endif // LLVM_LIBC_SRC___SUPPORT_HASHTABLE_BITMASK_H diff --git a/libc/src/__support/HashTable/generic/bitmask_impl.inc b/libc/src/__support/HashTable/generic/bitmask_impl.inc new file mode 100644 index 00000000000000..13e08382adf622 --- /dev/null +++ b/libc/src/__support/HashTable/generic/bitmask_impl.inc @@ -0,0 +1,102 @@ +//===-- HashTable BitMasks Generic Implementation ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/endian.h" + +namespace LIBC_NAMESPACE { +namespace internal { +// Helper function to spread a byte across the whole word. +// Accumutively, the procedure looks like: +// byte = 0x00000000000000ff +// byte | (byte << 8) = 0x000000000000ffff +// byte | (byte << 16) = 0x00000000ffffffff +// byte | (byte << 32) = 0xffffffffffffffff +LIBC_INLINE constexpr uintptr_t repeat_byte(uintptr_t byte) { + size_t shift_amount = 8; + while (shift_amount < sizeof(uintptr_t) * 8) { + byte |= byte << shift_amount; + shift_amount <<= 1; + } + return byte; +} + +using BitMask = BitMaskAdaptor; +using IteratableBitMask = IteratableBitMaskAdaptor; + +struct Group { + uintptr_t data; + + // Load a group of control words from an arbitary address. + LIBC_INLINE static Group load(const void *__restrict addr) { + union { + uintptr_t value; + char bytes[sizeof(uintptr_t)]; + } data; + for (size_t i = 0; i < sizeof(uintptr_t); ++i) + data.bytes[i] = static_cast(addr)[i]; + return {data.value}; + } + + // Find out the lanes equal to the given byte and return the bitmask + // with corresponding bits set. + LIBC_INLINE IteratableBitMask match_byte(uint8_t byte) const { + // Given byte = 0x10, suppose the data is: + // + // data = [ 0x10 | 0x10 | 0x00 | 0xF1 | ... ] + // + // First, we compare the byte using XOR operation: + // + // [ 0x10 | 0x10 | 0x10 | 0x10 | ... ] (0) + // ^ [ 0x10 | 0x10 | 0x00 | 0xF1 | ... ] (1) + // = [ 0x00 | 0x00 | 0x10 | 0xE1 | ... ] (2) + // + // Notice that the equal positions will now be 0x00, so if we substract 0x01 + // respective to every byte, it will need to carry the substraction to upper + // bits (assume no carry from the hidden parts) + // [ 0x00 | 0x00 | 0x10 | 0xE1 | ... ] (2) + // - [ 0x01 | 0x01 | 0x01 | 0x01 | ... ] (3) + // = [ 0xFE | 0xFF | 0x0F | 0xE0 | ... ] (4) + // + // But there may be some bytes whose highest bit is already set after the + // xor operation. To rule out these positions, we AND them with the NOT + // of the XOR result: + // + // [ 0xFF | 0xFF | 0xEF | 0x1E | ... ] (5, NOT (2)) + // & [ 0xFE | 0xFF | 0x0F | 0xE0 | ... ] (4) + // = [ 0xFE | 0xFF | 0x0F | 0x10 | ... ] (6) + // + // To make the bitmask iteratable, only one bit can be set in each stride. + // So we AND each byte with 0x80 and keep only the highest bit: + // + // [ 0xFE | 0xFF | 0x0F | 0x10 | ... ] (6) + // & [ 0x80 | 0x80 | 0x80 | 0x80 | ... ] (7) + // = [ 0x80 | 0x80 | 0x00 | 0x00 | ... ] (8) + // + // However, there are possitbilites for false positives. For example, if the + // data is [ 0x10 | 0x11 | 0x10 | 0xF1 | ... ]. This only happens when there + // is a key only differs from the searched by the lowest bit. The claims + // are: + // + // - This never happens for `EMPTY` and `DELETED`, only full entries. + // - The check for key equality will catch these. + // - This only happens if there is at least 1 true match. + // - The chance of this happening is very low (< 1% chance per byte). + auto cmp = data ^ repeat_byte(byte); + auto result = LIBC_NAMESPACE::Endian::to_little_endian( + (cmp - repeat_byte(0x01)) & ~cmp & repeat_byte(0x80)); + return {result}; + } + + // Find out the lanes equal to EMPTY or DELETE (highest bit set) and + // return the bitmask with corresponding bits set. + LIBC_INLINE BitMask mask_available() const { + return {LIBC_NAMESPACE::Endian::to_little_endian(data) & repeat_byte(0x80)}; + } +}; +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/src/__support/HashTable/randomness.h b/libc/src/__support/HashTable/randomness.h new file mode 100644 index 00000000000000..bcc91190e9d452 --- /dev/null +++ b/libc/src/__support/HashTable/randomness.h @@ -0,0 +1,62 @@ +//===-- HashTable Randomness ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_HASHTABLE_RANDOMNESS_H +#define LLVM_LIBC_SRC___SUPPORT_HASHTABLE_RANDOMNESS_H + +#include "src/__support/common.h" +#include "src/__support/hash.h" +#include "src/__support/macros/attributes.h" +#if defined(LIBC_HASHTABLE_USE_GETRANDOM) +#include "src/errno/libc_errno.h" +#include "src/sys/random/getrandom.h" +#endif + +namespace LIBC_NAMESPACE { +namespace internal { +namespace randomness { +// We need an initial state for the hash function. More entropy are to be added +// at the first use and each round of reseeding. The following random numbers +// are generated from https://www.random.org/cgi-bin/randbyte?nbytes=64&format=h +LIBC_INLINE_VAR thread_local HashState state = { + 0x38049a7ea6f5a79b, 0x45cb02147c3f718a, 0x53eb431c12770718, + 0x5b55742bd20a2fcb}; +LIBC_INLINE_VAR thread_local uint64_t counter = 0; +LIBC_INLINE_VAR constexpr uint64_t RESEED_PERIOD = 1024; +LIBC_INLINE uint64_t next_random_seed() { + if (counter % RESEED_PERIOD == 0) { + uint64_t entropy[2]; + entropy[0] = reinterpret_cast(&entropy); + entropy[1] = reinterpret_cast(&state); +#if defined(LIBC_HASHTABLE_USE_GETRANDOM) + int errno_backup = libc_errno; + ssize_t count = sizeof(entropy); + uint8_t *buffer = reinterpret_cast(entropy); + while (count > 0) { + ssize_t len = getrandom(buffer, count, 0); + if (len == -1) { + if (libc_errno == ENOSYS) + break; + continue; + } + count -= len; + buffer += len; + } + libc_errno = errno_backup; +#endif + state.update(&entropy, sizeof(entropy)); + } + state.update(&counter, sizeof(counter)); + counter++; + return state.finish(); +} + +} // namespace randomness +} // namespace internal +} // namespace LIBC_NAMESPACE +#endif // LLVM_LIBC_SRC___SUPPORT_HASHTABLE_RANDOMNESS_H diff --git a/libc/src/__support/HashTable/sse2/bitmask_impl.inc b/libc/src/__support/HashTable/sse2/bitmask_impl.inc new file mode 100644 index 00000000000000..6308f2fed6661c --- /dev/null +++ b/libc/src/__support/HashTable/sse2/bitmask_impl.inc @@ -0,0 +1,40 @@ +//===-- HashTable BitMasks SSE2 Implementation ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +namespace LIBC_NAMESPACE { +namespace internal { +// With SSE2, every bitmask is iteratable as +// we use single bit to encode the data. + +using BitMask = BitMaskAdaptor; +using IteratableBitMask = IteratableBitMaskAdaptor; + +struct Group { + __m128i data; + + // Load a group of control words from an arbitary address. + LIBC_INLINE static Group load(const void *__restrict addr) { + return {_mm_loadu_si128(static_cast(addr))}; + } + + // Find out the lanes equal to the given byte and return the bitmask + // with corresponding bits set. + LIBC_INLINE IteratableBitMask match_byte(uint8_t byte) const { + auto cmp = _mm_cmpeq_epi8(data, _mm_set1_epi8(byte)); + auto bitmask = static_cast(_mm_movemask_epi8(cmp)); + return {bitmask}; + } + + LIBC_INLINE BitMask mask_available() const { + auto bitmask = static_cast(_mm_movemask_epi8(data)); + return {bitmask}; + } +}; +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/src/__support/HashTable/table.h b/libc/src/__support/HashTable/table.h new file mode 100644 index 00000000000000..ec0ec78869ad58 --- /dev/null +++ b/libc/src/__support/HashTable/table.h @@ -0,0 +1,235 @@ +//===-- Fix-sized Monotonic HashTable ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_HASHTABLE_table_H +#define LLVM_LIBC_SRC___SUPPORT_HASHTABLE_table_H + +#include "include/llvm-libc-types/ENTRY.h" +#include "src/__support/CPP/new.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/HashTable/bitmask.h" +#include "src/__support/bit.h" +#include "src/__support/hash.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/optimization.h" +#include "src/__support/memory_size.h" +#include "src/string/memset.h" +#include "src/string/strcmp.h" +#include "src/string/strlen.h" +#include +#include + +namespace LIBC_NAMESPACE { +namespace internal { + +LIBC_INLINE uint8_t secondary_hash(uint64_t hash) { + // top 7 bits of the hash. + return static_cast(hash >> 57); +} + +// Probe sequence based on triangular numbers, which is guaranteed (since our +// table size is a power of two) to visit every group of elements exactly once. +// +// A triangular probe has us jump by 1 more group every time. So first we +// jump by 1 group (meaning we just continue our linear scan), then 2 groups +// (skipping over 1 group), then 3 groups (skipping over 2 groups), and so on. +// +// If we set sizeof(Group) to be one unit: +// T[k] = sum {1 + 2 + ... + k} = k * (k + 1) / 2 +// It is provable that T[k] mod 2^m generates a permutation of +// 0, 1, 2, 3, ..., 2^m - 2, 2^m - 1 +// Detailed proof is available at: +// https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/ +struct ProbeSequence { + size_t position; + size_t stride; + size_t entries_mask; + + LIBC_INLINE size_t next() { + position += stride; + position &= entries_mask; + stride += sizeof(Group); + return position; + } +}; + +// The number of entries is at least group width: we do not +// need to do the fixup when we set the control bytes. +// The number of entries is at least 8: we don't have to worry +// about special sizes when check the fullness of the table. +LIBC_INLINE size_t capacity_to_entries(size_t cap) { + if (8 >= sizeof(Group) && cap < 8) + return 8; + if (16 >= sizeof(Group) && cap < 15) + return 16; + if (cap < sizeof(Group)) + cap = sizeof(Group); + // overflow is always checked in allocate() + return next_power_of_two(cap * 8 / 7); +} + +// The heap memory layout for N buckets HashTable is as follows: +// +// ======================= +// | N * Entry | +// ======================= <- align boundary +// | Header | +// ======================= +// | (N + 1) * Byte | +// ======================= +// +// The trailing group part is to make sure we can always load +// a whole group of control bytes. + +struct HashTable { + HashState state; + size_t entries_mask; // number of buckets - 1 + size_t available_slots; // less than capacity +private: + // How many entries are there in the table. + LIBC_INLINE size_t num_of_entries() const { return entries_mask + 1; } + + LIBC_INLINE bool is_full() const { return available_slots == 0; } + + LIBC_INLINE size_t offset_from_entries() const { + size_t entries_size = num_of_entries() * sizeof(ENTRY); + return entries_size + offset_to(entries_size, table_alignment()); + } + + LIBC_INLINE constexpr static size_t table_alignment() { + return alignof(HashTable) > alignof(ENTRY) ? alignof(HashTable) + : alignof(ENTRY); + } + + LIBC_INLINE constexpr static size_t offset_to_groups() { + return sizeof(HashTable); + } + + LIBC_INLINE ENTRY &entry(size_t i) { + return reinterpret_cast(this)[-i - 1]; + } + + LIBC_INLINE uint8_t &control(size_t i) { + uint8_t *ptr = reinterpret_cast(this) + offset_to_groups(); + return ptr[i]; + } + + // We duplicate a group of control bytes to the end. Thus, it is possible that + // we need to set two control bytes at the same time. + LIBC_INLINE void set_ctrl(size_t index, uint8_t value) { + size_t index2 = ((index - sizeof(Group)) & entries_mask) + sizeof(Group); + control(index) = value; + control(index2) = value; + } + +public: + LIBC_INLINE static void deallocate(HashTable *table) { + if (table) { + void *ptr = + reinterpret_cast(table) - table->offset_from_entries(); + operator delete(ptr, std::align_val_t{table_alignment()}); + } + } + LIBC_INLINE static HashTable *allocate(size_t capacity, uint64_t randomness) { + // check if capacity_to_entries overflows MAX_MEM_SIZE + if (capacity > size_t{1} << (8 * sizeof(size_t) - 1 - 3)) + return nullptr; + SafeMemSize entries{capacity_to_entries(capacity)}; + SafeMemSize entries_size = entries * SafeMemSize{sizeof(ENTRY)}; + SafeMemSize align_boundary = entries_size.align_up(table_alignment()); + SafeMemSize ctrl_sizes = entries + SafeMemSize{sizeof(Group)}; + SafeMemSize header_size{offset_to_groups()}; + SafeMemSize total_size = + (align_boundary + header_size + ctrl_sizes).align_up(table_alignment()); + if (!total_size.valid()) + return nullptr; + AllocChecker ac; + + void *mem = operator new(total_size, std::align_val_t{table_alignment()}, + ac); + + HashTable *table = reinterpret_cast( + static_cast(mem) + align_boundary); + if (ac) { + table->entries_mask = entries - 1u; + table->available_slots = entries / 8 * 7; + table->state = HashState{randomness}; + memset(&table->control(0), 0x80, ctrl_sizes); + memset(mem, 0, table->offset_from_entries()); + } + return table; + } + +private: + LIBC_INLINE size_t find(const char *key, uint64_t primary) { + uint8_t secondary = secondary_hash(primary); + ProbeSequence sequence{static_cast(primary), 0, entries_mask}; + while (true) { + size_t pos = sequence.next(); + Group ctrls = Group::load(&control(pos)); + IteratableBitMask masks = ctrls.match_byte(secondary); + for (size_t i : masks) { + size_t index = (pos + i) & entries_mask; + ENTRY &entry = this->entry(index); + if (LIBC_LIKELY(entry.key != nullptr && strcmp(entry.key, key) == 0)) + return index; + } + BitMask available = ctrls.mask_available(); + // Since there is no deletion, the first time we find an available slot + // it is also ready to be used as an insertion point. Therefore, we also + // return the first available slot we find. If such entry is empty, the + // key will be nullptr. + if (LIBC_LIKELY(available.any_bit_set())) { + size_t index = + (pos + available.lowest_set_bit_nonzero()) & entries_mask; + return index; + } + } + } + +private: + LIBC_INLINE ENTRY *insert(ENTRY item, uint64_t primary) { + auto index = find(item.key, primary); + auto slot = &this->entry(index); + // SVr4 and POSIX.1-2001 specify that action is significant only for + // unsuccessful searches, so that an ENTER should not do anything + // for a successful search. + if (slot->key != nullptr) + return slot; + + if (!is_full()) { + set_ctrl(index, secondary_hash(primary)); + slot->key = item.key; + slot->data = item.data; + available_slots--; + return slot; + } + return nullptr; + } + +public: + LIBC_INLINE ENTRY *find(const char *key) { + LIBC_NAMESPACE::internal::HashState hasher = state; + hasher.update(key, strlen(key)); + uint64_t primary = hasher.finish(); + ENTRY &entry = this->entry(find(key, primary)); + if (entry.key == nullptr) + return nullptr; + return &entry; + } + LIBC_INLINE ENTRY *insert(ENTRY item) { + LIBC_NAMESPACE::internal::HashState hasher = state; + hasher.update(item.key, strlen(item.key)); + uint64_t primary = hasher.finish(); + return insert(item, primary); + } +}; +} // namespace internal +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC___SUPPORT_HASHTABLE_table_H diff --git a/libc/src/__support/bit.h b/libc/src/__support/bit.h index d0a15c89b7b45e..ab2e07744a866f 100644 --- a/libc/src/__support/bit.h +++ b/libc/src/__support/bit.h @@ -10,6 +10,7 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_BIT_H #define LLVM_LIBC_SRC___SUPPORT_BIT_H +#include "src/__support/CPP/type_traits.h" // make_unsigned #include "src/__support/macros/attributes.h" // LIBC_INLINE namespace LIBC_NAMESPACE { @@ -28,6 +29,14 @@ template LIBC_INLINE int constexpr correct_zero(T val, int bits) { } template LIBC_INLINE constexpr int clz(T val); +template <> LIBC_INLINE int clz(unsigned char val) { + return __builtin_clz(static_cast(val)) - + 8 * (sizeof(unsigned int) - sizeof(unsigned char)); +} +template <> LIBC_INLINE int clz(unsigned short val) { + return __builtin_clz(static_cast(val)) - + 8 * (sizeof(unsigned int) - sizeof(unsigned short)); +} template <> LIBC_INLINE int clz(unsigned int val) { return __builtin_clz(val); } @@ -42,6 +51,12 @@ clz(unsigned long long int val) { } template LIBC_INLINE constexpr int ctz(T val); +template <> LIBC_INLINE int ctz(unsigned char val) { + return __builtin_ctz(static_cast(val)); +} +template <> LIBC_INLINE int ctz(unsigned short val) { + return __builtin_ctz(static_cast(val)); +} template <> LIBC_INLINE int ctz(unsigned int val) { return __builtin_ctz(val); } @@ -72,6 +87,31 @@ template LIBC_INLINE constexpr int unsafe_clz(T val) { return __internal::clz(val); } +template LIBC_INLINE constexpr T next_power_of_two(T val) { + if (val == 0) + return 1; + T idx = safe_clz(val - 1); + return static_cast(1) << ((8ull * sizeof(T)) - idx); +} + +template LIBC_INLINE constexpr bool is_power_of_two(T val) { + return val != 0 && (val & (val - 1)) == 0; +} + +template LIBC_INLINE constexpr T offset_to(T val, T align) { + return (-val) & (align - 1); +} + +template LIBC_INLINE constexpr T rotate_left(T val, T amount) { + // Implementation taken from "Safe, Efficient, and Portable Rotate in C/C++" + // https://blog.regehr.org/archives/1063 + // Using the safe version as the rotation pattern is now recognized by both + // GCC and Clang. + using U = cpp::make_unsigned_t; + U v = static_cast(val); + U a = static_cast(amount); + return (v << a) | (v >> ((-a) & (sizeof(U) * 8 - 1))); +} } // namespace LIBC_NAMESPACE #endif // LLVM_LIBC_SRC___SUPPORT_BIT_H diff --git a/libc/src/__support/hash.h b/libc/src/__support/hash.h new file mode 100644 index 00000000000000..ad12cf79e8d2cf --- /dev/null +++ b/libc/src/__support/hash.h @@ -0,0 +1,164 @@ +//===-- Portable string hash function ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_HASH_H +#define LLVM_LIBC_SRC___SUPPORT_HASH_H + +#include "src/__support/UInt128.h" // UInt128 +#include "src/__support/bit.h" // rotate_left +#include "src/__support/macros/attributes.h" // LIBC_INLINE +#include // For uint64_t + +namespace LIBC_NAMESPACE { +namespace internal { + +// Folded multiplication. +// This function multiplies two 64-bit integers and xor the high and +// low 64-bit parts of the result. +LIBC_INLINE uint64_t folded_multiply(uint64_t x, uint64_t y) { + UInt128 p = static_cast(x) * static_cast(y); + uint64_t low = static_cast(p); + uint64_t high = static_cast(p >> 64); + return low ^ high; +} + +// Read as little endian. +// Shift-and-or implementation does not give a satisfactory code on aarch64. +// Therefore, we use a union to read the value. +template LIBC_INLINE T read_little_endian(const void *ptr) { + const uint8_t *bytes = static_cast(ptr); + union { + T value; + uint8_t buffer[sizeof(T)]; + } data; +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + // Compiler should able to optimize this as a load followed by a byte swap. + // On aarch64 (-mbig-endian), this compiles to the following for int: + // ldr w0, [x0] + // rev w0, w0 + // ret + for (size_t i = 0; i < sizeof(T); ++i) { + data.buffer[i] = bytes[sizeof(T) - i - 1]; + } +#else + for (size_t i = 0; i < sizeof(T); ++i) { + data.buffer[i] = bytes[i]; + } +#endif + return data.value; +} + +// Specialized read functions for small values. size must be <= 8. +LIBC_INLINE void read_small_values(const void *ptr, size_t size, uint64_t &low, + uint64_t &high) { + const uint8_t *bytes = static_cast(ptr); + if (size >= 2) { + if (size >= 4) { + low = static_cast(read_little_endian(&bytes[0])); + high = + static_cast(read_little_endian(&bytes[size - 4])); + } else { + low = static_cast(read_little_endian(&bytes[0])); + high = static_cast(bytes[size - 1]); + } + } else { + if (size > 0) { + low = static_cast(bytes[0]); + high = static_cast(bytes[0]); + } else { + low = 0; + high = 0; + } + } +} + +// This constant comes from Kunth's prng (it empirically works well). +LIBC_INLINE_VAR constexpr uint64_t MULTIPLE = 6364136223846793005; +// Rotation amount for mixing. +LIBC_INLINE_VAR constexpr uint64_t ROTATE = 23; + +// Randomly generated values. For now, we use the same values as in aHash as +// they are widely tested. +// https://github.com/tkaitchuck/aHash/blob/9f6a2ad8b721fd28da8dc1d0b7996677b374357c/src/random_state.rs#L38 +LIBC_INLINE_VAR constexpr uint64_t RANDOMNESS[2][4] = { + {0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, + 0x082efa98ec4e6c89}, + {0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, + 0x3f84d5b5b5470917}, +}; + +// This is a portable string hasher. It is not cryptographically secure. +// The quality of the hash is good enough to pass all tests in SMHasher. +// The implementation is derived from the generic routine of aHash. +class HashState { + uint64_t buffer; + uint64_t pad; + uint64_t extra_keys[2]; + LIBC_INLINE void update(uint64_t low, uint64_t high) { + uint64_t combined = + folded_multiply(low ^ extra_keys[0], high ^ extra_keys[1]); + buffer = (buffer + pad) ^ combined; + buffer = rotate_left(buffer, ROTATE); + } + LIBC_INLINE static uint64_t mix(uint64_t seed) { + HashState mixer{RANDOMNESS[0][0], RANDOMNESS[0][1], RANDOMNESS[0][2], + RANDOMNESS[0][3]}; + mixer.update(seed, 0); + return mixer.finish(); + } + +public: + LIBC_INLINE constexpr HashState(uint64_t a, uint64_t b, uint64_t c, + uint64_t d) + : buffer(a), pad(b), extra_keys{c, d} {} + LIBC_INLINE HashState(uint64_t seed) { + // Mix one more round of the seed to make it stronger. + uint64_t mixed = mix(seed); + buffer = RANDOMNESS[1][0] ^ mixed; + pad = RANDOMNESS[1][1] ^ mixed; + extra_keys[0] = RANDOMNESS[1][2] ^ mixed; + extra_keys[1] = RANDOMNESS[1][3] ^ mixed; + } + LIBC_INLINE void update(const void *ptr, size_t size) { + uint8_t const *bytes = static_cast(ptr); + buffer = (buffer + size) * MULTIPLE; + uint64_t low, high; + if (size > 8) { + if (size > 16) { + // update tail + low = read_little_endian(&bytes[size - 16]); + high = read_little_endian(&bytes[size - 8]); + update(low, high); + while (size > 16) { + low = read_little_endian(&bytes[0]); + high = read_little_endian(&bytes[8]); + update(low, high); + bytes += 16; + size -= 16; + } + } else { + low = read_little_endian(&bytes[0]); + high = read_little_endian(&bytes[size - 8]); + update(low, high); + } + } else { + read_small_values(ptr, size, low, high); + update(low, high); + } + } + LIBC_INLINE uint64_t finish() { + uint64_t rot = buffer & 63; + uint64_t folded = folded_multiply(buffer, pad); + return rotate_left(folded, rot); + } +}; + +} // namespace internal +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC___SUPPORT_HASH_H diff --git a/libc/src/__support/memory_size.h b/libc/src/__support/memory_size.h new file mode 100644 index 00000000000000..df179a6604714b --- /dev/null +++ b/libc/src/__support/memory_size.h @@ -0,0 +1,72 @@ +//===-- Memory Size ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/limits.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/bit.h" +#include "src/__support/macros/attributes.h" +#include "src/__support/macros/optimization.h" + +namespace LIBC_NAMESPACE { +namespace internal { +template LIBC_INLINE bool mul_overflow(T a, T b, T *res) { +#if LIBC_HAS_BUILTIN(__builtin_mul_overflow) + return __builtin_mul_overflow(a, b, res); +#else + T max = cpp::numeric_limits::max(); + T min = cpp::numeric_limits::min(); + bool overflow = (b > 0 && (a > max / b || a < min / b)) || + (b < 0 && (a < max / b || a > min / b)); + if (!overflow) + *res = a * b; + return overflow; +#endif +} +// Limit memory size to the max of ssize_t +class SafeMemSize { +private: + using type = cpp::make_signed_t; + type value; + LIBC_INLINE explicit SafeMemSize(type value) : value(value) {} + +public: + LIBC_INLINE_VAR static constexpr size_t MAX_MEM_SIZE = + static_cast(cpp::numeric_limits::max()); + LIBC_INLINE explicit SafeMemSize(size_t value) + : value(value <= MAX_MEM_SIZE ? static_cast(value) : -1) {} + LIBC_INLINE operator size_t() { return static_cast(value); } + LIBC_INLINE bool valid() { return value >= 0; } + LIBC_INLINE SafeMemSize operator+(const SafeMemSize &other) { + type result; + if (LIBC_UNLIKELY((value | other.value) < 0)) + result = -1; + result = value + other.value; + return SafeMemSize{result}; + } + LIBC_INLINE SafeMemSize operator*(const SafeMemSize &other) { + type result; + if (LIBC_UNLIKELY((value | other.value) < 0)) + result = -1; + if (LIBC_UNLIKELY(mul_overflow(value, other.value, &result))) + result = -1; + return SafeMemSize{result}; + } + LIBC_INLINE SafeMemSize align_up(size_t alignment) { + if (!is_power_of_two(alignment) || alignment > MAX_MEM_SIZE || !valid()) + return SafeMemSize{type{-1}}; + + type offset = LIBC_NAMESPACE::offset_to(value, alignment); + + if (LIBC_UNLIKELY(offset > static_cast(MAX_MEM_SIZE) - value)) + return SafeMemSize{type{-1}}; + + return SafeMemSize{value + offset}; + } +}; +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/CMakeLists.txt b/libc/src/search/CMakeLists.txt new file mode 100644 index 00000000000000..4ae5274a3ba981 --- /dev/null +++ b/libc/src/search/CMakeLists.txt @@ -0,0 +1,79 @@ +add_subdirectory(hsearch) + +add_entrypoint_object( + hcreate + SRCS + hcreate.cpp + HDRS + hcreate.h + DEPENDS + libc.src.search.hsearch.global + libc.src.__support.HashTable.table + libc.src.__support.HashTable.randomness + libc.src.errno.errno + libc.include.search +) + +add_entrypoint_object( + hcreate_r + SRCS + hcreate_r.cpp + HDRS + hcreate_r.h + DEPENDS + libc.src.__support.HashTable.table + libc.src.__support.HashTable.randomness + libc.src.errno.errno + libc.include.search +) + +add_entrypoint_object( + hsearch + SRCS + hsearch.cpp + HDRS + hsearch.h + DEPENDS + libc.src.search.hsearch.global + libc.src.__support.HashTable.table + libc.src.__support.libc_assert + libc.src.errno.errno + libc.include.search +) + +add_entrypoint_object( + hsearch_r + SRCS + hsearch_r.cpp + HDRS + hsearch_r.h + DEPENDS + libc.src.__support.HashTable.table + libc.src.errno.errno + libc.include.search +) + +add_entrypoint_object( + hdestroy + SRCS + hdestroy.cpp + HDRS + hdestroy.h + DEPENDS + libc.src.search.hsearch.global + libc.src.__support.HashTable.table + libc.src.__support.libc_assert + libc.include.search +) + +add_entrypoint_object( + hdestroy_r + SRCS + hdestroy_r.cpp + HDRS + hdestroy_r.h + DEPENDS + libc.src.errno.errno + libc.src.__support.HashTable.table + libc.include.search +) diff --git a/libc/src/search/hcreate.cpp b/libc/src/search/hcreate.cpp new file mode 100644 index 00000000000000..9c05e317a2d05f --- /dev/null +++ b/libc/src/search/hcreate.cpp @@ -0,0 +1,28 @@ +//===-- Implementation of hcreate -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/search/hcreate.h" +#include "src/__support/HashTable/randomness.h" +#include "src/__support/HashTable/table.h" +#include "src/errno/libc_errno.h" +#include "src/search/hsearch/global.h" + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(int, hcreate, (size_t capacity)) { + uint64_t randomness = internal::randomness::next_random_seed(); + internal::HashTable *table = + internal::HashTable::allocate(capacity, randomness); + if (table == nullptr) { + libc_errno = ENOMEM; + return 0; + } + internal::global_hash_table = table; + return 1; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hcreate.h b/libc/src/search/hcreate.h new file mode 100644 index 00000000000000..2ac37fb030c26f --- /dev/null +++ b/libc/src/search/hcreate.h @@ -0,0 +1,18 @@ +//===-- Implementation header for hcreate -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SEARCH_HCREATE_H +#define LLVM_LIBC_SRC_SEARCH_HCREATE_H + +#include + +namespace LIBC_NAMESPACE { +int hcreate(size_t capacity); +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SEARCH_HCREATE_H diff --git a/libc/src/search/hcreate_r.cpp b/libc/src/search/hcreate_r.cpp new file mode 100644 index 00000000000000..612a45cd0c688b --- /dev/null +++ b/libc/src/search/hcreate_r.cpp @@ -0,0 +1,32 @@ +//===-- Implementation of hcreate_r -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/search/hcreate_r.h" +#include "src/__support/HashTable/randomness.h" +#include "src/__support/HashTable/table.h" +#include "src/errno/libc_errno.h" + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(int, hcreate_r, + (size_t capacity, struct hsearch_data *htab)) { + if (htab == nullptr) { + libc_errno = EINVAL; + return 0; + } + uint64_t randomness = internal::randomness::next_random_seed(); + internal::HashTable *table = + internal::HashTable::allocate(capacity, randomness); + if (table == nullptr) { + libc_errno = ENOMEM; + return 0; + } + htab->__opaque = table; + return 1; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hcreate_r.h b/libc/src/search/hcreate_r.h new file mode 100644 index 00000000000000..e81895ef815c9f --- /dev/null +++ b/libc/src/search/hcreate_r.h @@ -0,0 +1,18 @@ +//===-- Implementation header for hcreate_r ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SEARCH_HCREATE_R_H +#define LLVM_LIBC_SRC_SEARCH_HCREATE_R_H + +#include + +namespace LIBC_NAMESPACE { +int hcreate_r(size_t capacity, struct hsearch_data *htab); +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SEARCH_HCREATE_R_H diff --git a/libc/src/search/hdestroy.cpp b/libc/src/search/hdestroy.cpp new file mode 100644 index 00000000000000..1af64f195e326e --- /dev/null +++ b/libc/src/search/hdestroy.cpp @@ -0,0 +1,21 @@ +//===-- Implementation of hdestroy ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/search/hdestroy.h" +#include "src/__support/HashTable/table.h" +#include "src/__support/libc_assert.h" +#include "src/search/hsearch/global.h" + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(void, hdestroy, (void)) { + LIBC_ASSERT(internal::global_hash_table != nullptr); + internal::HashTable::deallocate(internal::global_hash_table); + internal::global_hash_table = nullptr; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hdestroy.h b/libc/src/search/hdestroy.h new file mode 100644 index 00000000000000..b81e309a6bc809 --- /dev/null +++ b/libc/src/search/hdestroy.h @@ -0,0 +1,18 @@ +//===-- Implementation header for hdestroy -----------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SEARCH_HDESTROY_H +#define LLVM_LIBC_SRC_SEARCH_HDESTROY_H + +#include + +namespace LIBC_NAMESPACE { +void hdestroy(void); +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SEARCH_HDESTROY_H diff --git a/libc/src/search/hdestroy_r.cpp b/libc/src/search/hdestroy_r.cpp new file mode 100644 index 00000000000000..e2fda93931f782 --- /dev/null +++ b/libc/src/search/hdestroy_r.cpp @@ -0,0 +1,25 @@ +//===-- Implementation of hdestroy_r ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/search/hdestroy_r.h" +#include "src/__support/HashTable/table.h" +#include "src/errno/libc_errno.h" + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(void, hdestroy_r, (struct hsearch_data * htab)) { + if (htab == nullptr) { + libc_errno = EINVAL; + return; + } + internal::HashTable *table = + static_cast(htab->__opaque); + internal::HashTable::deallocate(table); + htab->__opaque = nullptr; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hdestroy_r.h b/libc/src/search/hdestroy_r.h new file mode 100644 index 00000000000000..503af417944488 --- /dev/null +++ b/libc/src/search/hdestroy_r.h @@ -0,0 +1,18 @@ +//===-- Implementation header for hdestroy_r ---------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SEARCH_HDESTROY_R_H +#define LLVM_LIBC_SRC_SEARCH_HDESTROY_R_H + +#include + +namespace LIBC_NAMESPACE { +void hdestroy_r(struct hsearch_data *htab); +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SEARCH_HDESTROY_R_H diff --git a/libc/src/search/hsearch.cpp b/libc/src/search/hsearch.cpp new file mode 100644 index 00000000000000..3a0d09aae835b0 --- /dev/null +++ b/libc/src/search/hsearch.cpp @@ -0,0 +1,36 @@ +//===-- Implementation of hsearch -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/search/hsearch.h" +#include "src/__support/HashTable/table.h" +#include "src/__support/libc_assert.h" +#include "src/errno/libc_errno.h" +#include "src/search/hsearch/global.h" + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(ENTRY *, hsearch, (ENTRY item, ACTION action)) { + ENTRY *result; + LIBC_ASSERT(internal::global_hash_table != nullptr); + switch (action) { + case FIND: + result = internal::global_hash_table->find(item.key); + if (result == nullptr) { + libc_errno = ESRCH; + } + break; + case ENTER: + result = internal::global_hash_table->insert(item); + if (result == nullptr) { + libc_errno = ENOMEM; + } + break; + } + return result; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hsearch.h b/libc/src/search/hsearch.h new file mode 100644 index 00000000000000..32dc073a49b834 --- /dev/null +++ b/libc/src/search/hsearch.h @@ -0,0 +1,18 @@ +//===-- Implementation header for hsearch -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SEARCH_HSEARCH_H +#define LLVM_LIBC_SRC_SEARCH_HSEARCH_H + +#include // ENTRY, ACTION + +namespace LIBC_NAMESPACE { +ENTRY *hsearch(ENTRY item, ACTION action); +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SEARCH_HSEARCH_H diff --git a/libc/src/search/hsearch/CMakeLists.txt b/libc/src/search/hsearch/CMakeLists.txt new file mode 100644 index 00000000000000..17289f03d0628f --- /dev/null +++ b/libc/src/search/hsearch/CMakeLists.txt @@ -0,0 +1,7 @@ +add_object_library( + global + SRCS + global.cpp + HDRS + global.h +) diff --git a/libc/src/search/hsearch/global.cpp b/libc/src/search/hsearch/global.cpp new file mode 100644 index 00000000000000..b6782ada50de45 --- /dev/null +++ b/libc/src/search/hsearch/global.cpp @@ -0,0 +1,13 @@ +//===-- Global hashtable implementation -----------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +namespace LIBC_NAMESPACE { +namespace internal { +struct HashTable *global_hash_table = nullptr; +} +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hsearch/global.h b/libc/src/search/hsearch/global.h new file mode 100644 index 00000000000000..292008cb0c8075 --- /dev/null +++ b/libc/src/search/hsearch/global.h @@ -0,0 +1,13 @@ +//===-- Global hashtable header -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +namespace LIBC_NAMESPACE { +namespace internal { +extern struct HashTable *global_hash_table; +} +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hsearch_r.cpp b/libc/src/search/hsearch_r.cpp new file mode 100644 index 00000000000000..958fba7c00d0d4 --- /dev/null +++ b/libc/src/search/hsearch_r.cpp @@ -0,0 +1,42 @@ +//===-- Implementation of hsearch_r -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/search/hsearch_r.h" +#include "src/__support/HashTable/table.h" +#include "src/errno/libc_errno.h" + +namespace LIBC_NAMESPACE { +LLVM_LIBC_FUNCTION(int, hsearch_r, + (ENTRY item, ACTION action, ENTRY **retval, + struct hsearch_data *htab)) { + if (htab == nullptr) { + libc_errno = EINVAL; + return 0; + } + internal::HashTable *table = + static_cast(htab->__opaque); + switch (action) { + case FIND: + *retval = table->find(item.key); + if (*retval == nullptr) { + libc_errno = ESRCH; + return 0; + } + break; + case ENTER: + *retval = table->insert(item); + if (*retval == nullptr) { + libc_errno = ENOMEM; + return 0; + } + break; + } + return 1; +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/search/hsearch_r.h b/libc/src/search/hsearch_r.h new file mode 100644 index 00000000000000..d36094c2eba585 --- /dev/null +++ b/libc/src/search/hsearch_r.h @@ -0,0 +1,19 @@ +//===-- Implementation header for hsearch_r ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SEARCH_HSEARCH_R_H +#define LLVM_LIBC_SRC_SEARCH_HSEARCH_R_H + +#include // ENTRY, ACTION + +namespace LIBC_NAMESPACE { +int hsearch_r(ENTRY item, ACTION action, ENTRY **retval, + struct hsearch_data *htab); +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_SEARCH_HSEARCH_R_H diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt index 52452cd1037dbf..c45b94f364397e 100644 --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -46,6 +46,7 @@ add_subdirectory(stdlib) add_subdirectory(inttypes) add_subdirectory(stdio) add_subdirectory(wchar) +add_subdirectory(search) if(${LIBC_TARGET_OS} STREQUAL "linux") add_subdirectory(fcntl) diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt index 2920535fbaa56e..2b9fa93bb548e5 100644 --- a/libc/test/src/__support/CMakeLists.txt +++ b/libc/test/src/__support/CMakeLists.txt @@ -126,6 +126,32 @@ add_libc_test( libc.src.__support.char_vector ) +add_libc_test( + hash_test + SUITE + libc-support-tests + SRCS + hash_test.cpp + DEPENDS + libc.src.__support.hash + libc.src.__support.CPP.new + libc.src.stdlib.rand + libc.src.stdlib.srand + libc.src.string.memset + UNIT_TEST_ONLY + # Aligned Allocation is not supported in hermetic builds. +) + +add_libc_test( + memory_size_test + SUITE + libc-support-tests + SRCS + memory_size_test.cpp + DEPENDS + libc.src.__support.memory_size +) + add_executable( libc_str_to_float_comparison_test str_to_float_comparison_test.cpp @@ -155,3 +181,4 @@ add_subdirectory(File) add_subdirectory(RPC) add_subdirectory(OSUtil) add_subdirectory(FPUtil) +add_subdirectory(HashTable) diff --git a/libc/test/src/__support/HashTable/CMakeLists.txt b/libc/test/src/__support/HashTable/CMakeLists.txt new file mode 100644 index 00000000000000..ee8dde107c3fe7 --- /dev/null +++ b/libc/test/src/__support/HashTable/CMakeLists.txt @@ -0,0 +1,33 @@ +add_libc_test( + bitmask_test + SUITE + libc-support-tests + SRCS + bitmask_test.cpp + DEPENDS + libc.src.__support.HashTable.bitmask +) + +add_libc_test( + table_test + SUITE + libc-support-tests + SRCS + table_test.cpp + DEPENDS + libc.src.__support.HashTable.randomness + libc.src.__support.HashTable.table + libc.src.__support.common + UNIT_TEST_ONLY +) + +add_libc_test( + group_test + SUITE + libc-support-tests + SRCS + group_test.cpp + DEPENDS + libc.src.__support.HashTable.bitmask + libc.src.stdlib.rand +) diff --git a/libc/test/src/__support/HashTable/bitmask_test.cpp b/libc/test/src/__support/HashTable/bitmask_test.cpp new file mode 100644 index 00000000000000..c816c5d1063889 --- /dev/null +++ b/libc/test/src/__support/HashTable/bitmask_test.cpp @@ -0,0 +1,69 @@ +//===-- Unittests for bitmask ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/HashTable/bitmask.h" +#include "test/UnitTest/Test.h" +namespace LIBC_NAMESPACE { +namespace internal { + +using ShortBitMask = BitMaskAdaptor; +using LargeBitMask = BitMaskAdaptor; + +TEST(LlvmLibcHashTableBitMaskTest, SingleBitStrideLowestSetBit) { + uint16_t data = 0xffff; + for (size_t i = 0; i < 16; ++i) { + if (ShortBitMask{data}.any_bit_set()) { + ASSERT_EQ(ShortBitMask{data}.lowest_set_bit_nonzero(), i); + data <<= 1; + } + } +} + +TEST(LlvmLibcHashTableBitMaskTest, MultiBitStrideLowestSetBit) { + uint64_t data = 0xffff'ffff'ffff'ffff; + for (size_t i = 0; i < 8; ++i) { + for (size_t j = 0; j < 8; ++j) { + if (LargeBitMask{data}.any_bit_set()) { + ASSERT_EQ(LargeBitMask{data}.lowest_set_bit_nonzero(), i); + data <<= 1; + } + } + } +} + +TEST(LlvmLibcHashTableBitMaskTest, SingleBitStrideIteration) { + using Iter = IteratableBitMaskAdaptor; + uint16_t data = 0xffff; + for (size_t i = 0; i < 16; ++i) { + Iter iter = {data}; + size_t j = i; + for (auto x : iter) { + ASSERT_EQ(x, j); + j++; + } + ASSERT_EQ(j, size_t{16}); + data <<= 1; + } +} + +TEST(LlvmLibcHashTableBitMaskTest, MultiBitStrideIteration) { + using Iter = IteratableBitMaskAdaptor; + uint64_t data = Iter::MASK; + for (size_t i = 0; i < 8; ++i) { + Iter iter = {data}; + size_t j = i; + for (auto x : iter) { + ASSERT_EQ(x, j); + j++; + } + ASSERT_EQ(j, size_t{8}); + data <<= Iter::STRIDE; + } +} +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/HashTable/group_test.cpp b/libc/test/src/__support/HashTable/group_test.cpp new file mode 100644 index 00000000000000..907908335863a8 --- /dev/null +++ b/libc/test/src/__support/HashTable/group_test.cpp @@ -0,0 +1,90 @@ +//===-- Unittests for control group ---------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/HashTable/bitmask.h" + +#include "src/stdlib/rand.h" +#include "test/UnitTest/Test.h" +#include + +namespace LIBC_NAMESPACE { +namespace internal { + +struct ByteArray { + alignas(Group) uint8_t data[sizeof(Group) + 1]{}; +}; + +TEST(LlvmLibcHashTableBitMaskTest, Match) { + // Any pair of targets have bit differences not only at the lowest bit. + // No False positive. + uint8_t targets[4] = {0x00, 0x11, 0xFF, 0x0F}; + size_t count[4] = {0, 0, 0, 0}; + size_t appearance[4][sizeof(Group)]; + ByteArray array{}; + + union { + uintptr_t random; + int data[sizeof(uintptr_t) / sizeof(int)]; + }; + + for (int &i : data) + i = rand(); + + for (size_t i = 0; i < sizeof(Group); ++i) { + size_t choice = random % 4; + random /= 4; + array.data[i] = targets[choice]; + appearance[choice][count[choice]++] = i; + } + + for (size_t t = 0; t < sizeof(targets); ++t) { + auto bitmask = Group::load(array.data).match_byte(targets[t]); + for (size_t i = 0; i < count[t]; ++i) { + size_t iterated = 0; + for (size_t position : bitmask) { + ASSERT_EQ(appearance[t][iterated], position); + iterated++; + } + ASSERT_EQ(count[t], iterated); + } + } +} + +TEST(LlvmLibcHashTableBitMaskTest, MaskAvailable) { + uint8_t values[3] = {0x00, 0x0F, 0x80}; + + for (size_t i = 0; i < sizeof(Group); ++i) { + ByteArray array{}; + + union { + uintptr_t random; + int data[sizeof(uintptr_t) / sizeof(int)]; + }; + + for (int &j : data) + j = rand(); + + ASSERT_FALSE(Group::load(array.data).mask_available().any_bit_set()); + + array.data[i] = 0x80; + for (size_t j = 0; j < sizeof(Group); ++j) { + if (i == j) + continue; + size_t sample_space = 2 + (j > i); + size_t choice = random % sample_space; + random /= sizeof(values); + array.data[j] = values[choice]; + } + + auto mask = Group::load(array.data).mask_available(); + ASSERT_TRUE(mask.any_bit_set()); + ASSERT_EQ(mask.lowest_set_bit_nonzero(), i); + } +} +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/HashTable/table_test.cpp b/libc/test/src/__support/HashTable/table_test.cpp new file mode 100644 index 00000000000000..f0aa82f2d5c768 --- /dev/null +++ b/libc/test/src/__support/HashTable/table_test.cpp @@ -0,0 +1,77 @@ +//===-- Unittests for table -----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/HashTable/randomness.h" +#include "src/__support/HashTable/table.h" +#include "test/UnitTest/Test.h" + +namespace LIBC_NAMESPACE { +namespace internal { +TEST(LlvmLibcTableTest, AllocationAndDeallocation) { + size_t caps[] = {0, 1, 2, 3, 4, 7, 11, 37, 1024, 5261, 19999}; + const char *keys[] = {"", "a", "ab", "abc", + "abcd", "abcde", "abcdef", "abcdefg", + "abcdefgh", "abcdefghi", "abcdefghij"}; + for (size_t i : caps) { + HashTable *table = HashTable::allocate(i, 1); + ASSERT_NE(table, static_cast(nullptr)); + for (const char *key : keys) { + ASSERT_EQ(table->find(key), static_cast(nullptr)); + } + HashTable::deallocate(table); + } + ASSERT_EQ(HashTable::allocate(-1, 0), static_cast(nullptr)); + HashTable::deallocate(nullptr); +} + +TEST(LlvmLibcTableTest, Insertion) { + union key { + uint64_t value; + char bytes[8]; + } keys[256]; + for (size_t k = 0; k < 256; ++k) { + keys[k].value = LIBC_NAMESPACE::Endian::to_little_endian(k); + } + constexpr size_t CAP = next_power_of_two((sizeof(Group) + 1) * 8 / 7) / 8 * 7; + static_assert(CAP + 1 < 256, "CAP is too large for this test."); + HashTable *table = + HashTable::allocate(sizeof(Group) + 1, randomness::next_random_seed()); + ASSERT_NE(table, static_cast(nullptr)); + + // insert to full capacity. + for (size_t i = 0; i < CAP; ++i) { + ASSERT_NE(table->insert({keys[i].bytes, keys[i].bytes}), + static_cast(nullptr)); + } + + // one more insert should fail. + ASSERT_EQ(table->insert({keys[CAP + 1].bytes, keys[CAP + 1].bytes}), + static_cast(nullptr)); + + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(strcmp(table->find(keys[i].bytes)->key, keys[i].bytes), 0); + } + for (size_t i = CAP; i < 256; ++i) { + ASSERT_EQ(table->find(keys[i].bytes), static_cast(nullptr)); + } + + // do not replace old value + for (size_t i = 0; i < CAP; ++i) { + ASSERT_NE(table->insert({keys[i].bytes, reinterpret_cast(i)}), + static_cast(nullptr)); + } + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(table->find(keys[i].bytes)->data, + reinterpret_cast(keys[i].bytes)); + } + + HashTable::deallocate(table); +} + +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/bit_test.cpp b/libc/test/src/__support/bit_test.cpp index 09d9c2f0a4ed88..e585735394e2c7 100644 --- a/libc/test/src/__support/bit_test.cpp +++ b/libc/test/src/__support/bit_test.cpp @@ -15,4 +15,53 @@ TEST(LlvmLibcBlockBitTest, TODO) { // TODO Implement me. } +TEST(LlvmLibcBlockBitTest, OffsetTo) { + ASSERT_EQ(offset_to(0, 512), 0); + ASSERT_EQ(offset_to(1, 512), 511); + ASSERT_EQ(offset_to(2, 512), 510); + ASSERT_EQ(offset_to(13, 1), 0); + ASSERT_EQ(offset_to(13, 4), 3); + for (unsigned int i = 0; i < 31; ++i) { + ASSERT_EQ((offset_to(i, 1u << i) + i) % (1u << i), 0u); + } +} + +TEST(LlvmLibcBlockBitTest, RotateLeft) { + { + unsigned current = 1; + for (unsigned i = 0; i < 8 * sizeof(unsigned); ++i) { + ASSERT_EQ(1u << i, current); + ASSERT_EQ(current, rotate_left(1u, i)); + current = rotate_left(current, 1u); + } + ASSERT_EQ(current, 1u); + } + { + int current = 1; + for (int i = 0; i < 8 * static_cast(sizeof(int)); ++i) { + ASSERT_EQ(1 << i, current); + ASSERT_EQ(current, rotate_left(1, i)); + current = rotate_left(current, 1); + } + ASSERT_EQ(current, 1); + } +} + +TEST(LlvmLibcBlockBitTest, NextPowerOfTwo) { + ASSERT_EQ(1u, next_power_of_two(0u)); + for (unsigned int i = 0; i < 31; ++i) { + ASSERT_EQ(1u << (i + 1), next_power_of_two((1u << i) + 1)); + ASSERT_EQ(1u << i, next_power_of_two(1u << i)); + } +} + +TEST(LlvmLibcBlockBitTest, IsPowerOfTwo) { + ASSERT_FALSE(is_power_of_two(0u)); + ASSERT_TRUE(is_power_of_two(1u)); + for (unsigned int i = 1; i < 31; ++i) { + ASSERT_TRUE(is_power_of_two(1u << i)); + ASSERT_FALSE(is_power_of_two((1u << i) + 1)); + } +} + } // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/hash_test.cpp b/libc/test/src/__support/hash_test.cpp new file mode 100644 index 00000000000000..612efd544c66f9 --- /dev/null +++ b/libc/test/src/__support/hash_test.cpp @@ -0,0 +1,140 @@ +//===-- Unittests for hash ------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/new.h" +#include "src/__support/hash.h" +#include "src/stdlib/rand.h" +#include "src/stdlib/srand.h" +#include "src/string/memset.h" +#include "test/UnitTest/Test.h" + +template struct AlignedMemory { + T *data; + size_t offset; + std::align_val_t alignment; + AlignedMemory(size_t size, size_t alignment, size_t offset) + : offset(offset), alignment{alignment} { + size_t sz = size * sizeof(T); + size_t aligned = sz + ((-sz) & (alignment - 1)) + alignment; + LIBC_NAMESPACE::AllocChecker ac; + data = static_cast(operator new(aligned, this->alignment, ac)); + data += offset % alignment; + } + ~AlignedMemory() { operator delete(data - offset, alignment); } +}; + +size_t sizes[] = {0, 1, 23, 59, 1024, 5261}; +char values[] = {0, 1, 23, 59, 102, -1}; + +// Hash value should not change with different alignments. +TEST(LlvmLibcHashTest, SanityCheck) { + for (size_t sz : sizes) { + for (uint8_t val : values) { + uint64_t hash; + { + AlignedMemory mem(sz, 64, 0); + LIBC_NAMESPACE::memset(mem.data, val, sz); + LIBC_NAMESPACE::internal::HashState state{0x1234567890abcdef}; + state.update(mem.data, sz); + hash = state.finish(); + } + for (size_t offset = 1; offset < 64; ++offset) { + AlignedMemory mem(sz, 64, offset); + LIBC_NAMESPACE::memset(mem.data, val, sz); + LIBC_NAMESPACE::internal::HashState state{0x1234567890abcdef}; + state.update(mem.data, sz); + ASSERT_EQ(hash, state.finish()); + } + } + } +} + +static inline size_t popcnt(uint64_t x) { + size_t count = 0; + while (x) { + count += x & 1; + x >>= 1; + } + return count; +} + +// Mutate a single bit in a rather large input. The hash should change +// significantly. At least one fifth of the bits should not match. +TEST(LlvmLibcHashTest, Avalanche) { + for (size_t sz : sizes) { + for (uint8_t val : values) { + uint64_t hash; + AlignedMemory mem(sz, 64, 0); + LIBC_NAMESPACE::memset(mem.data, val, sz); + { + LIBC_NAMESPACE::internal::HashState state{0xabcdef1234567890}; + state.update(mem.data, sz); + hash = state.finish(); + } + for (size_t i = 0; i < sz; ++i) { + for (size_t j = 0; j < 8; ++j) { + uint8_t mask = 1 << j; + mem.data[i] ^= mask; + { + LIBC_NAMESPACE::internal::HashState state{0xabcdef1234567890}; + state.update(mem.data, sz); + uint64_t new_hash = state.finish(); + ASSERT_GE(popcnt(hash ^ new_hash), size_t{13}); + } + mem.data[i] ^= mask; + } + } + } + } +} + +// Hash a random sequence of input. The LSB should be uniform enough such that +// values spread across the entire range. +TEST(LlvmLibcHashTest, UniformLSB) { + LIBC_NAMESPACE::srand(0xffffffff); + for (size_t sz : sizes) { + AlignedMemory counters(sz, sizeof(size_t), 0); + LIBC_NAMESPACE::memset(counters.data, 0, sz * sizeof(size_t)); + for (size_t i = 0; i < 200 * sz; ++i) { + int randomness[8] = {LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand(), + LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand(), + LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand(), + LIBC_NAMESPACE::rand(), LIBC_NAMESPACE::rand()}; + { + LIBC_NAMESPACE::internal::HashState state{0x1a2b3c4d5e6f7a8b}; + state.update(randomness, sizeof(randomness)); + uint64_t hash = state.finish(); + counters.data[hash % sz]++; + } + } + for (size_t i = 0; i < sz; ++i) { + ASSERT_GE(counters.data[i], size_t{140}); + ASSERT_LE(counters.data[i], size_t{260}); + } + } +} + +// Hash a low entropy sequence. The MSB should be uniform enough such that +// there is no significant bias even if the value range is small. +// Top 7 bits is examined because it will be used as a secondary key in +// the hash table. +TEST(LlvmLibcHashTest, UniformMSB) { + size_t sz = 1 << 7; + AlignedMemory counters(sz, sizeof(size_t), 0); + LIBC_NAMESPACE::memset(counters.data, 0, sz * sizeof(size_t)); + for (size_t i = 0; i < 200 * sz; ++i) { + LIBC_NAMESPACE::internal::HashState state{0xa1b2c3d4e5f6a7b8}; + state.update(&i, sizeof(i)); + uint64_t hash = state.finish(); + counters.data[hash >> 57]++; + } + for (size_t i = 0; i < sz; ++i) { + ASSERT_GE(counters.data[i], size_t{140}); + ASSERT_LE(counters.data[i], size_t{260}); + } +} diff --git a/libc/test/src/__support/memory_size_test.cpp b/libc/test/src/__support/memory_size_test.cpp new file mode 100644 index 00000000000000..98b6a613e62fb4 --- /dev/null +++ b/libc/test/src/__support/memory_size_test.cpp @@ -0,0 +1,85 @@ +//===-- Unittests for MemorySize ------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/memory_size.h" +#include "test/UnitTest/Test.h" + +namespace LIBC_NAMESPACE { +namespace internal { +static inline constexpr size_t SAFE_MEM_SIZE_TEST_LIMIT = + static_cast(cpp::numeric_limits>::max()); + +TEST(LlvmLibcMemSizeTest, Constuction) { + ASSERT_FALSE(SafeMemSize{static_cast(-1)}.valid()); + ASSERT_FALSE(SafeMemSize{static_cast(-2)}.valid()); + ASSERT_FALSE(SafeMemSize{static_cast(-1024 + 33)}.valid()); + ASSERT_FALSE(SafeMemSize{static_cast(-1024 + 66)}.valid()); + ASSERT_FALSE(SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT + 1}.valid()); + ASSERT_FALSE(SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT + 13}.valid()); + + ASSERT_TRUE(SafeMemSize{static_cast(1)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(1024 + 13)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(2048 - 13)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(4096 + 1)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(8192 - 1)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(16384 + 15)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(32768 * 3)}.valid()); + ASSERT_TRUE(SafeMemSize{static_cast(65536 * 13)}.valid()); + ASSERT_TRUE(SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT}.valid()); + ASSERT_TRUE(SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT - 1}.valid()); + ASSERT_TRUE(SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT - 13}.valid()); +} + +TEST(LlvmLibcMemSizeTest, Addition) { + auto max = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT}; + auto half = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT / 2}; + auto third = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT / 3}; + + ASSERT_TRUE(half.valid()); + ASSERT_TRUE(third.valid()); + ASSERT_TRUE((half + half).valid()); + ASSERT_TRUE((third + third + third).valid()); + ASSERT_TRUE((half + third).valid()); + + ASSERT_FALSE((max + SafeMemSize{static_cast(1)}).valid()); + ASSERT_FALSE((third + third + third + third).valid()); + ASSERT_FALSE((half + half + half).valid()); +} + +TEST(LlvmLibcMemSizeTest, Multiplication) { + auto max = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT}; + auto half = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT / 2}; + auto third = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT / 3}; + + ASSERT_TRUE((max * SafeMemSize{static_cast(1)}).valid()); + ASSERT_TRUE((max * SafeMemSize{static_cast(0)}).valid()); + + ASSERT_FALSE((max * SafeMemSize{static_cast(2)}).valid()); + ASSERT_FALSE((half * half).valid()); + ASSERT_FALSE((half * SafeMemSize{static_cast(3)}).valid()); + ASSERT_FALSE((third * SafeMemSize{static_cast(4)}).valid()); +} + +TEST(LlvmLibcMemSizeTest, AlignUp) { + size_t sizes[] = { + 0, 1, 8, 13, 60, 97, 128, 1024, 5124, 5120, + }; + for (size_t i = 2; i <= 16; ++i) { + size_t alignment = 1 << i; + for (size_t size : sizes) { + auto safe_size = SafeMemSize{size}; + auto safe_aligned_size = safe_size.align_up(alignment); + ASSERT_TRUE(safe_aligned_size.valid()); + ASSERT_EQ(static_cast(safe_aligned_size) % alignment, size_t{0}); + } + } + auto max = SafeMemSize{SAFE_MEM_SIZE_TEST_LIMIT}; + ASSERT_FALSE(max.align_up(8).valid()); +} +} // namespace internal +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/search/CMakeLists.txt b/libc/test/src/search/CMakeLists.txt new file mode 100644 index 00000000000000..d624f144309490 --- /dev/null +++ b/libc/test/src/search/CMakeLists.txt @@ -0,0 +1,16 @@ +add_custom_target(libc_search_unittests) +add_libc_unittest( + hsearch_test + SUITE + libc_search_unittests + SRCS + hsearch_test.cpp + DEPENDS + libc.src.search.hsearch_r + libc.src.search.hcreate_r + libc.src.search.hdestroy_r + libc.src.search.hsearch + libc.src.search.hcreate + libc.src.search.hdestroy + libc.src.errno.errno +) diff --git a/libc/test/src/search/hsearch_test.cpp b/libc/test/src/search/hsearch_test.cpp new file mode 100644 index 00000000000000..bc9dea748758ac --- /dev/null +++ b/libc/test/src/search/hsearch_test.cpp @@ -0,0 +1,124 @@ +//===-- Unittests for hsearch ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/HashTable/table.h" +#include "src/__support/bit.h" +#include "src/search/hcreate.h" +#include "src/search/hcreate_r.h" +#include "src/search/hdestroy.h" +#include "src/search/hdestroy_r.h" +#include "src/search/hsearch.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/Test.h" +#include + +TEST(LlvmLibcHsearchTest, CreateTooLarge) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + struct hsearch_data hdata; + ASSERT_THAT(LIBC_NAMESPACE::hcreate(-1), Fails(ENOMEM, 0)); + ASSERT_THAT(LIBC_NAMESPACE::hcreate_r(-1, &hdata), Fails(ENOMEM, 0)); +} + +TEST(LlvmLibcHSearchTest, CreateInvalid) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(LIBC_NAMESPACE::hcreate_r(16, nullptr), Fails(EINVAL, 0)); +} + +TEST(LlvmLibcHSearchTest, CreateValid) { + struct hsearch_data hdata; + ASSERT_GT(LIBC_NAMESPACE::hcreate_r(1, &hdata), 0); + LIBC_NAMESPACE::hdestroy_r(&hdata); + + ASSERT_GT(LIBC_NAMESPACE::hcreate(1), 0); + LIBC_NAMESPACE::hdestroy(); +} + +char search_data[] = "1234567890abcdefghijklmnopqrstuvwxyz" + "1234567890abcdefghijklmnopqrstuvwxyz" + "1234567890abcdefghijklmnopqrstuvwxyz" + "1234567890abcdefghijklmnopqrstuvwxyz" + "1234567890abcdefghijklmnopqrstuvwxyz"; +char search_data2[] = + "@@@@@@@@@@@@@@!!!!!!!!!!!!!!!!!###########$$$$$$$$$$^^^^^^&&&&&&&&"; + +constexpr size_t GROUP_SIZE = sizeof(LIBC_NAMESPACE::internal::Group); +constexpr size_t CAP = + LIBC_NAMESPACE::next_power_of_two((GROUP_SIZE + 1) * 8 / 7) / 8 * 7; +static_assert(CAP < sizeof(search_data), "CAP too large"); + +TEST(LlvmLibcHSearchTest, InsertTooMany) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_GT(LIBC_NAMESPACE::hcreate(GROUP_SIZE + 1), 0); + + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch({&search_data[i], nullptr}, ENTER)->key, + &search_data[i]); + } + ASSERT_THAT(static_cast( + LIBC_NAMESPACE::hsearch({search_data2, nullptr}, ENTER)), + Fails(ENOMEM, static_cast(nullptr))); + LIBC_NAMESPACE::hdestroy(); +} + +TEST(LlvmLibcHSearchTest, NotFound) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_GT(LIBC_NAMESPACE::hcreate(GROUP_SIZE + 1), 0); + ASSERT_THAT(static_cast( + LIBC_NAMESPACE::hsearch({search_data2, nullptr}, FIND)), + Fails(ESRCH, static_cast(nullptr))); + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch({&search_data[i], nullptr}, ENTER)->key, + &search_data[i]); + } + ASSERT_THAT(static_cast( + LIBC_NAMESPACE::hsearch({search_data2, nullptr}, FIND)), + Fails(ESRCH, static_cast(nullptr))); + LIBC_NAMESPACE::hdestroy(); +} + +TEST(LlvmLibcHSearchTest, Found) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_GT(LIBC_NAMESPACE::hcreate(GROUP_SIZE + 1), 0); + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch( + {&search_data[i], reinterpret_cast(i)}, ENTER) + ->key, + &search_data[i]); + } + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch({&search_data[i], nullptr}, FIND)->data, + reinterpret_cast(i)); + } + LIBC_NAMESPACE::hdestroy(); +} + +TEST(LlvmLibcHSearchTest, OnlyInsertWhenNotFound) { + using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails; + ASSERT_GT(LIBC_NAMESPACE::hcreate(GROUP_SIZE + 1), 0); + for (size_t i = 0; i < CAP / 7 * 5; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch( + {&search_data[i], reinterpret_cast(i)}, ENTER) + ->key, + &search_data[i]); + } + for (size_t i = 0; i < CAP; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch( + {&search_data[i], reinterpret_cast(1000 + i)}, ENTER) + ->key, + &search_data[i]); + } + for (size_t i = 0; i < CAP / 7 * 5; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch({&search_data[i], nullptr}, FIND)->data, + reinterpret_cast(i)); + } + for (size_t i = CAP / 7 * 5; i < CAP; ++i) { + ASSERT_EQ(LIBC_NAMESPACE::hsearch({&search_data[i], nullptr}, FIND)->data, + reinterpret_cast(1000 + i)); + } + LIBC_NAMESPACE::hdestroy(); +} diff --git a/lld/test/wasm/lto/Inputs/comdat_ordering1.ll b/lld/test/wasm/lto/Inputs/comdat_ordering1.ll new file mode 100644 index 00000000000000..b866c6efeba10e --- /dev/null +++ b/lld/test/wasm/lto/Inputs/comdat_ordering1.ll @@ -0,0 +1,42 @@ +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; Generated from this C++ code and simplified manually: +; +; int foo(); +; inline int unused = foo(); +; +; int main() { +; return foo(); +; } + +$unused = comdat any + +@unused = linkonce_odr global i32 0, comdat, align 4 +@_ZGV6unused = linkonce_odr global i32 0, comdat($unused), align 4 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @__cxx_global_var_init, ptr @unused }] + +define internal void @__cxx_global_var_init() comdat($unused) { +entry: + %0 = load i8, ptr @_ZGV6unused, align 4 + %1 = and i8 %0, 1 + %guard.uninitialized = icmp eq i8 %1, 0 + br i1 %guard.uninitialized, label %init.check, label %init.end + +init.check: ; preds = %entry + store i8 1, ptr @_ZGV6unused, align 4 + %call = call i32 @foo() + store i32 %call, ptr @unused, align 4 + br label %init.end + +init.end: ; preds = %init.check, %entry + ret void +} + +declare i32 @foo() + +define i32 @main() { +entry: + %call = call i32 @foo() + ret i32 %call +} diff --git a/lld/test/wasm/lto/Inputs/comdat_ordering2.ll b/lld/test/wasm/lto/Inputs/comdat_ordering2.ll new file mode 100644 index 00000000000000..58ab5122bad881 --- /dev/null +++ b/lld/test/wasm/lto/Inputs/comdat_ordering2.ll @@ -0,0 +1,39 @@ +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; Generated from this C++ code and simplified manually: +; +; int foo(); +; inline int unused = foo(); +; +; int foo() { +; return 42; +; } + +$unused = comdat any + +@unused = linkonce_odr global i32 0, comdat, align 4 +@_ZGV6unused = linkonce_odr global i32 0, comdat($unused), align 4 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @__cxx_global_var_init, ptr @unused }] + +define internal void @__cxx_global_var_init() comdat($unused) { +entry: + %0 = load i8, ptr @_ZGV6unused, align 4 + %1 = and i8 %0, 1 + %guard.uninitialized = icmp eq i8 %1, 0 + br i1 %guard.uninitialized, label %init.check, label %init.end + +init.check: ; preds = %entry + store i8 1, ptr @_ZGV6unused, align 4 + %call = call i32 @foo() + store i32 %call, ptr @unused, align 4 + br label %init.end + +init.end: ; preds = %init.check, %entry + ret void +} + +define i32 @foo() { +entry: + ret i32 42 +} diff --git a/lld/test/wasm/lto/comdat_ordering.test b/lld/test/wasm/lto/comdat_ordering.test new file mode 100644 index 00000000000000..12a0efc39aff34 --- /dev/null +++ b/lld/test/wasm/lto/comdat_ordering.test @@ -0,0 +1,19 @@ +; Check if we handle a variable (here __cxx_global_var_init) in different LTO +; bitcode modules sharing a comdat. + +; RUN: llvm-as %S/Inputs/comdat_ordering1.ll -o %t1.o +; RUN: llvm-as %S/Inputs/comdat_ordering2.ll -o %t2.o +; RUN: llvm-ar rcs %t1.a %t1.o +; RUN: llvm-ar rcs %t2.a %t2.o +; RUN: wasm-ld %t1.a %t2.a -o %t.wasm --no-entry --export=main --export=__wasm_call_ctors +; RUN: obj2yaml %t.wasm | FileCheck %s + +; CHECK: - Type: CUSTOM +; CHECK-NEXT: Name: name +; CHECK-NEXT: FunctionNames: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: Name: __wasm_call_ctors +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: Name: __cxx_global_var_init + +; CHECK-NOT: Name: __cxx_global_var_init.2 diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index a00e336118d8c8..76370525c37199 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -50,8 +50,10 @@ void SymbolTable::addFile(InputFile *file, StringRef symName) { // LLVM bitcode file if (auto *f = dyn_cast(file)) { - f->parse(symName); + // This order, first adding to `bitcodeFiles` and then parsing is necessary. + // See https://github.com/llvm/llvm-project/pull/73095 bitcodeFiles.push_back(f); + f->parse(symName); return; } diff --git a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py index 2e078ce9446b01..e63a26f543cc42 100644 --- a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py +++ b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py @@ -118,7 +118,7 @@ def test_class_with_only_const_static(self): def check_global_var(self, name: str, expect_type, expect_val): var_list = self.target().FindGlobalVariables(name, lldb.UINT32_MAX) - self.assertEqual(len(var_list), 1) + self.assertGreaterEqual(len(var_list), 1) varobj = var_list[0] self.assertEqual(varobj.type.name, expect_type) self.assertEqual(varobj.value, expect_val) diff --git a/llvm/include/llvm/CodeGen/CallBrPrepare.h b/llvm/include/llvm/CodeGen/CallBrPrepare.h new file mode 100644 index 00000000000000..989343b02d024a --- /dev/null +++ b/llvm/include/llvm/CodeGen/CallBrPrepare.h @@ -0,0 +1,23 @@ +//===-- CallBrPrepare - Prepare callbr for code generation ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_CALLBRPREPARE_H +#define LLVM_CODEGEN_CALLBRPREPARE_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class CallBrPreparePass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &Fn, FunctionAnalysisManager &FAM); +}; + +} // namespace llvm + +#endif // LLVM_CODEGEN_CALLBRPREPARE_H diff --git a/llvm/include/llvm/Config/llvm-config.h.cmake b/llvm/include/llvm/Config/llvm-config.h.cmake index 7f1fdb42d2f968..0e64a698badee3 100644 --- a/llvm/include/llvm/Config/llvm-config.h.cmake +++ b/llvm/include/llvm/Config/llvm-config.h.cmake @@ -16,7 +16,7 @@ /* Indicate that this is LLVM compiled from the amd-gfx branch. */ #define LLVM_HAVE_BRANCH_AMD_GFX -#define LLVM_MAIN_REVISION 482039 +#define LLVM_MAIN_REVISION 482050 /* Define if LLVM_ENABLE_DUMP is enabled */ #cmakedefine LLVM_ENABLE_DUMP diff --git a/llvm/lib/CodeGen/CallBrPrepare.cpp b/llvm/lib/CodeGen/CallBrPrepare.cpp index db243a0bfebe1a..fddc4d74b2da91 100644 --- a/llvm/lib/CodeGen/CallBrPrepare.cpp +++ b/llvm/lib/CodeGen/CallBrPrepare.cpp @@ -31,6 +31,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/CodeGen/CallBrPrepare.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -53,15 +54,16 @@ using namespace llvm; #define DEBUG_TYPE "callbrprepare" +static bool SplitCriticalEdges(ArrayRef CBRs, DominatorTree &DT); +static bool InsertIntrinsicCalls(ArrayRef CBRs, + DominatorTree &DT); +static void UpdateSSA(DominatorTree &DT, CallBrInst *CBR, CallInst *Intrinsic, + SSAUpdater &SSAUpdate); +static SmallVector FindCallBrs(Function &Fn); + namespace { class CallBrPrepare : public FunctionPass { - bool SplitCriticalEdges(ArrayRef CBRs, DominatorTree &DT); - bool InsertIntrinsicCalls(ArrayRef CBRs, - DominatorTree &DT) const; - void UpdateSSA(DominatorTree &DT, CallBrInst *CBR, CallInst *Intrinsic, - SSAUpdater &SSAUpdate) const; - public: CallBrPrepare() : FunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override; @@ -71,6 +73,26 @@ class CallBrPrepare : public FunctionPass { } // end anonymous namespace +PreservedAnalyses CallBrPreparePass::run(Function &Fn, + FunctionAnalysisManager &FAM) { + bool Changed = false; + SmallVector CBRs = FindCallBrs(Fn); + + if (CBRs.empty()) + return PreservedAnalyses::all(); + + auto &DT = FAM.getResult(Fn); + + Changed |= SplitCriticalEdges(CBRs, DT); + Changed |= InsertIntrinsicCalls(CBRs, DT); + + if (!Changed) + return PreservedAnalyses::all(); + PreservedAnalyses PA; + PA.preserve(); + return PA; +} + char CallBrPrepare::ID = 0; INITIALIZE_PASS_BEGIN(CallBrPrepare, DEBUG_TYPE, "Prepare callbr", false, false) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) @@ -82,7 +104,7 @@ void CallBrPrepare::getAnalysisUsage(AnalysisUsage &AU) const { AU.addPreserved(); } -static SmallVector FindCallBrs(Function &Fn) { +SmallVector FindCallBrs(Function &Fn) { SmallVector CBRs; for (BasicBlock &BB : Fn) if (auto *CBR = dyn_cast(BB.getTerminator())) @@ -91,8 +113,7 @@ static SmallVector FindCallBrs(Function &Fn) { return CBRs; } -bool CallBrPrepare::SplitCriticalEdges(ArrayRef CBRs, - DominatorTree &DT) { +bool SplitCriticalEdges(ArrayRef CBRs, DominatorTree &DT) { bool Changed = false; CriticalEdgeSplittingOptions Options(&DT); Options.setMergeIdenticalEdges(); @@ -114,8 +135,7 @@ bool CallBrPrepare::SplitCriticalEdges(ArrayRef CBRs, return Changed; } -bool CallBrPrepare::InsertIntrinsicCalls(ArrayRef CBRs, - DominatorTree &DT) const { +bool InsertIntrinsicCalls(ArrayRef CBRs, DominatorTree &DT) { bool Changed = false; SmallPtrSet Visited; IRBuilder<> Builder(CBRs[0]->getContext()); @@ -160,9 +180,8 @@ static void PrintDebugDomInfo(const DominatorTree &DT, const Use &U, } #endif -void CallBrPrepare::UpdateSSA(DominatorTree &DT, CallBrInst *CBR, - CallInst *Intrinsic, - SSAUpdater &SSAUpdate) const { +void UpdateSSA(DominatorTree &DT, CallBrInst *CBR, CallInst *Intrinsic, + SSAUpdater &SSAUpdate) { SmallPtrSet Visited; BasicBlock *DefaultDest = CBR->getDefaultDest(); diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index e06370c5463fa0..aeb9726a186b51 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -72,6 +72,7 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TypeBasedAliasAnalysis.h" #include "llvm/Analysis/UniformityAnalysis.h" +#include "llvm/CodeGen/CallBrPrepare.h" #include "llvm/CodeGen/DwarfEHPrepare.h" #include "llvm/CodeGen/ExpandLargeDivRem.h" #include "llvm/CodeGen/ExpandLargeFpConvert.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 3a9a37a231f8d2..f64aed31703427 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -448,6 +448,7 @@ FUNCTION_PASS("declare-to-assign", llvm::AssignmentTrackingPass()) FUNCTION_PASS("expand-large-div-rem", ExpandLargeDivRemPass(TM)); FUNCTION_PASS("expand-large-fp-convert", ExpandLargeFpConvertPass(TM)); FUNCTION_PASS("dwarfehprepare", DwarfEHPreparePass(TM)); +FUNCTION_PASS("callbrprepare", CallBrPreparePass()); #undef FUNCTION_PASS #ifndef FUNCTION_PASS_WITH_PARAMS diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 7cff963d766c49..a4185cdd92bc2f 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2436,15 +2436,31 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) { if (match(GEP.getOperand(1), m_OneUse(m_Add(m_Value(Idx1), m_Value(Idx2))))) { // %idx = add i64 %idx1, %idx2 - // %gep = getelementptr i32, i32* %ptr, i64 %idx + // %gep = getelementptr i32, ptr %ptr, i64 %idx // as: - // %newptr = getelementptr i32, i32* %ptr, i64 %idx1 - // %newgep = getelementptr i32, i32* %newptr, i64 %idx2 + // %newptr = getelementptr i32, ptr %ptr, i64 %idx1 + // %newgep = getelementptr i32, ptr %newptr, i64 %idx2 auto *NewPtr = Builder.CreateGEP(GEP.getResultElementType(), GEP.getPointerOperand(), Idx1); return GetElementPtrInst::Create(GEP.getResultElementType(), NewPtr, Idx2); } + ConstantInt *C; + if (match(GEP.getOperand(1), m_OneUse(m_SExt(m_OneUse(m_NSWAdd( + m_Value(Idx1), m_ConstantInt(C))))))) { + // %add = add nsw i32 %idx1, idx2 + // %sidx = sext i32 %add to i64 + // %gep = getelementptr i32, ptr %ptr, i64 %sidx + // as: + // %newptr = getelementptr i32, ptr %ptr, i32 %idx1 + // %newgep = getelementptr i32, ptr %newptr, i32 idx2 + auto *NewPtr = Builder.CreateGEP( + GEP.getResultElementType(), GEP.getPointerOperand(), + Builder.CreateSExt(Idx1, GEP.getOperand(1)->getType())); + return GetElementPtrInst::Create( + GEP.getResultElementType(), NewPtr, + Builder.CreateSExt(C, GEP.getOperand(1)->getType())); + } } if (!GEP.isInBounds()) { diff --git a/llvm/test/CodeGen/AArch64/callbr-prepare.ll b/llvm/test/CodeGen/AArch64/callbr-prepare.ll index 08b48d65c89a0c..701355793d1cae 100644 --- a/llvm/test/CodeGen/AArch64/callbr-prepare.ll +++ b/llvm/test/CodeGen/AArch64/callbr-prepare.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt %s -callbrprepare -S -o - | FileCheck %s +; RUN: opt %s -passes=callbrprepare -S -o - | FileCheck %s define i32 @test0() { ; CHECK-LABEL: @test0( diff --git a/llvm/test/Transforms/InstCombine/array.ll b/llvm/test/Transforms/InstCombine/array.ll new file mode 100644 index 00000000000000..8bab3104fd8cd0 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/array.ll @@ -0,0 +1,74 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define void @test(ptr %ptr, i32 %a, i32 %b) { +; CHECK-LABEL: define void @test( +; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[A]] to i64 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[PTR]], i64 [[TMP0]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[TMP1]], i64 10 +; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4 +; CHECK-NEXT: ret void +; +entry: + %add = add nsw i32 %a, 10 + %idx = sext i32 %add to i64 + %gep = getelementptr inbounds i32, ptr %ptr, i64 %idx + store i32 %b, ptr %gep + ret void +} + +define i32 @test_add_res_moreoneuse(ptr %ptr, i32 %a, i32 %b) { +; CHECK-LABEL: define i32 @test_add_res_moreoneuse( +; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 5 +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[ADD]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[IDX]] +; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4 +; CHECK-NEXT: ret i32 [[ADD]] +; +entry: + %add = add nsw i32 %a, 5 + %idx = sext i32 %add to i64 + %gep = getelementptr inbounds i32, ptr %ptr, i64 %idx + store i32 %b, ptr %gep + ret i32 %add +} + +define void @test_addop_nonsw_flag(ptr %ptr, i32 %a, i32 %b) { +; CHECK-LABEL: define void @test_addop_nonsw_flag( +; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], 10 +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[ADD]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[IDX]] +; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4 +; CHECK-NEXT: ret void +; +entry: + %add = add i32 %a, 10 + %idx = sext i32 %add to i64 + %gep = getelementptr inbounds i32, ptr %ptr, i64 %idx + store i32 %b, ptr %gep + ret void +} + +define void @test_add_op2_not_constant(ptr %ptr, i32 %a, i32 %b) { +; CHECK-LABEL: define void @test_add_op2_not_constant( +; CHECK-SAME: ptr [[PTR:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A]], [[B]] +; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[ADD]] to i64 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, ptr [[PTR]], i64 [[IDX]] +; CHECK-NEXT: store i32 [[B]], ptr [[GEP]], align 4 +; CHECK-NEXT: ret void +; +entry: + %add = add i32 %a, %b + %idx = sext i32 %add to i64 + %gep = getelementptr inbounds i32, ptr %ptr, i64 %idx + store i32 %b, ptr %gep + ret void +} diff --git a/mlir/include/mlir-c/Target/LLVMIR.h b/mlir/include/mlir-c/Target/LLVMIR.h new file mode 100644 index 00000000000000..effa74b905ce66 --- /dev/null +++ b/mlir/include/mlir-c/Target/LLVMIR.h @@ -0,0 +1,39 @@ +//===-- LLVMIR.h - C Interface for MLIR LLVMIR Target -------------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header declares the C interface to target LLVMIR with MLIR. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_C_TARGET_LLVMIR_H +#define MLIR_C_TARGET_LLVMIR_H + +#include "mlir-c/IR.h" +#include "mlir-c/Support.h" +#include "llvm-c/Support.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Translate operation that satisfies LLVM dialect module requirements into an +/// LLVM IR module living in the given context. This translates operations from +/// any dilalect that has a registered implementation of +/// LLVMTranslationDialectInterface. +/// +/// \returns the generated LLVM IR Module from the translated MLIR module, it is +/// owned by the caller. +MLIR_CAPI_EXPORTED LLVMModuleRef +mlirTranslateModuleToLLVMIR(MlirOperation module, LLVMContextRef context); + +#ifdef __cplusplus +} +#endif + +#endif // MLIR_C_TARGET_LLVMIR_H diff --git a/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorAttrDefs.td b/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorAttrDefs.td index 7fcd1bc2a384a5..12c1068ae1f546 100644 --- a/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorAttrDefs.td +++ b/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorAttrDefs.td @@ -382,9 +382,6 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding", /// the null encoding (since dense-tensors are always all-dense). bool isAllDense() const; - /// Returns true if it is a sparse tensor encoding in COO format. - bool isCOO() const; - /// Returns true if every level is ordered. Also returns true for /// the null encoding (since dense-tensors are always all-ordered). bool isAllOrdered() const; @@ -467,37 +464,22 @@ def SparseTensorStorageSpecifierKindAttr def IsSparseTensorPred : CPred<"!!::mlir::sparse_tensor::getSparseTensorEncoding($_self)">; -def IsCOOPred - : CPred<"!!::mlir::sparse_tensor::getSparseTensorEncoding($_self) && " - " ::mlir::sparse_tensor::getSparseTensorEncoding($_self).isCOO()">; - def IsSparseTensorSlicePred : CPred<"!!::mlir::sparse_tensor::getSparseTensorEncoding($_self) && " " ::mlir::sparse_tensor::getSparseTensorEncoding($_self).isSlice()">; -// The following four follow the same idiom as `TensorOf`, `AnyTensor`, -// `RankedTensorOf`, `AnyRankedTensor`. - class SparseTensorOf allowedTypes> : TensorOf; -class COOSparseTensorOf allowedTypes> - : TensorOf; - class SparseTensorSliceOf allowedTypes> : TensorOf; +class ScalarLikeOf allowedTypes> + : AnyTypeOf<[0DTensorOf, AnyTypeOf], "scalar like">; + def AnySparseTensor : SparseTensorOf<[AnyType]>; -def AnyCOOSparseTensor : COOSparseTensorOf<[AnyType]>; def AnySparseTensorSlice : SparseTensorSliceOf<[AnyType]>; - -class RankedSparseTensorOf allowedTypes> - : RankedTensorOf; - -def AnyRankedSparseTensor : RankedSparseTensorOf<[AnyType]>; - -class ScalarLikeOf allowedTypes> - : AnyTypeOf<[0DTensorOf, AnyTypeOf]>; +def AnyIndexingScalarLike : ScalarLikeOf<[AnySignlessIntegerOrIndex]>; //===----------------------------------------------------------------------===// // Sparse Tensor Sorting Algorithm Attribute. diff --git a/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorOps.td b/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorOps.td index c5cb0ac155d682..3127cf1b1bcf69 100644 --- a/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorOps.td +++ b/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorOps.td @@ -58,7 +58,7 @@ def SparseTensor_AssembleOp : SparseTensor_Op<"assemble", [Pure]>, Arguments<(ins TensorOf<[AnyType]>:$values, Variadic>:$levels)>, Results<(outs AnySparseTensor: $result)> { - let summary = "Returns a sparse tensor from the given values, levels"; + let summary = "Returns a sparse tensor assembled from the given values and levels"; let description = [{ Assembles the values and per-level coordinate or postion arrays into a sparse tensor. @@ -71,7 +71,7 @@ def SparseTensor_AssembleOp : SparseTensor_Op<"assemble", [Pure]>, each supplies the sparse tensor coordinates scheme in the sparse tensor for the corresponding level as specifed by `sparse_tensor::StorageLayout`. - This operation can be used to materialize a sparse tensor from external + This operation can be used to assemble a sparse tensor from external sources; e.g., when passing two numpy arrays from Python. Disclaimer: This is the user's responsibility to provide input that can be @@ -82,7 +82,6 @@ def SparseTensor_AssembleOp : SparseTensor_Op<"assemble", [Pure]>, dimOrdering/higherOrdering mappings. However, the current implementation does not yet support them. - Example: ```mlir @@ -109,16 +108,17 @@ def SparseTensor_DisassembleOp : SparseTensor_Op<"disassemble", [Pure, SameVaria Variadic>:$out_levels)>, Results<(outs TensorOf<[AnyType]>:$ret_values, Variadic>:$ret_levels, - ScalarLikeOf<[AnySignlessIntegerOrIndex]>:$val_len, - Variadic>:$lvl_lens)> { - let summary = "Returns the (values, coordinates) pair unpacked from the input tensor"; + AnyIndexingScalarLike:$val_len, + Variadic:$lvl_lens)> { + let summary = "Returns the (values, coordinates) pair disassembled from the input tensor"; let description = [{ - The disassemble operation is the inverse of `sparse_tensor::assemble`. It returns - the values and per-level position and coordinate array to the user - from the sparse tensor along with the actual length of the memory used in - each returned buffer. This operation can be used for returning an - unpacked MLIR sparse tensor to frontend; e.g., returning two numpy arrays to Python. + The disassemble operation is the inverse of `sparse_tensor::assemble`. + It returns the values and per-level position and coordinate array to the + user from the sparse tensor along with the actual length of the memory used + in each returned buffer. This operation can be used for returning an + disassembled MLIR sparse tensor to frontend; e.g., returning two numpy arrays + to Python. Disclaimer: This is the user's responsibility to allocate large enough buffers to hold the sparse tensor. The sparsifier simply copies each fields @@ -921,10 +921,9 @@ def SparseTensor_SortOp : SparseTensor_Op<"sort">, let summary = "Sorts the arrays in xs and ys lexicographically on the " "integral values found in the xs list"; let description = [{ - Sparse_tensor.sort sort the `xs` values along with some `ys` values - that are put in a single linear buffer `xy`. - The affine map attribute `perm_map` specifies the permutation to be applied on - the `xs` before comparison, the rank of the permutation map + Sorts the `xs` values along with some `ys` values that are put in a single linear + buffer `xy`. The affine map attribute `perm_map` specifies the permutation to be + applied on the `xs` before comparison, the rank of the permutation map also specifies the number of `xs` values in `xy`. The optional index attribute `ny` provides the number of `ys` values in `xy`. When `ny` is not explicitly specified, its value is 0. @@ -950,14 +949,14 @@ def SparseTensor_SortOp : SparseTensor_Op<"sort">, } def SparseTensor_ReorderCOOOp : SparseTensor_Op<"reorder_coo", [Pure]>, - Arguments<(ins AnyCOOSparseTensor: $input_coo, + Arguments<(ins AnySparseTensor: $input_coo, SparseTensorSortKindAttr:$algorithm)>, - Results<(outs AnyCOOSparseTensor: $result_coo)> { + Results<(outs AnySparseTensor: $result_coo)> { let summary = "Reorder the input COO such that it has the the same order as " "the output COO"; let description = [{ - sparse_tensor.reorder_coo reorder input COO to the same order as specified by - the output format. E.g., reorder an unordered COO into an ordered one. + Reorders the input COO to the same order as specified by the output format. + E.g., reorder an unordered COO into an ordered one. The input and result COO tensor must have the same element type, position type and coordinate type. At the moment, the operation also only supports ordering diff --git a/mlir/lib/CAPI/CMakeLists.txt b/mlir/lib/CAPI/CMakeLists.txt index 707e78ac3d1ea3..6c438508425b7c 100644 --- a/mlir/lib/CAPI/CMakeLists.txt +++ b/mlir/lib/CAPI/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(Interfaces) add_subdirectory(IR) add_subdirectory(RegisterEverything) add_subdirectory(Transforms) +add_subdirectory(Target) if(MLIR_ENABLE_EXECUTION_ENGINE) add_subdirectory(ExecutionEngine) @@ -36,4 +37,3 @@ if(MLIR_BUILD_MLIR_C_DYLIB) endif() endif() endif() - diff --git a/mlir/lib/CAPI/Target/CMakeLists.txt b/mlir/lib/CAPI/Target/CMakeLists.txt new file mode 100644 index 00000000000000..ce86fd3def964c --- /dev/null +++ b/mlir/lib/CAPI/Target/CMakeLists.txt @@ -0,0 +1,12 @@ +add_mlir_upstream_c_api_library(MLIRCAPITarget + LLVMIR.cpp + + LINK_COMPONENTS + Core + + LINK_LIBS PUBLIC + MLIRToLLVMIRTranslationRegistration + MLIRCAPIIR + MLIRLLVMToLLVMIRTranslation + MLIRSupport +) diff --git a/mlir/lib/CAPI/Target/LLVMIR.cpp b/mlir/lib/CAPI/Target/LLVMIR.cpp new file mode 100644 index 00000000000000..dc798372be7467 --- /dev/null +++ b/mlir/lib/CAPI/Target/LLVMIR.cpp @@ -0,0 +1,36 @@ +//===-- LLVMIR.h - C Interface for MLIR LLVMIR Target ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "mlir-c/Target/LLVMIR.h" +#include "llvm-c/Support.h" + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include + +#include "mlir/CAPI/IR.h" +#include "mlir/CAPI/Support.h" +#include "mlir/CAPI/Wrap.h" +#include "mlir/Target/LLVMIR/ModuleTranslation.h" + +using namespace mlir; + +LLVMModuleRef mlirTranslateModuleToLLVMIR(MlirOperation module, + LLVMContextRef context) { + Operation *moduleOp = unwrap(module); + + llvm::LLVMContext *ctx = llvm::unwrap(context); + + std::unique_ptr llvmModule = + mlir::translateModuleToLLVMIR(moduleOp, *ctx); + + LLVMModuleRef moduleRef = llvm::wrap(llvmModule.release()); + + return moduleRef; +} diff --git a/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp b/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp index 74d2fd5fd9f829..d4f8afdd62f238 100644 --- a/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp +++ b/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp @@ -316,10 +316,6 @@ bool SparseTensorEncodingAttr::isAllDense() const { return !getImpl() || llvm::all_of(getLvlTypes(), isDenseLT); } -bool SparseTensorEncodingAttr::isCOO() const { - return getImpl() && isCOOType(*this, 0, true); -} - bool SparseTensorEncodingAttr::isAllOrdered() const { return !getImpl() || llvm::all_of(getLvlTypes(), isOrderedLT); } @@ -1664,14 +1660,18 @@ LogicalResult ReorderCOOOp::verify() { SparseTensorType srcStt = getSparseTensorType(getInputCoo()); SparseTensorType dstStt = getSparseTensorType(getResultCoo()); + if (!isCOOType(srcStt.getEncoding(), 0, /*isUnique=*/true) || + !isCOOType(dstStt.getEncoding(), 0, /*isUnique=*/true)) + emitError("Unexpected non-COO sparse tensors"); + if (!srcStt.hasSameDimToLvl(dstStt)) emitError("Unmatched dim2lvl map between input and result COO"); if (srcStt.getPosType() != dstStt.getPosType() || srcStt.getCrdType() != dstStt.getCrdType() || - srcStt.getElementType() != dstStt.getElementType()) { + srcStt.getElementType() != dstStt.getElementType()) emitError("Unmatched storage format between input and result COO"); - } + return success(); } diff --git a/mlir/test/CAPI/CMakeLists.txt b/mlir/test/CAPI/CMakeLists.txt index 16a3d0ed9c62fb..1096a3b0806648 100644 --- a/mlir/test/CAPI/CMakeLists.txt +++ b/mlir/test/CAPI/CMakeLists.txt @@ -85,3 +85,12 @@ _add_capi_test_executable(mlir-capi-transform-test MLIRCAPIRegisterEverything MLIRCAPITransformDialect ) + +_add_capi_test_executable(mlir-capi-translation-test + translation.c + LINK_LIBS PRIVATE + MLIRCAPIIR + MLIRCAPILLVM + MLIRCAPIRegisterEverything + MLIRCAPITarget +) diff --git a/mlir/test/CAPI/translation.c b/mlir/test/CAPI/translation.c new file mode 100644 index 00000000000000..e58a01d8c171d0 --- /dev/null +++ b/mlir/test/CAPI/translation.c @@ -0,0 +1,67 @@ +//===- translation.c - Test MLIR Target translations ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// RUN: mlir-capi-translation-test 2>&1 | FileCheck %s + +#include "llvm-c/Core.h" +#include "llvm-c/Support.h" +#include "llvm-c/Types.h" + +#include "mlir-c/BuiltinTypes.h" +#include "mlir-c/Dialect/LLVM.h" +#include "mlir-c/IR.h" +#include "mlir-c/RegisterEverything.h" +#include "mlir-c/Support.h" +#include "mlir-c/Target/LLVMIR.h" + +#include +#include +#include +#include +#include + +// CHECK-LABEL: testToLLVMIR() +static void testToLLVMIR(MlirContext ctx) { + fprintf(stderr, "testToLLVMIR()\n"); + LLVMContextRef llvmCtx = LLVMContextCreate(); + + const char *moduleString = "llvm.func @add(%arg0: i64, %arg1: i64) -> i64 { \ + %0 = llvm.add %arg0, %arg1 : i64 \ + llvm.return %0 : i64 \ + }"; + + mlirRegisterAllLLVMTranslations(ctx); + + MlirModule module = + mlirModuleCreateParse(ctx, mlirStringRefCreateFromCString(moduleString)); + + MlirOperation operation = mlirModuleGetOperation(module); + + LLVMModuleRef llvmModule = mlirTranslateModuleToLLVMIR(operation, llvmCtx); + + // clang-format off + // CHECK: define i64 @add(i64 %[[arg1:.*]], i64 %[[arg2:.*]]) { + // CHECK-NEXT: %[[arg3:.*]] = add i64 %[[arg1]], %[[arg2]] + // CHECK-NEXT: ret i64 %[[arg3]] + // CHECK-NEXT: } + // clang-format on + LLVMDumpModule(llvmModule); + + LLVMDisposeModule(llvmModule); + mlirModuleDestroy(module); +} + +int main(void) { + MlirContext ctx = mlirContextCreate(); + mlirDialectHandleRegisterDialect(mlirGetDialectHandle__llvm__(), ctx); + mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("llvm")); + testToLLVMIR(ctx); + mlirContextDestroy(ctx); + return 0; +} diff --git a/mlir/test/CMakeLists.txt b/mlir/test/CMakeLists.txt index e4343095578c1f..ccb4a98254cd57 100644 --- a/mlir/test/CMakeLists.txt +++ b/mlir/test/CMakeLists.txt @@ -45,10 +45,10 @@ if (MLIR_INCLUDE_INTEGRATION_TESTS) message(FATAL_ERROR "MLIR_INCLUDE_INTEGRATION_TESTS requires a native target") endif() - # When the Integration tests are requested via the MLIR_INCLUDE_INTEGRATION_TESTS - # configuration flag, we automatically include sm80 tests when build for - # cuSparse when the configuration flag MLIR_ENABLE_CUDA_CUSPARSE is set and - # include sm80 lt tests when the MLIR_ENABLE_CUDA_CUSPARSELT is set in + # When the Integration tests are requested via the MLIR_INCLUDE_INTEGRATION_TESTS + # configuration flag, we automatically include sm80 tests when build for + # cuSparse when the configuration flag MLIR_ENABLE_CUDA_CUSPARSE is set and + # include sm80 lt tests when the MLIR_ENABLE_CUDA_CUSPARSELT is set in # addition to those. if (MLIR_ENABLE_CUDA_CUSPARSE) set(MLIR_RUN_CUDA_SM80_TESTS ON) @@ -101,6 +101,7 @@ set(MLIR_TEST_DEPENDS mlir-capi-quant-test mlir-capi-sparse-tensor-test mlir-capi-transform-test + mlir-capi-translation-test mlir-linalg-ods-yaml-gen mlir-lsp-server mlir-pdll-lsp-server diff --git a/mlir/test/lit.cfg.py b/mlir/test/lit.cfg.py index 87bbe51e95d4c9..17c29445ba82b1 100644 --- a/mlir/test/lit.cfg.py +++ b/mlir/test/lit.cfg.py @@ -106,6 +106,7 @@ def add_runtime(name): "mlir-capi-quant-test", "mlir-capi-sparse-tensor-test", "mlir-capi-transform-test", + "mlir-capi-translation-test", "mlir-cpu-runner", add_runtime("mlir_runner_utils"), add_runtime("mlir_c_runner_utils"), diff --git a/openmp/libomptarget/src/CMakeLists.txt b/openmp/libomptarget/src/CMakeLists.txt index 4ef0b8124acdb4..a83965f59b3eb0 100644 --- a/openmp/libomptarget/src/CMakeLists.txt +++ b/openmp/libomptarget/src/CMakeLists.txt @@ -44,6 +44,12 @@ if (LIBOMP_HAVE_VERSION_SCRIPT_FLAG) "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/exports") endif() +# Define the TARGET_NAME and DEBUG_PREFIX. +target_compile_definitions(omptarget PRIVATE + TARGET_NAME=omptarget + DEBUG_PREFIX="omptarget" +) + # libomptarget.so needs to be aware of where the plugins live as they # are now separated in the build directory. set_target_properties(omptarget PROPERTIES diff --git a/openmp/libomptarget/src/private.h b/openmp/libomptarget/src/private.h index f082f6e3b9fc83..eb54b1a635418a 100644 --- a/openmp/libomptarget/src/private.h +++ b/openmp/libomptarget/src/private.h @@ -257,11 +257,6 @@ struct TargetMemsetArgsTy { } #endif -#define TARGET_NAME Libomptarget -#ifndef DEBUG_PREFIX -#define DEBUG_PREFIX GETNAME(TARGET_NAME) -#endif - //////////////////////////////////////////////////////////////////////////////// /// dump a table of all the host-target pointer pairs on failure static inline void dumpTargetPointerMappings(const ident_t *Loc, diff --git a/openmp/libomptarget/test/env/omp_target_debug.c b/openmp/libomptarget/test/env/omp_target_debug.c index 76d182d2481c0d..ec81873a091f50 100644 --- a/openmp/libomptarget/test/env/omp_target_debug.c +++ b/openmp/libomptarget/test/env/omp_target_debug.c @@ -8,6 +8,6 @@ int main(void) { return 0; } -// DEBUG: Libomptarget -// NDEBUG-NOT: Libomptarget +// DEBUG: omptarget +// NDEBUG-NOT: omptarget // NDEBUG-NOT: Target diff --git a/openmp/libomptarget/test/mapping/alloc_fail.c b/openmp/libomptarget/test/mapping/alloc_fail.c index d764087723baa4..c4ae70fc730c2d 100644 --- a/openmp/libomptarget/test/mapping/alloc_fail.c +++ b/openmp/libomptarget/test/mapping/alloc_fail.c @@ -2,9 +2,9 @@ // RUN: %libomptarget-run-fail-generic 2>&1 \ // RUN: | %fcheck-generic -// CHECK: Libomptarget message: explicit extension not allowed: host address specified is 0x{{.*}} (8 bytes), but device allocation maps to host at 0x{{.*}} (8 bytes) -// CHECK: Libomptarget error: Call to getTargetPointer returned null pointer (device failure or illegal mapping). -// CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory +// CHECK: omptarget message: explicit extension not allowed: host address specified is 0x{{.*}} (8 bytes), but device allocation maps to host at 0x{{.*}} (8 bytes) +// CHECK: omptarget error: Call to getTargetPointer returned null pointer (device failure or illegal mapping). +// CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory int main() { int arr[4] = {0, 1, 2, 3}; diff --git a/openmp/libomptarget/test/mapping/ompx_hold/omp_target_disassociate_ptr.c b/openmp/libomptarget/test/mapping/ompx_hold/omp_target_disassociate_ptr.c index ea4a1ffc979a5e..302bf4ffd0a5f4 100644 --- a/openmp/libomptarget/test/mapping/ompx_hold/omp_target_disassociate_ptr.c +++ b/openmp/libomptarget/test/mapping/ompx_hold/omp_target_disassociate_ptr.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { #pragma omp target data map(ompx_hold, alloc : X) #endif { - // CHECK: Libomptarget error: Trying to disassociate a pointer with a + // CHECK: omptarget error: Trying to disassociate a pointer with a // CHECK-SAME: non-zero hold reference count // CHECK-NEXT: omp_target_disassociate_ptr failed if (omp_target_disassociate_ptr(&X, DevNum)) { diff --git a/openmp/libomptarget/test/mapping/padding_not_mapped.c b/openmp/libomptarget/test/mapping/padding_not_mapped.c index 9d5ef212ffc246..3ee70ab64048d4 100644 --- a/openmp/libomptarget/test/mapping/padding_not_mapped.c +++ b/openmp/libomptarget/test/mapping/padding_not_mapped.c @@ -34,9 +34,9 @@ int main() { #pragma omp target update from(s.x) // should have no effect fprintf(stderr, "s.x = %d\n", s.x); - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) - // CHECK: Libomptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) + // CHECK: omptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target enter data map(present, alloc: s.x) return 0; diff --git a/openmp/libomptarget/test/mapping/present/target.c b/openmp/libomptarget/test/mapping/present/target.c index 1bf724e308e525..4344c42c808ffe 100644 --- a/openmp/libomptarget/test/mapping/present/target.c +++ b/openmp/libomptarget/test/mapping/present/target.c @@ -10,7 +10,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", &i, sizeof i); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : i) #pragma omp target map(present, alloc : i) ; @@ -18,11 +18,11 @@ int main() { // CHECK: i is present fprintf(stderr, "i is present\n"); - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) - // CHECK: Libomptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). - // CHECK: Libomptarget error: Call to targetDataBegin failed, abort target. - // CHECK: Libomptarget error: Failed to process data before launching the kernel. - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) + // CHECK: omptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). + // CHECK: omptarget error: Call to targetDataBegin failed, abort target. + // CHECK: omptarget error: Failed to process data before launching the kernel. + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target map(present, alloc : i) ; diff --git a/openmp/libomptarget/test/mapping/present/target_array_extension.c b/openmp/libomptarget/test/mapping/present/target_array_extension.c index 063eafd8307f01..873b2b36170ee8 100644 --- a/openmp/libomptarget/test/mapping/present/target_array_extension.c +++ b/openmp/libomptarget/test/mapping/present/target_array_extension.c @@ -60,7 +60,7 @@ int main() { fprintf(stderr, "addr=%p, size=%ld\n", &arr[LARGE_BEG], LARGE_SIZE * sizeof arr[0]); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : arr[LARGE]) { #pragma omp target map(present, tofrom : arr[SMALL]) @@ -70,12 +70,12 @@ int main() { // CHECK: arr is present fprintf(stderr, "arr is present\n"); - // CHECK: Libomptarget message: explicit extension not allowed: host address specified is 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes), but device allocation maps to host at 0x{{0*}}[[#SMALL_ADDR]] ([[#SMALL_BYTES]] bytes) - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes) - // CHECK: Libomptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). - // CHECK: Libomptarget error: Call to targetDataBegin failed, abort target. - // CHECK: Libomptarget error: Failed to process data before launching the kernel. - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: explicit extension not allowed: host address specified is 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes), but device allocation maps to host at 0x{{0*}}[[#SMALL_ADDR]] ([[#SMALL_BYTES]] bytes) + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes) + // CHECK: omptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). + // CHECK: omptarget error: Call to targetDataBegin failed, abort target. + // CHECK: omptarget error: Failed to process data before launching the kernel. + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target data map(alloc : arr[SMALL]) { #pragma omp target map(present, tofrom : arr[LARGE]) diff --git a/openmp/libomptarget/test/mapping/present/target_data.c b/openmp/libomptarget/test/mapping/present/target_data.c index 7acc850d349b88..f894283a0c99c8 100644 --- a/openmp/libomptarget/test/mapping/present/target_data.c +++ b/openmp/libomptarget/test/mapping/present/target_data.c @@ -10,7 +10,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", &i, sizeof i); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : i) #pragma omp target data map(present, alloc : i) ; @@ -18,8 +18,8 @@ int main() { // CHECK: i is present fprintf(stderr, "i is present\n"); - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target data map(present, alloc : i) ; diff --git a/openmp/libomptarget/test/mapping/present/target_data_array_extension.c b/openmp/libomptarget/test/mapping/present/target_data_array_extension.c index 783ab4a9a56a7c..794543a246040b 100644 --- a/openmp/libomptarget/test/mapping/present/target_data_array_extension.c +++ b/openmp/libomptarget/test/mapping/present/target_data_array_extension.c @@ -60,7 +60,7 @@ int main() { fprintf(stderr, "addr=%p, size=%ld\n", &arr[LARGE_BEG], LARGE_SIZE * sizeof arr[0]); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : arr[LARGE]) { #pragma omp target data map(present, tofrom : arr[SMALL]) @@ -70,10 +70,10 @@ int main() { // CHECK: arr is present fprintf(stderr, "arr is present\n"); - // CHECK: Libomptarget message: explicit extension not allowed: host address specified is 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes), but device allocation maps to host at 0x{{0*}}[[#SMALL_ADDR]] ([[#SMALL_BYTES]] bytes) - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes) - // CHECK: Libomptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: explicit extension not allowed: host address specified is 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes), but device allocation maps to host at 0x{{0*}}[[#SMALL_ADDR]] ([[#SMALL_BYTES]] bytes) + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#LARGE_ADDR]] ([[#LARGE_BYTES]] bytes) + // CHECK: omptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target data map(alloc : arr[SMALL]) { #pragma omp target data map(present, tofrom : arr[LARGE]) diff --git a/openmp/libomptarget/test/mapping/present/target_data_at_exit.c b/openmp/libomptarget/test/mapping/present/target_data_at_exit.c index f4b0af8c3e6749..c1fbbae6f764d6 100644 --- a/openmp/libomptarget/test/mapping/present/target_data_at_exit.c +++ b/openmp/libomptarget/test/mapping/present/target_data_at_exit.c @@ -16,9 +16,9 @@ int main() { #pragma omp target exit data map(delete : i) } - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget // CHECK: success - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget fprintf(stderr, "success\n"); return 0; diff --git a/openmp/libomptarget/test/mapping/present/target_enter_data.c b/openmp/libomptarget/test/mapping/present/target_enter_data.c index 3695fe7ceb4ec3..871a05290ed654 100644 --- a/openmp/libomptarget/test/mapping/present/target_enter_data.c +++ b/openmp/libomptarget/test/mapping/present/target_enter_data.c @@ -10,7 +10,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", &i, sizeof i); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target enter data map(alloc : i) #pragma omp target enter data map(present, alloc : i) #pragma omp target exit data map(delete : i) @@ -18,9 +18,9 @@ int main() { // CHECK: i is present fprintf(stderr, "i is present\n"); - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) - // CHECK: Libomptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) + // CHECK: omptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target enter data map(present, alloc : i) // CHECK-NOT: i is present diff --git a/openmp/libomptarget/test/mapping/present/target_exit_data_delete.c b/openmp/libomptarget/test/mapping/present/target_exit_data_delete.c index 28f3cf1872ded7..0fb812b2998f93 100644 --- a/openmp/libomptarget/test/mapping/present/target_exit_data_delete.c +++ b/openmp/libomptarget/test/mapping/present/target_exit_data_delete.c @@ -10,15 +10,15 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", &i, sizeof i); -// CHECK-NOT: Libomptarget +// CHECK-NOT: omptarget #pragma omp target enter data map(alloc : i) #pragma omp target exit data map(present, delete : i) // CHECK: i was present fprintf(stderr, "i was present\n"); -// CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) -// CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory +// CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) +// CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target exit data map(present, delete : i) // CHECK-NOT: i was present diff --git a/openmp/libomptarget/test/mapping/present/target_exit_data_release.c b/openmp/libomptarget/test/mapping/present/target_exit_data_release.c index e03a0ca2e1b4de..14be22faba6210 100644 --- a/openmp/libomptarget/test/mapping/present/target_exit_data_release.c +++ b/openmp/libomptarget/test/mapping/present/target_exit_data_release.c @@ -10,15 +10,15 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", &i, sizeof i); -// CHECK-NOT: Libomptarget +// CHECK-NOT: omptarget #pragma omp target enter data map(alloc : i) #pragma omp target exit data map(present, release : i) // CHECK: i was present fprintf(stderr, "i was present\n"); -// CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) -// CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory +// CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) +// CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target exit data map(present, release : i) // CHECK-NOT: i was present diff --git a/openmp/libomptarget/test/mapping/present/target_update.c b/openmp/libomptarget/test/mapping/present/target_update.c index 75f4bc60dcc8a5..9f6783b6ef6939 100644 --- a/openmp/libomptarget/test/mapping/present/target_update.c +++ b/openmp/libomptarget/test/mapping/present/target_update.c @@ -24,7 +24,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", &i, sizeof i); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target enter data map(alloc : i) #pragma omp target update CLAUSE(present : i) #pragma omp target exit data map(delete : i) @@ -32,8 +32,8 @@ int main() { // CHECK: i is present fprintf(stderr, "i is present\n"); - // CHECK: Libomptarget message: device mapping required by 'present' motion modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' motion modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target update CLAUSE(present : i) // CHECK-NOT: i is present diff --git a/openmp/libomptarget/test/mapping/present/target_update_array_extension.c b/openmp/libomptarget/test/mapping/present/target_update_array_extension.c index 45895044e122a8..11ad4a8d493821 100644 --- a/openmp/libomptarget/test/mapping/present/target_update_array_extension.c +++ b/openmp/libomptarget/test/mapping/present/target_update_array_extension.c @@ -57,7 +57,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]], size=[[#%u,SIZE:]] fprintf(stderr, "addr=%p, size=%ld\n", arr, sizeof arr); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : arr[LARGE]) { #pragma omp target update CLAUSE(present : arr[SMALL]) @@ -66,8 +66,8 @@ int main() { // CHECK: arr is present fprintf(stderr, "arr is present\n"); - // CHECK: Libomptarget message: device mapping required by 'present' motion modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' motion modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] ([[#SIZE]] bytes) + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target data map(alloc : arr[SMALL]) { #pragma omp target update CLAUSE(present : arr[LARGE]) diff --git a/openmp/libomptarget/test/mapping/present/unified_shared_memory.c b/openmp/libomptarget/test/mapping/present/unified_shared_memory.c index f0de92f12310e5..ab6e3bd0e5fa70 100644 --- a/openmp/libomptarget/test/mapping/present/unified_shared_memory.c +++ b/openmp/libomptarget/test/mapping/present/unified_shared_memory.c @@ -12,7 +12,7 @@ int main() { int i; - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : i) #pragma omp target map(present, alloc : i) ; @@ -20,7 +20,7 @@ int main() { // CHECK: i is present fprintf(stderr, "i is present\n"); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target map(present, alloc : i) ; diff --git a/openmp/libomptarget/test/mapping/present/zero_length_array_section.c b/openmp/libomptarget/test/mapping/present/zero_length_array_section.c index 4f08d334eef196..e903a268ef335e 100644 --- a/openmp/libomptarget/test/mapping/present/zero_length_array_section.c +++ b/openmp/libomptarget/test/mapping/present/zero_length_array_section.c @@ -10,7 +10,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]] fprintf(stderr, "addr=%p\n", arr); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : arr[0 : 5]) #pragma omp target map(present, alloc : arr[0 : 0]) ; @@ -20,11 +20,11 @@ int main() { // arr[0:0] doesn't create an actual mapping in the first directive. // - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] (0 bytes) - // CHECK: Libomptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). - // CHECK: Libomptarget error: Call to targetDataBegin failed, abort target. - // CHECK: Libomptarget error: Failed to process data before launching the kernel. - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] (0 bytes) + // CHECK: omptarget error: Call to getTargetPointer returned null pointer ('present' map type modifier). + // CHECK: omptarget error: Call to targetDataBegin failed, abort target. + // CHECK: omptarget error: Failed to process data before launching the kernel. + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target data map(alloc : arr[0 : 0]) #pragma omp target map(present, alloc : arr[0 : 0]) ; diff --git a/openmp/libomptarget/test/mapping/present/zero_length_array_section_exit.c b/openmp/libomptarget/test/mapping/present/zero_length_array_section_exit.c index cf9d3c4a936fb1..5a7360542ec3c3 100644 --- a/openmp/libomptarget/test/mapping/present/zero_length_array_section_exit.c +++ b/openmp/libomptarget/test/mapping/present/zero_length_array_section_exit.c @@ -10,7 +10,7 @@ int main() { // CHECK: addr=0x[[#%x,HOST_ADDR:]] fprintf(stderr, "addr=%p\n", arr); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target enter data map(alloc : arr[0 : 5]) #pragma omp target exit data map(present, release : arr[0 : 0]) @@ -19,8 +19,8 @@ int main() { // arr[0:0] doesn't create an actual mapping in the first directive. // - // CHECK: Libomptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] (0 bytes) - // CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory + // CHECK: omptarget message: device mapping required by 'present' map type modifier does not exist for host address 0x{{0*}}[[#HOST_ADDR]] (0 bytes) + // CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory #pragma omp target enter data map(alloc : arr[0 : 0]) #pragma omp target exit data map(present, release : arr[0 : 0]) diff --git a/openmp/libomptarget/test/mapping/target_data_array_extension_at_exit.c b/openmp/libomptarget/test/mapping/target_data_array_extension_at_exit.c index b0027d90361a83..e300c800b2551e 100644 --- a/openmp/libomptarget/test/mapping/target_data_array_extension_at_exit.c +++ b/openmp/libomptarget/test/mapping/target_data_array_extension_at_exit.c @@ -66,7 +66,7 @@ void check_not_present() { arr[i] = 88; } - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget // CHECK-NOT: error for (int i = 0; i < SIZE; ++i) { if (arr[i] != 99) @@ -95,7 +95,7 @@ void check_is_present() { arr[i] = 88; } - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget // CHECK-NOT: error for (int i = 0; i < SIZE; ++i) { if (SMALL_BEG <= i && i < SMALL_END) { diff --git a/openmp/libomptarget/test/mapping/target_update_array_extension.c b/openmp/libomptarget/test/mapping/target_update_array_extension.c index c6c42bbdde1f32..ee926feb5c28e2 100644 --- a/openmp/libomptarget/test/mapping/target_update_array_extension.c +++ b/openmp/libomptarget/test/mapping/target_update_array_extension.c @@ -54,7 +54,7 @@ int main() { int arr[5]; - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : arr[LARGE]) { #pragma omp target update CLAUSE(arr[SMALL]) @@ -63,7 +63,7 @@ int main() { // CHECK: success fprintf(stderr, "success\n"); - // CHECK-NOT: Libomptarget + // CHECK-NOT: omptarget #pragma omp target data map(alloc : arr[SMALL]) { #pragma omp target update CLAUSE(arr[LARGE]) diff --git a/openmp/libomptarget/test/mapping/target_wrong_use_device_addr.c b/openmp/libomptarget/test/mapping/target_wrong_use_device_addr.c index bb5a1d44031a3f..6c7939ea196ad5 100644 --- a/openmp/libomptarget/test/mapping/target_wrong_use_device_addr.c +++ b/openmp/libomptarget/test/mapping/target_wrong_use_device_addr.c @@ -13,7 +13,7 @@ int main() { #pragma omp target data map(to : x [0:10]) { -// CHECK: Libomptarget device 0 info: variable x does not have a valid device +// CHECK: omptarget device 0 info: variable x does not have a valid device // counterpart #pragma omp target data use_device_addr(x) { diff --git a/openmp/libomptarget/test/offloading/info.c b/openmp/libomptarget/test/offloading/info.c index ecb5efb9b257e1..81300cc92ec56d 100644 --- a/openmp/libomptarget/test/offloading/info.c +++ b/openmp/libomptarget/test/offloading/info.c @@ -64,7 +64,7 @@ int main() { { val = 1; } __tgt_set_info_flag(0x0); -// INFO-NOT: Libomptarget device 0 info: {{.*}} +// INFO-NOT: omptarget device 0 info: {{.*}} #pragma omp target {} diff --git a/openmp/libomptarget/test/offloading/mandatory_but_no_devices.c b/openmp/libomptarget/test/offloading/mandatory_but_no_devices.c index 365efb6a076e87..ecdee72acad04f 100644 --- a/openmp/libomptarget/test/offloading/mandatory_but_no_devices.c +++ b/openmp/libomptarget/test/offloading/mandatory_but_no_devices.c @@ -47,7 +47,7 @@ #include #include -// CHECK: Libomptarget fatal error 1: failure of target construct while offloading is mandatory +// CHECK: omptarget fatal error 1: failure of target construct while offloading is mandatory int main(void) { int X; #pragma omp DIR device(omp_get_initial_device())