diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8be1ea2fb0145..8c290437fe16f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -122,6 +122,8 @@ Bug Fixes in This Version Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- The behvaiour of ``__add_pointer`` and ``__remove_pointer`` for Objective-C++'s ``id`` and interfaces has been fixed. + Bug Fixes to Attribute Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DynamicRecursiveASTVisitor.h b/clang/include/clang/AST/DynamicRecursiveASTVisitor.h index 4e0ba568263bf..703cca22777ad 100644 --- a/clang/include/clang/AST/DynamicRecursiveASTVisitor.h +++ b/clang/include/clang/AST/DynamicRecursiveASTVisitor.h @@ -251,11 +251,11 @@ template class DynamicRecursiveASTVisitorBase { // Decls. #define ABSTRACT_DECL(DECL) #define DECL(CLASS, BASE) \ + bool WalkUpFrom##CLASS##Decl(MaybeConst *D); \ virtual bool Traverse##CLASS##Decl(MaybeConst *D); #include "clang/AST/DeclNodes.inc" #define DECL(CLASS, BASE) \ - bool WalkUpFrom##CLASS##Decl(MaybeConst *D); \ virtual bool Visit##CLASS##Decl(MaybeConst *D) { return true; } #include "clang/AST/DeclNodes.inc" @@ -272,11 +272,11 @@ template class DynamicRecursiveASTVisitorBase { // Types. #define ABSTRACT_TYPE(CLASS, BASE) #define TYPE(CLASS, BASE) \ + bool WalkUpFrom##CLASS##Type(MaybeConst *T); \ virtual bool Traverse##CLASS##Type(MaybeConst *T); #include "clang/AST/TypeNodes.inc" #define TYPE(CLASS, BASE) \ - bool WalkUpFrom##CLASS##Type(MaybeConst *T); \ virtual bool Visit##CLASS##Type(MaybeConst *T) { return true; } #include "clang/AST/TypeNodes.inc" diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h index 2ab612613b73d..5f9110bc83b89 100644 --- a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h +++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h @@ -26,6 +26,7 @@ class CIRGenAction : public clang::ASTFrontendAction { public: enum class OutputType { EmitCIR, + EmitLLVM, }; private: @@ -55,6 +56,13 @@ class EmitCIRAction : public CIRGenAction { EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr); }; +class EmitLLVMAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitLLVMAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + } // namespace cir #endif diff --git a/clang/include/clang/CIR/LowerToLLVM.h b/clang/include/clang/CIR/LowerToLLVM.h new file mode 100644 index 0000000000000..9fd0706a91a5a --- /dev/null +++ b/clang/include/clang/CIR/LowerToLLVM.h @@ -0,0 +1,36 @@ +//====- LowerToLLVM.h- Lowering from CIR to LLVM --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares an interface for converting CIR modules to LLVM IR. +// +//===----------------------------------------------------------------------===// +#ifndef CLANG_CIR_LOWERTOLLVM_H +#define CLANG_CIR_LOWERTOLLVM_H + +#include "mlir/Pass/Pass.h" + +#include + +namespace llvm { +class LLVMContext; +class Module; +} // namespace llvm + +namespace mlir { +class ModuleOp; +} // namespace mlir + +namespace cir { + +namespace direct { +std::unique_ptr +lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp M, llvm::LLVMContext &Ctx); +} // namespace direct +} // namespace cir + +#endif // CLANG_CIR_LOWERTOLLVM_H diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index f3ef8525e15c2..4a99ecb33dfb2 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -5,3 +5,4 @@ add_subdirectory(Dialect) add_subdirectory(CodeGen) add_subdirectory(FrontendAction) add_subdirectory(Interfaces) +add_subdirectory(Lowering) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 21b6bc56ed050..eab6958ac8f6d 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -7,23 +7,47 @@ //===----------------------------------------------------------------------===// #include "clang/CIR/FrontendAction/CIRGenAction.h" -#include "clang/CIR/CIRGenerator.h" -#include "clang/Frontend/CompilerInstance.h" - #include "mlir/IR/MLIRContext.h" #include "mlir/IR/OwningOpRef.h" +#include "clang/CIR/CIRGenerator.h" +#include "clang/CIR/LowerToLLVM.h" +#include "clang/CodeGen/BackendUtil.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/IR/Module.h" using namespace cir; using namespace clang; namespace cir { +static BackendAction +getBackendActionFromOutputType(CIRGenAction::OutputType Action) { + switch (Action) { + case CIRGenAction::OutputType::EmitCIR: + assert(false && + "Unsupported output type for getBackendActionFromOutputType!"); + break; // Unreachable, but fall through to report that + case CIRGenAction::OutputType::EmitLLVM: + return BackendAction::Backend_EmitLL; + } + // We should only get here if a non-enum value is passed in or we went through + // the assert(false) case above + llvm_unreachable("Unsupported output type!"); +} + +static std::unique_ptr +lowerFromCIRToLLVMIR(mlir::ModuleOp MLIRModule, llvm::LLVMContext &LLVMCtx) { + return direct::lowerDirectlyFromCIRToLLVMIR(MLIRModule, LLVMCtx); +} + class CIRGenConsumer : public clang::ASTConsumer { virtual void anchor(); CIRGenAction::OutputType Action; + CompilerInstance &CI; + std::unique_ptr OutputStream; ASTContext *Context{nullptr}; @@ -31,18 +55,12 @@ class CIRGenConsumer : public clang::ASTConsumer { std::unique_ptr Gen; public: - CIRGenConsumer(CIRGenAction::OutputType Action, - DiagnosticsEngine &DiagnosticsEngine, - IntrusiveRefCntPtr VFS, - const HeaderSearchOptions &HeaderSearchOptions, - const CodeGenOptions &CodeGenOptions, - const TargetOptions &TargetOptions, - const LangOptions &LangOptions, - const FrontendOptions &FEOptions, + CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI, std::unique_ptr OS) - : Action(Action), OutputStream(std::move(OS)), FS(VFS), - Gen(std::make_unique(DiagnosticsEngine, std::move(VFS), - CodeGenOptions)) {} + : Action(Action), CI(CI), OutputStream(std::move(OS)), + FS(&CI.getVirtualFileSystem()), + Gen(std::make_unique(CI.getDiagnostics(), std::move(FS), + CI.getCodeGenOpts())) {} void Initialize(ASTContext &Ctx) override { assert(!Context && "initialized multiple times"); @@ -66,6 +84,17 @@ class CIRGenConsumer : public clang::ASTConsumer { MlirModule->print(*OutputStream, Flags); } break; + case CIRGenAction::OutputType::EmitLLVM: { + llvm::LLVMContext LLVMCtx; + std::unique_ptr LLVMModule = + lowerFromCIRToLLVMIR(MlirModule, LLVMCtx); + + BackendAction BEAction = getBackendActionFromOutputType(Action); + emitBackendOutput( + CI, CI.getCodeGenOpts(), C.getTargetInfo().getDataLayoutString(), + LLVMModule.get(), BEAction, FS, std::move(OutputStream)); + break; + } } } }; @@ -84,6 +113,8 @@ getOutputStream(CompilerInstance &CI, StringRef InFile, switch (Action) { case CIRGenAction::OutputType::EmitCIR: return CI.createDefaultOutputFile(false, InFile, "cir"); + case CIRGenAction::OutputType::EmitLLVM: + return CI.createDefaultOutputFile(false, InFile, "ll"); } llvm_unreachable("Invalid CIRGenAction::OutputType"); } @@ -95,10 +126,8 @@ CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (!Out) Out = getOutputStream(CI, InFile, Action); - auto Result = std::make_unique( - Action, CI.getDiagnostics(), &CI.getVirtualFileSystem(), - CI.getHeaderSearchOpts(), CI.getCodeGenOpts(), CI.getTargetOpts(), - CI.getLangOpts(), CI.getFrontendOpts(), std::move(Out)); + auto Result = + std::make_unique(Action, CI, std::move(Out)); return Result; } @@ -106,3 +135,7 @@ CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { void EmitCIRAction::anchor() {} EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {} + +void EmitLLVMAction::anchor() {} +EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {} diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt index b0616ab5d64b0..d87ff7665987d 100644 --- a/clang/lib/CIR/FrontendAction/CMakeLists.txt +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangCIRFrontendAction clangAST clangFrontend clangCIR + clangCIRLoweringDirectToLLVM MLIRCIR MLIRIR ) diff --git a/clang/lib/CIR/Lowering/CMakeLists.txt b/clang/lib/CIR/Lowering/CMakeLists.txt new file mode 100644 index 0000000000000..95c304ded9183 --- /dev/null +++ b/clang/lib/CIR/Lowering/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(DirectToLLVM) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt new file mode 100644 index 0000000000000..0268234c3a289 --- /dev/null +++ b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +add_clang_library(clangCIRLoweringDirectToLLVM + LowerToLLVM.cpp + ) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp new file mode 100644 index 0000000000000..3687e482fa9bb --- /dev/null +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -0,0 +1,39 @@ +//====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements lowering of CIR operations to LLVMIR. +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/LowerToLLVM.h" + +#include "mlir/IR/BuiltinOps.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/TimeProfiler.h" + +using namespace cir; +using namespace llvm; + +namespace cir { +namespace direct { + +std::unique_ptr +lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp MOp, LLVMContext &LLVMCtx) { + llvm::TimeTraceScope scope("lower from CIR to LLVM directly"); + + std::optional ModuleName = MOp.getName(); + auto M = std::make_unique( + ModuleName ? *ModuleName : "CIRToLLVMModule", LLVMCtx); + + if (!M) + report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!"); + + return M; +} +} // namespace direct +} // namespace cir diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 079bcd93d6162..c8d004163b96d 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -71,7 +71,13 @@ CreateFrontendBaseAction(CompilerInstance &CI) { llvm_unreachable("CIR suppport not built into clang"); #endif case EmitHTML: return std::make_unique(); - case EmitLLVM: return std::make_unique(); + case EmitLLVM: { +#if CLANG_ENABLE_CIR + if (UseCIR) + return std::make_unique(); +#endif + return std::make_unique(); + } case EmitLLVMOnly: return std::make_unique(); case EmitCodeGenOnly: return std::make_unique(); case EmitObj: return std::make_unique(); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index cbd99a1f9e9db..f7e627661d576 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -4573,7 +4573,9 @@ static void TryConstructorInitialization(Sema &S, CXXConstructorDecl *CtorDecl = cast(Best->Function); if (Result != OR_Deleted) { - if (!IsListInit && Kind.getKind() == InitializationKind::IK_Default && + if (!IsListInit && + (Kind.getKind() == InitializationKind::IK_Default || + Kind.getKind() == InitializationKind::IK_Direct) && DestRecordDecl != nullptr && DestRecordDecl->isAggregate() && DestRecordDecl->hasUninitializedExplicitInitFields()) { S.Diag(Kind.getLocation(), diag::warn_field_requires_explicit_init) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 8572ac03a9ff4..ef66d4ad6f43c 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1835,7 +1835,8 @@ QualType Sema::BuildPointerType(QualType T, if (checkQualifiedFunction(*this, T, Loc, QFK_Pointer)) return QualType(); - assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType"); + if (T->isObjCObjectType()) + return Context.getObjCObjectPointerType(T); // In ARC, it is forbidden to build pointers to unqualified pointers. if (getLangOpts().ObjCAutoRefCount) @@ -9899,8 +9900,7 @@ QualType Sema::BuiltinAddPointer(QualType BaseType, SourceLocation Loc) { } QualType Sema::BuiltinRemovePointer(QualType BaseType, SourceLocation Loc) { - // We don't want block pointers or ObjectiveC's id type. - if (!BaseType->isAnyPointerType() || BaseType->isObjCIdType()) + if (!BaseType->isAnyPointerType()) return BaseType; return BaseType->getPointeeType(); diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 55bcb6e220e1e..7b2cccce93cfe 100644 --- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -488,15 +488,17 @@ ExplodedGraph::trim(ArrayRef Sinks, while (!WL2.empty()) { const ExplodedNode *N = WL2.pop_back_val(); + auto [Place, Inserted] = Pass2.try_emplace(N); + // Skip this node if we have already processed it. - if (Pass2.contains(N)) + if (!Inserted) continue; // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->getID(), N->isSink()); - Pass2[N] = NewN; + Place->second = NewN; // Also record the reverse mapping from the new node to the old node. if (InverseMap) (*InverseMap)[NewN] = N; diff --git a/clang/test/CIR/Lowering/hello.c b/clang/test/CIR/Lowering/hello.c new file mode 100644 index 0000000000000..320041f0ab7dc --- /dev/null +++ b/clang/test/CIR/Lowering/hello.c @@ -0,0 +1,8 @@ +// Smoke test for ClangIR-to-LLVM IR code generation +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s + +// TODO: Add checks when proper lowering is implemented. +// For now, we're just creating an empty module. +// CHECK: ModuleID + +void foo() {} diff --git a/clang/test/Driver/tls-dialect.c b/clang/test/Driver/tls-dialect.c index 3471b55b0ebae..9ab79e87353d8 100644 --- a/clang/test/Driver/tls-dialect.c +++ b/clang/test/Driver/tls-dialect.c @@ -10,6 +10,11 @@ /// TLSDESC is not on by default in Linux, even on RISC-V, and is covered above // RUN: %clang -### --target=riscv64-android %s 2>&1 | FileCheck --check-prefix=DESC %s +/// Fuchsia supports TLSDESC by default for all architectures. +// RUN: %clang -### --target=riscv64-unknown-fuchsia %s 2>&1 | FileCheck --check-prefix=DESC %s +// RUN: %clang -### --target=aarch64-unknown-fuchsia %s 2>&1 | FileCheck --check-prefix=DESC %s +// RUN: %clang -### --target=x86_64-unknown-fuchsia %s 2>&1 | FileCheck --check-prefix=DESC %s + /// LTO // RUN: %clang -### --target=loongarch64-linux -flto -mtls-dialect=desc %s 2>&1 | FileCheck --check-prefix=LTO-DESC %s // RUN: %clang -### --target=loongarch64-linux -flto %s 2>&1 | FileCheck --check-prefix=LTO-NODESC %s diff --git a/clang/test/Interpreter/simple-exception.cpp b/clang/test/Interpreter/simple-exception.cpp index 651e8d9402f89..6749acd6e6bd2 100644 --- a/clang/test/Interpreter/simple-exception.cpp +++ b/clang/test/Interpreter/simple-exception.cpp @@ -1,7 +1,7 @@ // clang-format off // UNSUPPORTED: system-aix -// XFAIL for arm, or running on Windows. -// XFAIL: target=arm-{{.*}}, target=armv{{.*}}, system-windows +// XFAIL for arm and arm64, or running on Windows. +// XFAIL: target=arm{{.*}}, system-windows // RUN: cat %s | clang-repl | FileCheck %s // Incompatible with msan. It passes with -O3 but fail -Oz. Interpreter diff --git a/clang/test/SemaCXX/remove_pointer.mm b/clang/test/SemaCXX/remove_pointer.mm deleted file mode 100644 index d1cf1fa9f4efc..0000000000000 --- a/clang/test/SemaCXX/remove_pointer.mm +++ /dev/null @@ -1,8 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s - -// expected-no-diagnostics - -@class X; - -static_assert(__is_same(__remove_pointer(X *), X), ""); -static_assert(__is_same(__remove_pointer(id), id), ""); diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp index 52d9897cf9be6..7578b288d7b3f 100644 --- a/clang/test/SemaCXX/uninitialized.cpp +++ b/clang/test/SemaCXX/uninitialized.cpp @@ -2,6 +2,8 @@ // RUN: %clang_cc1 -fsyntax-only -Wall -Wc++20-compat -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++1z -verify %s -fexperimental-new-constant-interpreter // RUN: %clang_cc1 -fsyntax-only -Wall -Wc++20-compat -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -Wno-uninitialized-const-reference -std=c++20 -verify %s +void* operator new(__SIZE_TYPE__, void*); + // definitions for std::move namespace std { inline namespace foo { @@ -1540,6 +1542,48 @@ void aggregate() { }; }; + struct Embed { + int embed1; // #FIELD_EMBED1 + int embed2 [[clang::require_explicit_initialization]]; // #FIELD_EMBED2 + }; + struct EmbedDerived : Embed {}; + struct F { + Embed f1; + // expected-warning@+1 {{field in 'Embed' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + explicit F(const char(&)[1]) : f1() { + // expected-warning@+1 {{field in 'Embed' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + ::new(static_cast(&f1)) decltype(f1); + // expected-warning@+1 {{field in 'Embed' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + ::new(static_cast(&f1)) decltype(f1)(); +#if __cplusplus >= 202002L + // expected-warning@+1 {{field 'embed2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + ::new(static_cast(&f1)) decltype(f1)(1); +#endif + // expected-warning@+1 {{field 'embed2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + ::new(static_cast(&f1)) decltype(f1){1}; + } +#if __cplusplus >= 202002L + // expected-warning@+1 {{field 'embed2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + explicit F(const char(&)[2]) : f1(1) {} +#else + explicit F(const char(&)[2]) : f1{1, 2} { } +#endif + // expected-warning@+1 {{field 'embed2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + explicit F(const char(&)[3]) : f1{} {} + // expected-warning@+1 {{field 'embed2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + explicit F(const char(&)[4]) : f1{1} {} + // expected-warning@+1 {{field 'embed2' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_EMBED2 {{'embed2' declared here}} + explicit F(const char(&)[5]) : f1{.embed1 = 1} {} + }; + F ctors[] = { + F(""), + F("_"), + F("__"), + F("___"), + F("____") + }; + (void)ctors; + S::foo(S{1, 2, 3, 4}); S::foo(S{.s1 = 100, .s4 = 100}); S::foo(S{.s1 = 100}); // expected-warning {{field 's4' requires explicit initialization but is not explicitly initialized}} expected-note@#FIELD_S4 {{'s4' declared here}} diff --git a/clang/test/SemaObjCXX/type-traits.mm b/clang/test/SemaObjCXX/type-traits.mm new file mode 100644 index 0000000000000..81b9573b52192 --- /dev/null +++ b/clang/test/SemaObjCXX/type-traits.mm @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++17 %s + +// expected-no-diagnostics + +@interface I; +@end + +@class C; + +static_assert(__is_same(__add_pointer(id), id*)); +static_assert(__is_same(__add_pointer(I), I*)); + +static_assert(__is_same(__remove_pointer(C*), C)); +static_assert(!__is_same(__remove_pointer(id), id)); +static_assert(__is_same(__remove_pointer(id*), id)); +static_assert(__is_same(__remove_pointer(__add_pointer(id)), id)); +static_assert(__is_same(__add_pointer(__remove_pointer(id)), id)); diff --git a/compiler-rt/lib/orc/macho_platform.cpp b/compiler-rt/lib/orc/macho_platform.cpp index 4b603fd95e316..8ca68587aeb36 100644 --- a/compiler-rt/lib/orc/macho_platform.cpp +++ b/compiler-rt/lib/orc/macho_platform.cpp @@ -557,12 +557,6 @@ Error MachOPlatformRuntimeState::registerObjectPlatformSections( return make_error(ErrStream.str()); } - ORC_RT_DEBUG({ - printdbg(" UnwindInfo: %s, UseCallbackStyleUnwindInfo: %s\n", - UnwindInfo ? "true" : "false", - UseCallbackStyleUnwindInfo ? "true" : "false"); - }); - if (UnwindInfo && UseCallbackStyleUnwindInfo) { ORC_RT_DEBUG({ printdbg(" Registering new-style unwind info for:\n" diff --git a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp index 3638f1c36ddd9..e1471dfdf6807 100644 --- a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp @@ -32,7 +32,7 @@ template static scudo::Options getOptionsForConfig() { return AO.load(); } -template static void testSecondaryBasic(void) { +template static void testBasic(void) { using SecondaryT = scudo::MapAllocator>; scudo::Options Options = getOptionsForConfig>(); @@ -85,7 +85,7 @@ template static void testSecondaryBasic(void) { L->unmapTestOnly(); } -struct NoCacheConfig { +struct TestNoCacheConfig { static const bool MaySupportMemoryTagging = false; template using TSDRegistryT = void; template using PrimaryT = void; @@ -97,7 +97,7 @@ struct NoCacheConfig { }; }; -struct TestConfig { +struct TestCacheConfig { static const bool MaySupportMemoryTagging = false; template using TSDRegistryT = void; template using PrimaryT = void; @@ -117,15 +117,15 @@ struct TestConfig { }; }; -TEST(ScudoSecondaryTest, SecondaryBasic) { - testSecondaryBasic(); - testSecondaryBasic(); - testSecondaryBasic(); +TEST(ScudoSecondaryTest, Basic) { + testBasic(); + testBasic(); + testBasic(); } -struct MapAllocatorTest : public Test { - using Config = scudo::DefaultConfig; - using LargeAllocator = scudo::MapAllocator>; +struct ScudoSecondaryAllocatorTest : public Test { + using LargeAllocator = + scudo::MapAllocator>; void SetUp() override { Allocator->init(nullptr); } @@ -134,13 +134,13 @@ struct MapAllocatorTest : public Test { std::unique_ptr Allocator = std::make_unique(); scudo::Options Options = - getOptionsForConfig>(); + getOptionsForConfig>(); }; // This exercises a variety of combinations of size and alignment for the // MapAllocator. The size computation done here mimic the ones done by the // combined allocator. -TEST_F(MapAllocatorTest, SecondaryCombinations) { +TEST_F(ScudoSecondaryAllocatorTest, Combinations) { constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16); constexpr scudo::uptr HeaderSize = scudo::roundUp(8, MinAlign); for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) { @@ -168,7 +168,7 @@ TEST_F(MapAllocatorTest, SecondaryCombinations) { Str.output(); } -TEST_F(MapAllocatorTest, SecondaryIterate) { +TEST_F(ScudoSecondaryAllocatorTest, Iterate) { std::vector V; const scudo::uptr PageSize = scudo::getPageSizeCached(); for (scudo::uptr I = 0; I < 32U; I++) @@ -190,34 +190,8 @@ TEST_F(MapAllocatorTest, SecondaryIterate) { Str.output(); } -TEST_F(MapAllocatorTest, SecondaryCacheOptions) { - if (!Allocator->canCache(0U)) - TEST_SKIP("Secondary Cache disabled"); - - // Attempt to set a maximum number of entries higher than the array size. - EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4096U)); - - // Attempt to set an invalid (negative) number of entries - EXPECT_FALSE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, -1)); - - // Various valid combinations. - EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); - EXPECT_TRUE( - Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); - EXPECT_TRUE(Allocator->canCache(1UL << 18)); - EXPECT_TRUE( - Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17)); - EXPECT_FALSE(Allocator->canCache(1UL << 18)); - EXPECT_TRUE(Allocator->canCache(1UL << 16)); - EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 0U)); - EXPECT_FALSE(Allocator->canCache(1UL << 16)); - EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); - EXPECT_TRUE( - Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); - EXPECT_TRUE(Allocator->canCache(1UL << 16)); -} - -struct MapAllocatorWithReleaseTest : public MapAllocatorTest { +struct ScudoSecondaryAllocatorWithReleaseTest + : public ScudoSecondaryAllocatorTest { void SetUp() override { Allocator->init(nullptr, /*ReleaseToOsInterval=*/0); } void performAllocations() { @@ -249,11 +223,11 @@ struct MapAllocatorWithReleaseTest : public MapAllocatorTest { bool Ready = false; }; -TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) { +TEST_F(ScudoSecondaryAllocatorWithReleaseTest, ThreadsRace) { std::thread Threads[16]; for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++) - Threads[I] = - std::thread(&MapAllocatorWithReleaseTest::performAllocations, this); + Threads[I] = std::thread( + &ScudoSecondaryAllocatorWithReleaseTest::performAllocations, this); { std::unique_lock Lock(Mutex); Ready = true; @@ -266,7 +240,7 @@ TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) { Str.output(); } -struct MapAllocatorCacheTest : public Test { +struct ScudoSecondaryAllocatorCacheTest : public Test { static constexpr scudo::u32 UnmappedMarker = 0xDEADBEEF; static void testUnmapCallback(scudo::MemMapT &MemMap) { @@ -274,7 +248,7 @@ struct MapAllocatorCacheTest : public Test { *Ptr = UnmappedMarker; } - using SecondaryConfig = scudo::SecondaryConfig; + using SecondaryConfig = scudo::SecondaryConfig; using CacheConfig = SecondaryConfig::CacheConfig; using CacheT = scudo::MapAllocatorCache; @@ -315,7 +289,7 @@ struct MapAllocatorCacheTest : public Test { } }; -TEST_F(MapAllocatorCacheTest, CacheOrder) { +TEST_F(ScudoSecondaryAllocatorCacheTest, EntryOrder) { std::vector MemMaps; Cache->setOption(scudo::Option::MaxCacheEntriesCount, CacheConfig::getEntriesArraySize()); @@ -336,7 +310,7 @@ TEST_F(MapAllocatorCacheTest, CacheOrder) { MemMap.unmap(); } -TEST_F(MapAllocatorCacheTest, PartialChunkHeuristicRetrievalTest) { +TEST_F(ScudoSecondaryAllocatorCacheTest, PartialChunkHeuristicRetrievalTest) { const scudo::uptr FragmentedPages = 1 + scudo::CachedBlock::MaxReleasedCachePages; scudo::uptr EntryHeaderPos; @@ -360,7 +334,7 @@ TEST_F(MapAllocatorCacheTest, PartialChunkHeuristicRetrievalTest) { MemMap.unmap(); } -TEST_F(MapAllocatorCacheTest, MemoryLeakTest) { +TEST_F(ScudoSecondaryAllocatorCacheTest, MemoryLeakTest) { std::vector MemMaps; // Fill the cache above MaxEntriesCount to force an eviction // The first cache entry should be evicted (because it is the oldest) @@ -387,3 +361,24 @@ TEST_F(MapAllocatorCacheTest, MemoryLeakTest) { for (auto &MemMap : MemMaps) MemMap.unmap(); } + +TEST_F(ScudoSecondaryAllocatorCacheTest, Options) { + // Attempt to set a maximum number of entries higher than the array size. + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntriesCount, 4096U)); + + // Attempt to set an invalid (negative) number of entries + EXPECT_FALSE(Cache->setOption(scudo::Option::MaxCacheEntriesCount, -1)); + + // Various valid combinations. + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); + EXPECT_TRUE(Cache->canCache(1UL << 18)); + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17)); + EXPECT_FALSE(Cache->canCache(1UL << 18)); + EXPECT_TRUE(Cache->canCache(1UL << 16)); + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntriesCount, 0U)); + EXPECT_FALSE(Cache->canCache(1UL << 16)); + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); + EXPECT_TRUE(Cache->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); + EXPECT_TRUE(Cache->canCache(1UL << 16)); +} diff --git a/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions.cpp b/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions.cpp deleted file mode 100644 index 7e9c40c724aec..0000000000000 --- a/compiler-rt/test/orc/TestCases/Darwin/Generic/exceptions.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clangxx -c -o %t %s -// RUN: %llvm_jitlink -slab-allocate=20Mb %t -// -// REQUIRES: system-darwin && host-arch-compatible - -int main(int argc, char *argv[]) { - try { - throw 42; - } catch (int E) { - return 42 - E; - } - return 1; -} diff --git a/flang/runtime/exceptions.cpp b/flang/runtime/exceptions.cpp index b41c7cf438f56..257c71b51edb3 100644 --- a/flang/runtime/exceptions.cpp +++ b/flang/runtime/exceptions.cpp @@ -13,11 +13,14 @@ #include #if defined(__aarch64__) && !defined(_WIN32) #include -#elif defined(__x86_64__) +#elif defined(__x86_64__) && !defined(_WIN32) #include #endif -// fenv.h may not define exception macros. +// File fenv.h usually, but not always, defines standard exceptions as both +// enumerator values and preprocessor #defines. Some x86 environments also +// define a nonstandard __FE_DENORM enumerator, but without a corresponding +// #define, which makes it more difficult to determine if it is present or not. #ifndef FE_INVALID #define FE_INVALID 0 #endif @@ -33,6 +36,12 @@ #ifndef FE_INEXACT #define FE_INEXACT 0 #endif +#if FE_INVALID == 1 && FE_DIVBYZERO == 4 && FE_OVERFLOW == 8 && \ + FE_UNDERFLOW == 16 && FE_INEXACT == 32 +#define __FE_DENORM 2 +#else +#define __FE_DENORM 0 +#endif namespace Fortran::runtime { @@ -44,11 +53,7 @@ uint32_t RTNAME(MapException)(uint32_t excepts) { Terminator terminator{__FILE__, __LINE__}; static constexpr uint32_t v{FE_INVALID}; -#if __x86_64__ - static constexpr uint32_t s{__FE_DENORM}; // nonstandard, not a #define -#else - static constexpr uint32_t s{0}; -#endif + static constexpr uint32_t s{__FE_DENORM}; static constexpr uint32_t z{FE_DIVBYZERO}; static constexpr uint32_t o{FE_OVERFLOW}; static constexpr uint32_t u{FE_UNDERFLOW}; diff --git a/flang/test/Semantics/kinds04_q10.f90 b/flang/test/Semantics/kinds04_q10.f90 index aa5c4abe2f1df..3bec7a386585b 100644 --- a/flang/test/Semantics/kinds04_q10.f90 +++ b/flang/test/Semantics/kinds04_q10.f90 @@ -1,4 +1,5 @@ -! RUN: %python %S/test_errors.py %s %flang_fc1 +! RUN: not %flang_fc1 %s 2>%t.stderr +! RUN: FileCheck %s --input-file=%t.stderr --check-prefixes=PORTABILITY,ERROR,WARNING%if system-aix %{,AIX_WARNING%} ! C716 If both kind-param and exponent-letter appear, exponent-letter ! shall be E. (As an extension we also allow an exponent-letter which matches ! the kind-param) @@ -12,10 +13,12 @@ subroutine s(var) !PORTABILITY: Explicit kind parameter together with non-'E' exponent letter is not standard real :: realvar4 = 4.0D6_8 !WARNING: Explicit kind parameter on real constant disagrees with exponent letter 'q' + !AIX_WARNING: underflow on REAL(10) to REAL(4) conversion real :: realvar5 = 4.0Q6_10 !PORTABILITY: Explicit kind parameter together with non-'E' exponent letter is not standard real :: realvar6 = 4.0Q6_16 real :: realvar7 = 4.0E6_8 + !AIX_WARNING: underflow on REAL(10) to REAL(4) conversion real :: realvar8 = 4.0E6_10 real :: realvar9 = 4.0E6_16 !ERROR: Unsupported REAL(KIND=32) @@ -29,6 +32,7 @@ subroutine s(var) !PORTABILITY: Explicit kind parameter together with non-'E' exponent letter is not standard double precision :: doublevar5 = 4.0Q6_16 double precision :: doublevar6 = 4.0E6_8 + !AIX_WARNING: underflow on REAL(10) to REAL(8) conversion double precision :: doublevar7 = 4.0E6_10 double precision :: doublevar8 = 4.0E6_16 !ERROR: Unsupported REAL(KIND=32) diff --git a/libc/include/llvm-libc-macros/limits-macros.h b/libc/include/llvm-libc-macros/limits-macros.h index d4aa7ae539e8a..a4957225c9d34 100644 --- a/libc/include/llvm-libc-macros/limits-macros.h +++ b/libc/include/llvm-libc-macros/limits-macros.h @@ -235,4 +235,8 @@ #define _POSIX_PATH_MAX 256 #endif +#ifndef _POSIX_ARG_MAX +#define _POSIX_ARG_MAX 4096 +#endif + #endif // LLVM_LIBC_MACROS_LIMITS_MACROS_H diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index 8a400065376f4..82f1de6bad394 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -38,13 +38,12 @@ What's New in Libc++ 21.0.0? Implemented Papers ------------------ -- TODO - +- N4258: Cleaning-up noexcept in the Library (`Github `__) Improvements and New Features ----------------------------- -- The ``std::ranges::copy`` and ``std::ranges::copy_n`` algorithms have been optimized for ``std::vector::iterator``\s, +- The ``std::ranges::{copy, copy_n, copy_backward}`` algorithms have been optimized for ``std::vector::iterator``\s, resulting in a performance improvement of up to 2000x. diff --git a/libcxx/docs/Status/Cxx17Papers.csv b/libcxx/docs/Status/Cxx17Papers.csv index fbcac452adb8b..24fc7f718c360 100644 --- a/libcxx/docs/Status/Cxx17Papers.csv +++ b/libcxx/docs/Status/Cxx17Papers.csv @@ -3,7 +3,7 @@ "`N4089 `__","Safe conversions in ``unique_ptr``\ .","2014-11 (Urbana)","|Complete|","5","" "`N4169 `__","A proposal to add invoke function template","2014-11 (Urbana)","|Complete|","3.7","" "`N4190 `__","Removing auto_ptr, random_shuffle(), And Old Stuff.","2014-11 (Urbana)","|Complete|","15","" -"`N4258 `__","Cleaning-up noexcept in the Library.","2014-11 (Urbana)","|In Progress|","3.7","" +"`N4258 `__","Cleaning-up noexcept in the Library.","2014-11 (Urbana)","|Complete|","21","" "`N4259 `__","Wording for std::uncaught_exceptions","2014-11 (Urbana)","|Complete|","3.7","``std::uncaught_exception`` is deprecated since LLVM 20" "`N4277 `__","TriviallyCopyable ``reference_wrapper``\ .","2014-11 (Urbana)","|Complete|","3.2","" "`N4279 `__","Improved insertion interface for unique-key maps.","2014-11 (Urbana)","|Complete|","3.7","" diff --git a/libcxx/include/__algorithm/copy_backward.h b/libcxx/include/__algorithm/copy_backward.h index 48a768f577f55..02ffc14361e6a 100644 --- a/libcxx/include/__algorithm/copy_backward.h +++ b/libcxx/include/__algorithm/copy_backward.h @@ -10,11 +10,14 @@ #define _LIBCPP___ALGORITHM_COPY_BACKWARD_H #include <__algorithm/copy_move_common.h> +#include <__algorithm/copy_n.h> #include <__algorithm/iterator_operations.h> #include <__algorithm/min.h> #include <__config> +#include <__fwd/bit_reference.h> #include <__iterator/iterator_traits.h> #include <__iterator/segmented_iterator.h> +#include <__memory/pointer_traits.h> #include <__type_traits/common_type.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_constructible.h> @@ -34,6 +37,124 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_InIter, _OutIter> __copy_backward(_InIter __first, _Sent __last, _OutIter __result); +template +_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_backward_aligned( + __bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) { + using _In = __bit_iterator<_Cp, _IsConst>; + using difference_type = typename _In::difference_type; + using __storage_type = typename _In::__storage_type; + + const int __bits_per_word = _In::__bits_per_word; + difference_type __n = __last - __first; + if (__n > 0) { + // do first word + if (__last.__ctz_ != 0) { + difference_type __dn = std::min(static_cast(__last.__ctz_), __n); + __n -= __dn; + unsigned __clz = __bits_per_word - __last.__ctz_; + __storage_type __m = (~__storage_type(0) << (__last.__ctz_ - __dn)) & (~__storage_type(0) >> __clz); + __storage_type __b = *__last.__seg_ & __m; + *__result.__seg_ &= ~__m; + *__result.__seg_ |= __b; + __result.__ctz_ = static_cast(((-__dn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word); + // __last.__ctz_ = 0 + } + // __last.__ctz_ == 0 || __n == 0 + // __result.__ctz_ == 0 || __n == 0 + // do middle words + __storage_type __nw = __n / __bits_per_word; + __result.__seg_ -= __nw; + __last.__seg_ -= __nw; + std::copy_n(std::__to_address(__last.__seg_), __nw, std::__to_address(__result.__seg_)); + __n -= __nw * __bits_per_word; + // do last word + if (__n > 0) { + __storage_type __m = ~__storage_type(0) << (__bits_per_word - __n); + __storage_type __b = *--__last.__seg_ & __m; + *--__result.__seg_ &= ~__m; + *__result.__seg_ |= __b; + __result.__ctz_ = static_cast(-__n & (__bits_per_word - 1)); + } + } + return __result; +} + +template +_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_backward_unaligned( + __bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) { + using _In = __bit_iterator<_Cp, _IsConst>; + using difference_type = typename _In::difference_type; + using __storage_type = typename _In::__storage_type; + + const int __bits_per_word = _In::__bits_per_word; + difference_type __n = __last - __first; + if (__n > 0) { + // do first word + if (__last.__ctz_ != 0) { + difference_type __dn = std::min(static_cast(__last.__ctz_), __n); + __n -= __dn; + unsigned __clz_l = __bits_per_word - __last.__ctz_; + __storage_type __m = (~__storage_type(0) << (__last.__ctz_ - __dn)) & (~__storage_type(0) >> __clz_l); + __storage_type __b = *__last.__seg_ & __m; + unsigned __clz_r = __bits_per_word - __result.__ctz_; + __storage_type __ddn = std::min(__dn, static_cast(__result.__ctz_)); + if (__ddn > 0) { + __m = (~__storage_type(0) << (__result.__ctz_ - __ddn)) & (~__storage_type(0) >> __clz_r); + *__result.__seg_ &= ~__m; + if (__result.__ctz_ > __last.__ctz_) + *__result.__seg_ |= __b << (__result.__ctz_ - __last.__ctz_); + else + *__result.__seg_ |= __b >> (__last.__ctz_ - __result.__ctz_); + __result.__ctz_ = static_cast(((-__ddn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word); + __dn -= __ddn; + } + if (__dn > 0) { + // __result.__ctz_ == 0 + --__result.__seg_; + __result.__ctz_ = static_cast(-__dn & (__bits_per_word - 1)); + __m = ~__storage_type(0) << __result.__ctz_; + *__result.__seg_ &= ~__m; + __last.__ctz_ -= __dn + __ddn; + *__result.__seg_ |= __b << (__result.__ctz_ - __last.__ctz_); + } + // __last.__ctz_ = 0 + } + // __last.__ctz_ == 0 || __n == 0 + // __result.__ctz_ != 0 || __n == 0 + // do middle words + unsigned __clz_r = __bits_per_word - __result.__ctz_; + __storage_type __m = ~__storage_type(0) >> __clz_r; + for (; __n >= __bits_per_word; __n -= __bits_per_word) { + __storage_type __b = *--__last.__seg_; + *__result.__seg_ &= ~__m; + *__result.__seg_ |= __b >> __clz_r; + *--__result.__seg_ &= __m; + *__result.__seg_ |= __b << __result.__ctz_; + } + // do last word + if (__n > 0) { + __m = ~__storage_type(0) << (__bits_per_word - __n); + __storage_type __b = *--__last.__seg_ & __m; + __clz_r = __bits_per_word - __result.__ctz_; + __storage_type __dn = std::min(__n, static_cast(__result.__ctz_)); + __m = (~__storage_type(0) << (__result.__ctz_ - __dn)) & (~__storage_type(0) >> __clz_r); + *__result.__seg_ &= ~__m; + *__result.__seg_ |= __b >> (__bits_per_word - __result.__ctz_); + __result.__ctz_ = static_cast(((-__dn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word); + __n -= __dn; + if (__n > 0) { + // __result.__ctz_ == 0 + --__result.__seg_; + __result.__ctz_ = static_cast(-__n & (__bits_per_word - 1)); + __m = ~__storage_type(0) << __result.__ctz_; + *__result.__seg_ &= ~__m; + *__result.__seg_ |= __b << (__result.__ctz_ - (__bits_per_word - __n - __dn)); + } + } + } + return __result; +} + template struct __copy_backward_impl { template @@ -107,6 +228,16 @@ struct __copy_backward_impl { } } + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<__bit_iterator<_Cp, _IsConst>, __bit_iterator<_Cp, false> > + operator()(__bit_iterator<_Cp, _IsConst> __first, + __bit_iterator<_Cp, _IsConst> __last, + __bit_iterator<_Cp, false> __result) { + if (__last.__ctz_ == __result.__ctz_) + return std::make_pair(__last, std::__copy_backward_aligned(__first, __last, __result)); + return std::make_pair(__last, std::__copy_backward_unaligned(__first, __last, __result)); + } + // At this point, the iterators have been unwrapped so any `contiguous_iterator` has been unwrapped to a pointer. template ::value, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_In*, _Out*> diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference index 46d2b2f7ed948..bb8d4725c3980 100644 --- a/libcxx/include/__bit_reference +++ b/libcxx/include/__bit_reference @@ -11,6 +11,7 @@ #define _LIBCPP___BIT_REFERENCE #include <__algorithm/copy.h> +#include <__algorithm/copy_backward.h> #include <__algorithm/copy_n.h> #include <__algorithm/min.h> #include <__bit/countr.h> @@ -185,134 +186,6 @@ private: __mask_(__m) {} }; -// copy_backward - -template -_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_backward_aligned( - __bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) { - using _In = __bit_iterator<_Cp, _IsConst>; - using difference_type = typename _In::difference_type; - using __storage_type = typename _In::__storage_type; - - const int __bits_per_word = _In::__bits_per_word; - difference_type __n = __last - __first; - if (__n > 0) { - // do first word - if (__last.__ctz_ != 0) { - difference_type __dn = std::min(static_cast(__last.__ctz_), __n); - __n -= __dn; - unsigned __clz = __bits_per_word - __last.__ctz_; - __storage_type __m = (~__storage_type(0) << (__last.__ctz_ - __dn)) & (~__storage_type(0) >> __clz); - __storage_type __b = *__last.__seg_ & __m; - *__result.__seg_ &= ~__m; - *__result.__seg_ |= __b; - __result.__ctz_ = static_cast(((-__dn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word); - // __last.__ctz_ = 0 - } - // __last.__ctz_ == 0 || __n == 0 - // __result.__ctz_ == 0 || __n == 0 - // do middle words - __storage_type __nw = __n / __bits_per_word; - __result.__seg_ -= __nw; - __last.__seg_ -= __nw; - std::copy_n(std::__to_address(__last.__seg_), __nw, std::__to_address(__result.__seg_)); - __n -= __nw * __bits_per_word; - // do last word - if (__n > 0) { - __storage_type __m = ~__storage_type(0) << (__bits_per_word - __n); - __storage_type __b = *--__last.__seg_ & __m; - *--__result.__seg_ &= ~__m; - *__result.__seg_ |= __b; - __result.__ctz_ = static_cast(-__n & (__bits_per_word - 1)); - } - } - return __result; -} - -template -_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> __copy_backward_unaligned( - __bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) { - using _In = __bit_iterator<_Cp, _IsConst>; - using difference_type = typename _In::difference_type; - using __storage_type = typename _In::__storage_type; - - const int __bits_per_word = _In::__bits_per_word; - difference_type __n = __last - __first; - if (__n > 0) { - // do first word - if (__last.__ctz_ != 0) { - difference_type __dn = std::min(static_cast(__last.__ctz_), __n); - __n -= __dn; - unsigned __clz_l = __bits_per_word - __last.__ctz_; - __storage_type __m = (~__storage_type(0) << (__last.__ctz_ - __dn)) & (~__storage_type(0) >> __clz_l); - __storage_type __b = *__last.__seg_ & __m; - unsigned __clz_r = __bits_per_word - __result.__ctz_; - __storage_type __ddn = std::min(__dn, static_cast(__result.__ctz_)); - if (__ddn > 0) { - __m = (~__storage_type(0) << (__result.__ctz_ - __ddn)) & (~__storage_type(0) >> __clz_r); - *__result.__seg_ &= ~__m; - if (__result.__ctz_ > __last.__ctz_) - *__result.__seg_ |= __b << (__result.__ctz_ - __last.__ctz_); - else - *__result.__seg_ |= __b >> (__last.__ctz_ - __result.__ctz_); - __result.__ctz_ = static_cast(((-__ddn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word); - __dn -= __ddn; - } - if (__dn > 0) { - // __result.__ctz_ == 0 - --__result.__seg_; - __result.__ctz_ = static_cast(-__dn & (__bits_per_word - 1)); - __m = ~__storage_type(0) << __result.__ctz_; - *__result.__seg_ &= ~__m; - __last.__ctz_ -= __dn + __ddn; - *__result.__seg_ |= __b << (__result.__ctz_ - __last.__ctz_); - } - // __last.__ctz_ = 0 - } - // __last.__ctz_ == 0 || __n == 0 - // __result.__ctz_ != 0 || __n == 0 - // do middle words - unsigned __clz_r = __bits_per_word - __result.__ctz_; - __storage_type __m = ~__storage_type(0) >> __clz_r; - for (; __n >= __bits_per_word; __n -= __bits_per_word) { - __storage_type __b = *--__last.__seg_; - *__result.__seg_ &= ~__m; - *__result.__seg_ |= __b >> __clz_r; - *--__result.__seg_ &= __m; - *__result.__seg_ |= __b << __result.__ctz_; - } - // do last word - if (__n > 0) { - __m = ~__storage_type(0) << (__bits_per_word - __n); - __storage_type __b = *--__last.__seg_ & __m; - __clz_r = __bits_per_word - __result.__ctz_; - __storage_type __dn = std::min(__n, static_cast(__result.__ctz_)); - __m = (~__storage_type(0) << (__result.__ctz_ - __dn)) & (~__storage_type(0) >> __clz_r); - *__result.__seg_ &= ~__m; - *__result.__seg_ |= __b >> (__bits_per_word - __result.__ctz_); - __result.__ctz_ = static_cast(((-__dn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word); - __n -= __dn; - if (__n > 0) { - // __result.__ctz_ == 0 - --__result.__seg_; - __result.__ctz_ = static_cast(-__n & (__bits_per_word - 1)); - __m = ~__storage_type(0) << __result.__ctz_; - *__result.__seg_ &= ~__m; - *__result.__seg_ |= __b << (__result.__ctz_ - (__bits_per_word - __n - __dn)); - } - } - } - return __result; -} - -template -inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __bit_iterator<_Cp, false> copy_backward( - __bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) { - if (__last.__ctz_ == __result.__ctz_) - return std::__copy_backward_aligned(__first, __last, __result); - return std::__copy_backward_unaligned(__first, __last, __result); -} - // move template @@ -876,9 +749,8 @@ private: template _LIBCPP_CONSTEXPR_SINCE_CXX20 friend __bit_iterator<_Dp, false> __copy_backward_unaligned( __bit_iterator<_Dp, _IC> __first, __bit_iterator<_Dp, _IC> __last, __bit_iterator<_Dp, false> __result); - template - _LIBCPP_CONSTEXPR_SINCE_CXX20 friend __bit_iterator<_Dp, false> - copy_backward(__bit_iterator<_Dp, _IC> __first, __bit_iterator<_Dp, _IC> __last, __bit_iterator<_Dp, false> __result); + template + friend struct __copy_backward_impl; template friend __bit_iterator<_Cr, false> __swap_ranges_aligned(__bit_iterator<_Cl, false>, __bit_iterator<_Cl, false>, __bit_iterator<_Cr, false>); diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table index 9a82ec51daee7..d7b312f8774fc 100644 --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -770,9 +770,10 @@ public: _LIBCPP_HIDE_FROM_ABI __hash_table& operator=(const __hash_table& __u); _LIBCPP_HIDE_FROM_ABI __hash_table& operator=(__hash_table&& __u) - _NOEXCEPT_(__node_traits::propagate_on_container_move_assignment::value&& - is_nothrow_move_assignable<__node_allocator>::value&& is_nothrow_move_assignable::value&& - is_nothrow_move_assignable::value); + _NOEXCEPT_(is_nothrow_move_assignable::value&& is_nothrow_move_assignable::value && + ((__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits<__node_allocator>::is_always_equal::value)); template _LIBCPP_HIDE_FROM_ABI void __assign_unique(_InputIterator __first, _InputIterator __last); template @@ -1238,10 +1239,11 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u, } template -inline __hash_table<_Tp, _Hash, _Equal, _Alloc>& -__hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(__hash_table&& __u) _NOEXCEPT_( - __node_traits::propagate_on_container_move_assignment::value&& is_nothrow_move_assignable<__node_allocator>::value&& - is_nothrow_move_assignable::value&& is_nothrow_move_assignable::value) { +inline __hash_table<_Tp, _Hash, _Equal, _Alloc>& __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(__hash_table&& __u) + _NOEXCEPT_(is_nothrow_move_assignable::value&& is_nothrow_move_assignable::value && + ((__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits<__node_allocator>::is_always_equal::value)) { __move_assign(__u, integral_constant()); return *this; } diff --git a/libcxx/include/__tree b/libcxx/include/__tree index acad6c33f8782..c627641d5d86f 100644 --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -987,9 +987,12 @@ public: _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t) _NOEXCEPT_( is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible::value); _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t, const allocator_type& __a); - _LIBCPP_HIDE_FROM_ABI __tree& operator=(__tree&& __t) _NOEXCEPT_( - __node_traits::propagate_on_container_move_assignment::value&& is_nothrow_move_assignable::value&& - is_nothrow_move_assignable<__node_allocator>::value); + _LIBCPP_HIDE_FROM_ABI __tree& operator=(__tree&& __t) + _NOEXCEPT_(is_nothrow_move_assignable::value && + ((__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits<__node_allocator>::is_always_equal::value)); + _LIBCPP_HIDE_FROM_ABI ~__tree(); _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return iterator(__begin_node()); } @@ -1520,11 +1523,11 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) { } template -__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(__tree&& __t) _NOEXCEPT_( - __node_traits::propagate_on_container_move_assignment::value&& is_nothrow_move_assignable::value&& - is_nothrow_move_assignable<__node_allocator>::value) - -{ +__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(__tree&& __t) + _NOEXCEPT_(is_nothrow_move_assignable::value && + ((__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits<__node_allocator>::is_always_equal::value)) { __move_assign(__t, integral_constant()); return *this; } diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h index 8d9257eddfcd2..25e5c42f1dd6f 100644 --- a/libcxx/include/__vector/vector_bool.h +++ b/libcxx/include/__vector/vector_bool.h @@ -10,6 +10,7 @@ #define _LIBCPP___VECTOR_VECTOR_BOOL_H #include <__algorithm/copy.h> +#include <__algorithm/copy_backward.h> #include <__algorithm/fill_n.h> #include <__algorithm/iterator_operations.h> #include <__algorithm/max.h> diff --git a/libcxx/include/bitset b/libcxx/include/bitset index 2914dee3d5292..a20842985b3d5 100644 --- a/libcxx/include/bitset +++ b/libcxx/include/bitset @@ -130,6 +130,7 @@ template struct hash>; # include <__cxx03/bitset> #else # include <__algorithm/copy.h> +# include <__algorithm/copy_backward.h> # include <__algorithm/count.h> # include <__algorithm/fill.h> # include <__algorithm/fill_n.h> diff --git a/libcxx/include/deque b/libcxx/include/deque index df3094cff7f89..95200b4801d7f 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -59,9 +59,9 @@ public: deque& operator=(const deque& c); deque& operator=(deque&& c) - noexcept( - allocator_type::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + noexcept((__alloc_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable::value) || + allocator_traits::is_always_equal::value); deque& operator=(initializer_list il); template @@ -674,9 +674,10 @@ public: _LIBCPP_HIDE_FROM_ABI deque(deque&& __c) noexcept(is_nothrow_move_constructible::value); _LIBCPP_HIDE_FROM_ABI deque(deque&& __c, const __type_identity_t& __a); - _LIBCPP_HIDE_FROM_ABI deque& - operator=(deque&& __c) noexcept(__alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + _LIBCPP_HIDE_FROM_ABI deque& operator=(deque&& __c) noexcept( + (__alloc_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable::value) || + allocator_traits::is_always_equal::value); _LIBCPP_HIDE_FROM_ABI void assign(initializer_list __il) { assign(__il.begin(), __il.end()); } # endif // _LIBCPP_CXX03_LANG @@ -1379,8 +1380,9 @@ inline deque<_Tp, _Allocator>::deque(deque&& __c, const __type_identity_t inline deque<_Tp, _Allocator>& deque<_Tp, _Allocator>::operator=(deque&& __c) noexcept( - __alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value) { + (__alloc_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable::value) || + allocator_traits::is_always_equal::value) { __move_assign(__c, integral_constant()); return *this; } diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list index f3b9617ab2e04..4b6ca8ea8587c 100644 --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -58,9 +58,9 @@ public: forward_list& operator=(const forward_list& x); forward_list& operator=(forward_list&& x) - noexcept( - allocator_type::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + noexcept((__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable::value) || + allocator_traits::is_always_equal::value); forward_list& operator=(initializer_list il); template @@ -717,8 +717,9 @@ public: _LIBCPP_HIDE_FROM_ABI forward_list(initializer_list __il, const allocator_type& __a); _LIBCPP_HIDE_FROM_ABI forward_list& operator=(forward_list&& __x) noexcept( - __node_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + (__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable::value) || + allocator_traits::is_always_equal::value); _LIBCPP_HIDE_FROM_ABI forward_list& operator=(initializer_list __il); @@ -1009,8 +1010,10 @@ void forward_list<_Tp, _Alloc>::__move_assign(forward_list& __x, false_type) { } template -inline forward_list<_Tp, _Alloc>& forward_list<_Tp, _Alloc>::operator=(forward_list&& __x) _NOEXCEPT_( - __node_traits::propagate_on_container_move_assignment::value&& is_nothrow_move_assignable::value) { +inline forward_list<_Tp, _Alloc>& forward_list<_Tp, _Alloc>::operator=(forward_list&& __x) noexcept( + (__node_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable::value) || + allocator_traits::is_always_equal::value) { __move_assign(__x, integral_constant()); return *this; } diff --git a/libcxx/include/list b/libcxx/include/list index 5e2fd40d6ee9d..3fcf796ebc03d 100644 --- a/libcxx/include/list +++ b/libcxx/include/list @@ -60,9 +60,9 @@ public: list& operator=(const list& x); list& operator=(list&& x) - noexcept( - allocator_type::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + noexcept((__node_alloc_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits::is_always_equal::value); list& operator=(initializer_list); template void assign(Iter first, Iter last); @@ -728,9 +728,10 @@ public: _LIBCPP_HIDE_FROM_ABI list(list&& __c) _NOEXCEPT_(is_nothrow_move_constructible<__node_allocator>::value); _LIBCPP_HIDE_FROM_ABI list(list&& __c, const __type_identity_t& __a); - _LIBCPP_HIDE_FROM_ABI list& operator=(list&& __c) - _NOEXCEPT_(__node_alloc_traits::propagate_on_container_move_assignment::value&& - is_nothrow_move_assignable<__node_allocator>::value); + _LIBCPP_HIDE_FROM_ABI list& operator=(list&& __c) noexcept( + (__node_alloc_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits::is_always_equal::value); _LIBCPP_HIDE_FROM_ABI list& operator=(initializer_list __il) { assign(__il.begin(), __il.end()); @@ -1067,8 +1068,9 @@ inline list<_Tp, _Alloc>::list(list&& __c, const __type_identity_t inline list<_Tp, _Alloc>& list<_Tp, _Alloc>::operator=(list&& __c) noexcept( - __node_alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable<__node_allocator>::value) { + (__node_alloc_traits::propagate_on_container_move_assignment::value && + is_nothrow_move_assignable<__node_allocator>::value) || + allocator_traits::is_always_equal::value) { __move_assign(__c, integral_constant()); return *this; } diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h index 6d5c5167e91ed..081631a32b21d 100644 --- a/libcxx/test/benchmarks/GenerateInput.h +++ b/libcxx/test/benchmarks/GenerateInput.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -171,4 +172,31 @@ inline std::vector getRandomCStringInputs(std::size_t N) { return cinputs; } +template +struct Generate { + // When the contents don't matter + static T arbitrary(); + + // Prefer a cheap-to-construct element if possible + static T cheap(); + + // Prefer an expensive-to-construct element if possible + static T expensive(); +}; + +template + requires std::integral +struct Generate { + static T arbitrary() { return 42; } + static T cheap() { return 42; } + static T expensive() { return 42; } +}; + +template <> +struct Generate { + static std::string arbitrary() { return "hello world"; } + static std::string cheap() { return "small"; } + static std::string expensive() { return std::string(256, 'x'); } +}; + #endif // BENCHMARK_GENERATE_INPUT_H diff --git a/libcxx/test/benchmarks/Utilities.h b/libcxx/test/benchmarks/Utilities.h deleted file mode 100644 index fed16ba51f995..0000000000000 --- a/libcxx/test/benchmarks/Utilities.h +++ /dev/null @@ -1,37 +0,0 @@ -// -*- 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 BENCHMARK_UTILITIES_H -#define BENCHMARK_UTILITIES_H - -#include -#include - -#include "benchmark/benchmark.h" - -namespace UtilitiesInternal { -template -auto HaveDataImpl(int) -> decltype((std::declval().data(), std::true_type{})); -template -auto HaveDataImpl(long) -> std::false_type; -template -using HasData = decltype(HaveDataImpl(0)); -} // namespace UtilitiesInternal - -template ::value>* = nullptr> -void DoNotOptimizeData(Container& c) { - benchmark::DoNotOptimize(c.data()); -} - -template ::value>* = nullptr> -void DoNotOptimizeData(Container& c) { - benchmark::DoNotOptimize(&c); -} - -#endif // BENCHMARK_UTILITIES_H diff --git a/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp b/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp index 0777acbafb5cc..e0bd7e36f78ad 100644 --- a/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include diff --git a/libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp b/libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp new file mode 100644 index 0000000000000..c943d9a874b49 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include + +static void bm_ranges_copy_backward_vb(benchmark::State& state, bool aligned) { + auto n = state.range(); + std::vector in(n, true); + std::vector out(aligned ? n : n + 8); + benchmark::DoNotOptimize(&in); + auto dst = aligned ? out.end() : out.end() - 4; + for (auto _ : state) { + benchmark::DoNotOptimize(std::ranges::copy_backward(in, dst)); + benchmark::DoNotOptimize(&out); + } +} + +static void bm_copy_backward_vb(benchmark::State& state, bool aligned) { + auto n = state.range(); + std::vector in(n, true); + std::vector out(aligned ? n : n + 8); + benchmark::DoNotOptimize(&in); + auto beg = in.begin(); + auto end = in.end(); + auto dst = aligned ? out.end() : out.end() - 4; + for (auto _ : state) { + benchmark::DoNotOptimize(std::copy_backward(beg, end, dst)); + benchmark::DoNotOptimize(&out); + } +} + +static void bm_ranges_copy_backward_vb_aligned(benchmark::State& state) { bm_ranges_copy_backward_vb(state, true); } +static void bm_ranges_copy_backward_vb_unaligned(benchmark::State& state) { bm_ranges_copy_backward_vb(state, false); } + +static void bm_copy_backward_vb_aligned(benchmark::State& state) { bm_copy_backward_vb(state, true); } +static void bm_copy_backward_vb_unaligned(benchmark::State& state) { bm_copy_backward_vb(state, false); } + +// Test std::ranges::copy_backward for vector::iterator +BENCHMARK(bm_ranges_copy_backward_vb_aligned)->Range(8, 1 << 16)->DenseRange(102400, 204800, 4096); +BENCHMARK(bm_ranges_copy_backward_vb_unaligned)->Range(8, 1 << 20); + +// Test std::copy_backward for vector::iterator +BENCHMARK(bm_copy_backward_vb_aligned)->Range(8, 1 << 20); +BENCHMARK(bm_copy_backward_vb_unaligned)->Range(8, 1 << 20); + +BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/lower_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/lower_bound.bench.cpp index d9d57969df67a..31fb3597241fc 100644 --- a/libcxx/test/benchmarks/algorithms/lower_bound.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/lower_bound.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include diff --git a/libcxx/test/benchmarks/algorithms/make_heap.bench.cpp b/libcxx/test/benchmarks/algorithms/make_heap.bench.cpp index b7320e17c3e50..64d559620c512 100644 --- a/libcxx/test/benchmarks/algorithms/make_heap.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/make_heap.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/algorithms/make_heap_then_sort_heap.bench.cpp b/libcxx/test/benchmarks/algorithms/make_heap_then_sort_heap.bench.cpp index 5991d2846aee4..c6dc136be3ac4 100644 --- a/libcxx/test/benchmarks/algorithms/make_heap_then_sort_heap.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/make_heap_then_sort_heap.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/algorithms/pop_heap.bench.cpp b/libcxx/test/benchmarks/algorithms/pop_heap.bench.cpp index 5fef52284239d..e4b96a0ae48c7 100644 --- a/libcxx/test/benchmarks/algorithms/pop_heap.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/pop_heap.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp index 10254ac12cf56..a385185ec7fe5 100644 --- a/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-has-no-incomplete-pstl #include diff --git a/libcxx/test/benchmarks/algorithms/push_heap.bench.cpp b/libcxx/test/benchmarks/algorithms/push_heap.bench.cpp index 89d8122bd1dbe..7dfa0285348bd 100644 --- a/libcxx/test/benchmarks/algorithms/push_heap.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/push_heap.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/algorithms/set_intersection.bench.cpp b/libcxx/test/benchmarks/algorithms/set_intersection.bench.cpp index 9bde4bb29dc22..40292179781ee 100644 --- a/libcxx/test/benchmarks/algorithms/set_intersection.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/set_intersection.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include diff --git a/libcxx/test/benchmarks/algorithms/sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sort.bench.cpp index 899272e34795f..7f3ce6ff7a07e 100644 --- a/libcxx/test/benchmarks/algorithms/sort.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/sort.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/algorithms/sort_heap.bench.cpp b/libcxx/test/benchmarks/algorithms/sort_heap.bench.cpp index ee4b6bfc7387b..1ce9f1a6df9af 100644 --- a/libcxx/test/benchmarks/algorithms/sort_heap.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/sort_heap.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp index c68f73838c319..26e8de935f5c5 100644 --- a/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h b/libcxx/test/benchmarks/containers/ContainerBenchmarks.h deleted file mode 100644 index 5fc8981619672..0000000000000 --- a/libcxx/test/benchmarks/containers/ContainerBenchmarks.h +++ /dev/null @@ -1,332 +0,0 @@ -// -*- 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 BENCHMARK_CONTAINER_BENCHMARKS_H -#define BENCHMARK_CONTAINER_BENCHMARKS_H - -#include -#include -#include - -#include "benchmark/benchmark.h" -#include "../Utilities.h" -#include "test_iterators.h" - -namespace ContainerBenchmarks { - -template -void BM_ConstructSize(benchmark::State& st, Container) { - auto size = st.range(0); - for (auto _ : st) { - Container c(size); - DoNotOptimizeData(c); - } -} - -template -void BM_CopyConstruct(benchmark::State& st, Container) { - auto size = st.range(0); - Container c(size); - for (auto _ : st) { - auto v = c; - DoNotOptimizeData(v); - } -} - -template -void BM_Assignment(benchmark::State& st, Container) { - auto size = st.range(0); - Container c1; - Container c2(size); - for (auto _ : st) { - c1 = c2; - DoNotOptimizeData(c1); - DoNotOptimizeData(c2); - } -} - -template -void BM_AssignInputIterIter(benchmark::State& st, Container c, GenInputs gen) { - auto v = gen(1, sz...); - c.resize(st.range(0), v[0]); - auto in = gen(st.range(1), sz...); - benchmark::DoNotOptimize(&in); - benchmark::DoNotOptimize(&c); - for (auto _ : st) { - c.assign(cpp17_input_iterator(in.begin()), cpp17_input_iterator(in.end())); - benchmark::ClobberMemory(); - } -} - -template -void BM_ConstructSizeValue(benchmark::State& st, Container, typename Container::value_type const& val) { - const auto size = st.range(0); - for (auto _ : st) { - Container c(size, val); - DoNotOptimizeData(c); - } -} - -template -void BM_ConstructIterIter(benchmark::State& st, Container, GenInputs gen) { - auto in = gen(st.range(0)); - const auto begin = in.begin(); - const auto end = in.end(); - benchmark::DoNotOptimize(&in); - while (st.KeepRunning()) { - Container c(begin, end); - DoNotOptimizeData(c); - } -} - -template -void BM_ConstructFromRange(benchmark::State& st, Container, GenInputs gen) { - auto in = gen(st.range(0)); - benchmark::DoNotOptimize(&in); - while (st.KeepRunning()) { - Container c(std::from_range, in); - DoNotOptimizeData(c); - } -} - -template -void BM_Pushback_no_grow(benchmark::State& state, Container c) { - int count = state.range(0); - c.reserve(count); - while (state.KeepRunningBatch(count)) { - c.clear(); - for (int i = 0; i != count; ++i) { - c.push_back(i); - } - benchmark::DoNotOptimize(c.data()); - } -} - -template -void BM_InsertValue(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - const auto end = in.end(); - while (st.KeepRunning()) { - c.clear(); - for (auto it = in.begin(); it != end; ++it) { - benchmark::DoNotOptimize(&(*c.insert(*it).first)); - } - benchmark::ClobberMemory(); - } -} - -template -void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - const auto end = in.end(); - while (st.KeepRunning()) { - c.clear(); - c.rehash(16); - for (auto it = in.begin(); it != end; ++it) { - benchmark::DoNotOptimize(&(*c.insert(*it).first)); - } - benchmark::ClobberMemory(); - } -} - -template -void BM_Insert_InputIterIter_NoRealloc(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - DoNotOptimizeData(in); - const auto size = c.size(); - const auto beg = cpp17_input_iterator(in.begin()); - const auto end = cpp17_input_iterator(in.end()); - c.reserve(size + in.size()); // force no reallocation - for (auto _ : st) { - benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end))); - st.PauseTiming(); - c.erase(c.begin() + size, c.end()); // avoid the container to grow indefinitely - st.ResumeTiming(); - DoNotOptimizeData(c); - benchmark::ClobberMemory(); - } -} - -template -void BM_Insert_InputIterIter_Realloc_HalfFilled(benchmark::State& st, Container, GenInputs gen) { - const auto size = st.range(0); - Container a = gen(size); - Container in = gen(size + 10); - DoNotOptimizeData(a); - DoNotOptimizeData(in); - const auto beg = cpp17_input_iterator(in.begin()); - const auto end = cpp17_input_iterator(in.end()); - for (auto _ : st) { - st.PauseTiming(); - Container c; - c.reserve(size * 2); // Reallocation with half-filled container - c = a; - st.ResumeTiming(); - benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end))); - DoNotOptimizeData(c); - benchmark::ClobberMemory(); - } -} - -template -void BM_Insert_InputIterIter_Realloc_NearFull(benchmark::State& st, Container, GenInputs gen) { - const auto size = st.range(0); - Container a = gen(size); - Container in = gen(10); - DoNotOptimizeData(a); - DoNotOptimizeData(in); - const auto beg = cpp17_input_iterator(in.begin()); - const auto end = cpp17_input_iterator(in.end()); - for (auto _ : st) { - st.PauseTiming(); - Container c; - c.reserve(size + 5); // Reallocation almost-full container - c = a; - st.ResumeTiming(); - benchmark::DoNotOptimize(&(*c.insert(c.begin(), beg, end))); - DoNotOptimizeData(c); - benchmark::ClobberMemory(); - } -} - -template -void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - const auto end = in.end(); - c.insert(in.begin(), in.end()); - benchmark::DoNotOptimize(&c); - benchmark::DoNotOptimize(&in); - while (st.KeepRunning()) { - for (auto it = in.begin(); it != end; ++it) { - benchmark::DoNotOptimize(&(*c.insert(*it).first)); - } - benchmark::ClobberMemory(); - } -} - -template -void BM_EmplaceDuplicate(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - const auto end = in.end(); - c.insert(in.begin(), in.end()); - benchmark::DoNotOptimize(&c); - benchmark::DoNotOptimize(&in); - while (st.KeepRunning()) { - for (auto it = in.begin(); it != end; ++it) { - benchmark::DoNotOptimize(&(*c.emplace(*it).first)); - } - benchmark::ClobberMemory(); - } -} - -template -void BM_erase_iter_in_middle(benchmark::State& st, Container, GenInputs gen) { - auto in = gen(st.range(0)); - Container c(in.begin(), in.end()); - assert(c.size() > 2); - for (auto _ : st) { - auto mid = std::next(c.begin(), c.size() / 2); - auto tmp = *mid; - auto result = c.erase(mid); // erase an element in the middle - benchmark::DoNotOptimize(result); - c.push_back(std::move(tmp)); // and then push it back at the end to avoid needing a new container - } -} - -template -void BM_erase_iter_at_start(benchmark::State& st, Container, GenInputs gen) { - auto in = gen(st.range(0)); - Container c(in.begin(), in.end()); - assert(c.size() > 2); - for (auto _ : st) { - auto it = c.begin(); - auto tmp = *it; - auto result = c.erase(it); // erase the first element - benchmark::DoNotOptimize(result); - c.push_back(std::move(tmp)); // and then push it back at the end to avoid needing a new container - } -} - -template -void BM_Find(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - c.insert(in.begin(), in.end()); - benchmark::DoNotOptimize(&(*c.begin())); - const auto end = in.data() + in.size(); - while (st.KeepRunning()) { - for (auto it = in.data(); it != end; ++it) { - benchmark::DoNotOptimize(&(*c.find(*it))); - } - benchmark::ClobberMemory(); - } -} - -template -void BM_FindRehash(benchmark::State& st, Container c, GenInputs gen) { - c.rehash(8); - auto in = gen(st.range(0)); - c.insert(in.begin(), in.end()); - benchmark::DoNotOptimize(&(*c.begin())); - const auto end = in.data() + in.size(); - while (st.KeepRunning()) { - for (auto it = in.data(); it != end; ++it) { - benchmark::DoNotOptimize(&(*c.find(*it))); - } - benchmark::ClobberMemory(); - } -} - -template -void BM_Rehash(benchmark::State& st, Container c, GenInputs gen) { - auto in = gen(st.range(0)); - c.max_load_factor(3.0); - c.insert(in.begin(), in.end()); - benchmark::DoNotOptimize(c); - const auto bucket_count = c.bucket_count(); - while (st.KeepRunning()) { - c.rehash(bucket_count + 1); - c.rehash(bucket_count); - benchmark::ClobberMemory(); - } -} - -template -void BM_Compare_same_container(benchmark::State& st, Container, GenInputs gen) { - auto in = gen(st.range(0)); - Container c1(in.begin(), in.end()); - Container c2 = c1; - - benchmark::DoNotOptimize(&(*c1.begin())); - benchmark::DoNotOptimize(&(*c2.begin())); - while (st.KeepRunning()) { - bool res = c1 == c2; - benchmark::DoNotOptimize(&res); - benchmark::ClobberMemory(); - } -} - -template -void BM_Compare_different_containers(benchmark::State& st, Container, GenInputs gen) { - auto in1 = gen(st.range(0)); - auto in2 = gen(st.range(0)); - Container c1(in1.begin(), in1.end()); - Container c2(in2.begin(), in2.end()); - - benchmark::DoNotOptimize(&(*c1.begin())); - benchmark::DoNotOptimize(&(*c2.begin())); - while (st.KeepRunning()) { - bool res = c1 == c2; - benchmark::DoNotOptimize(&res); - benchmark::ClobberMemory(); - } -} - -} // namespace ContainerBenchmarks - -#endif // BENCHMARK_CONTAINER_BENCHMARKS_H diff --git a/libcxx/test/benchmarks/containers/container_benchmarks.h b/libcxx/test/benchmarks/containers/container_benchmarks.h new file mode 100644 index 0000000000000..e24bd767177e8 --- /dev/null +++ b/libcxx/test/benchmarks/containers/container_benchmarks.h @@ -0,0 +1,609 @@ +// -*- 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 TEST_BENCHMARKS_CONTAINERS_CONTAINER_BENCHMARKS_H +#define TEST_BENCHMARKS_CONTAINERS_CONTAINER_BENCHMARKS_H + +#include +#include +#include +#include // for std::from_range +#include +#include +#include + +#include "benchmark/benchmark.h" +#include "test_iterators.h" +#include "test_macros.h" +#include "../GenerateInput.h" + +namespace ContainerBenchmarks { + +template +void DoNotOptimizeData(Container& c) { + if constexpr (requires { c.data(); }) { + benchmark::DoNotOptimize(c.data()); + } else { + benchmark::DoNotOptimize(&c); + } +} + +// +// Sequence container operations +// +template +void BM_ctor_size(benchmark::State& st) { + auto size = st.range(0); + + for (auto _ : st) { + Container c(size); // we assume the destructor doesn't dominate the benchmark + DoNotOptimizeData(c); + } +} + +template +void BM_ctor_size_value(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const auto size = st.range(0); + ValueType value = gen(); + benchmark::DoNotOptimize(value); + + for (auto _ : st) { + Container c(size, value); // we assume the destructor doesn't dominate the benchmark + DoNotOptimizeData(c); + } +} + +template +void BM_ctor_iter_iter(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const auto size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + const auto begin = in.begin(); + const auto end = in.end(); + benchmark::DoNotOptimize(in); + + for (auto _ : st) { + Container c(begin, end); // we assume the destructor doesn't dominate the benchmark + DoNotOptimizeData(c); + } +} + +#if TEST_STD_VER >= 23 +template +void BM_ctor_from_range(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const auto size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + benchmark::DoNotOptimize(in); + + for (auto _ : st) { + Container c(std::from_range, in); // we assume the destructor doesn't dominate the benchmark + DoNotOptimizeData(c); + } +} +#endif + +template +void BM_ctor_copy(benchmark::State& st, Generator gen) { + auto size = st.range(0); + Container in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + for (auto _ : st) { + Container c(in); // we assume the destructor doesn't dominate the benchmark + DoNotOptimizeData(c); + DoNotOptimizeData(in); + } +} + +template +void BM_assignment(benchmark::State& st, Generator gen) { + auto size = st.range(0); + Container in1, in2; + std::generate_n(std::back_inserter(in1), size, gen); + std::generate_n(std::back_inserter(in2), size, gen); + DoNotOptimizeData(in1); + DoNotOptimizeData(in2); + + // Assign from one of two containers in succession to avoid + // hitting a self-assignment corner-case + Container c(in1); + bool toggle = false; + for (auto _ : st) { + c = toggle ? in1 : in2; + toggle = !toggle; + DoNotOptimizeData(c); + DoNotOptimizeData(in1); + DoNotOptimizeData(in2); + } +} + +// Benchmark Container::assign(input-iter, input-iter) when the container already contains +// the same number of elements that we're assigning. The intent is to check whether the +// implementation basically creates a new container from scratch or manages to reuse the +// pre-existing storage. +template +void BM_assign_input_iter_full(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + auto size = st.range(0); + std::vector in1, in2; + std::generate_n(std::back_inserter(in1), size, gen); + std::generate_n(std::back_inserter(in2), size, gen); + DoNotOptimizeData(in1); + DoNotOptimizeData(in2); + + Container c(in1.begin(), in1.end()); + bool toggle = false; + for (auto _ : st) { + std::vector& in = toggle ? in1 : in2; + auto first = in.data(); + auto last = in.data() + in.size(); + c.assign(cpp17_input_iterator(first), cpp17_input_iterator(last)); + toggle = !toggle; + DoNotOptimizeData(c); + } +} + +template +void BM_insert_begin(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + Container c(in.begin(), in.end()); + DoNotOptimizeData(c); + + ValueType value = gen(); + benchmark::DoNotOptimize(value); + + for (auto _ : st) { + c.insert(c.begin(), value); + DoNotOptimizeData(c); + + c.erase(std::prev(c.end())); // avoid growing indefinitely + } +} + +template + requires std::random_access_iterator +void BM_insert_middle(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + Container c(in.begin(), in.end()); + DoNotOptimizeData(c); + + ValueType value = gen(); + benchmark::DoNotOptimize(value); + + for (auto _ : st) { + auto mid = c.begin() + (size / 2); // requires random-access iterators in order to make sense + c.insert(mid, value); + DoNotOptimizeData(c); + + c.erase(c.end() - 1); // avoid growing indefinitely + } +} + +// Insert at the start of a vector in a scenario where the vector already +// has enough capacity to hold all the elements we are inserting. +template +void BM_insert_begin_input_iter_with_reserve_no_realloc(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + auto first = in.data(); + auto last = in.data() + in.size(); + + const int small = 100; // arbitrary + Container c; + c.reserve(size + small); // ensure no reallocation + std::generate_n(std::back_inserter(c), small, gen); + + for (auto _ : st) { + c.insert(c.begin(), cpp17_input_iterator(first), cpp17_input_iterator(last)); + DoNotOptimizeData(c); + + st.PauseTiming(); + c.erase(c.begin() + small, c.end()); // avoid growing indefinitely + st.ResumeTiming(); + } +} + +// Insert at the start of a vector in a scenario where the vector already +// has almost enough capacity to hold all the elements we are inserting, +// but does need to reallocate. +template +void BM_insert_begin_input_iter_with_reserve_almost_no_realloc(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + auto first = in.data(); + auto last = in.data() + in.size(); + + const int overflow = size / 10; // 10% of elements won't fit in the vector when we insert + Container c; + for (auto _ : st) { + st.PauseTiming(); + c = Container(); + c.reserve(size); + std::generate_n(std::back_inserter(c), overflow, gen); + st.ResumeTiming(); + + c.insert(c.begin(), cpp17_input_iterator(first), cpp17_input_iterator(last)); + DoNotOptimizeData(c); + } +} + +// Insert at the start of a vector in a scenario where the vector can fit a few +// more elements, but needs to reallocate almost immediately to fit the remaining +// elements. +template +void BM_insert_begin_input_iter_with_reserve_near_full(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + auto first = in.data(); + auto last = in.data() + in.size(); + + const int overflow = 9 * (size / 10); // 90% of elements won't fit in the vector when we insert + Container c; + for (auto _ : st) { + st.PauseTiming(); + c = Container(); + c.reserve(size); + std::generate_n(std::back_inserter(c), overflow, gen); + st.ResumeTiming(); + + c.insert(c.begin(), cpp17_input_iterator(first), cpp17_input_iterator(last)); + DoNotOptimizeData(c); + } +} + +template +void BM_erase_begin(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + Container c(in.begin(), in.end()); + DoNotOptimizeData(c); + + ValueType value = gen(); + benchmark::DoNotOptimize(value); + + for (auto _ : st) { + c.erase(c.begin()); + DoNotOptimizeData(c); + + c.insert(c.end(), value); // re-insert an element at the end to avoid needing a new container + } +} + +template + requires std::random_access_iterator +void BM_erase_middle(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + Container c(in.begin(), in.end()); + DoNotOptimizeData(c); + + ValueType value = gen(); + benchmark::DoNotOptimize(value); + + for (auto _ : st) { + auto mid = c.begin() + (size / 2); + c.erase(mid); + DoNotOptimizeData(c); + + c.insert(c.end(), value); // re-insert an element at the end to avoid needing a new container + } +} + +template +void BM_push_back(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + Container c; + DoNotOptimizeData(c); + while (st.KeepRunningBatch(size)) { + c.clear(); + for (int i = 0; i != size; ++i) { + c.push_back(in[i]); + } + DoNotOptimizeData(c); + } +} + +template +void BM_push_back_with_reserve(benchmark::State& st, Generator gen) { + using ValueType = typename Container::value_type; + const int size = st.range(0); + std::vector in; + std::generate_n(std::back_inserter(in), size, gen); + DoNotOptimizeData(in); + + Container c; + c.reserve(size); + DoNotOptimizeData(c); + while (st.KeepRunningBatch(size)) { + c.clear(); + for (int i = 0; i != size; ++i) { + c.push_back(in[i]); + } + DoNotOptimizeData(c); + } +} + +template +void sequence_container_benchmarks(std::string container) { + using ValueType = typename Container::value_type; + + using Generator = ValueType (*)(); + Generator cheap = [] { return Generate::cheap(); }; + Generator expensive = [] { return Generate::expensive(); }; + auto tostr = [&](Generator gen) { return gen == cheap ? " (cheap elements)" : " (expensive elements)"; }; + std::vector generators; + generators.push_back(cheap); + if constexpr (!std::is_integral_v) { + generators.push_back(expensive); + } + + // constructors + if constexpr (std::is_constructible_v) { + // not all containers provide this one + benchmark::RegisterBenchmark(container + "::ctor(size)", BM_ctor_size)->Arg(1024); + } + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::ctor(size, value_type)" + tostr(gen), [=](auto& st) { + BM_ctor_size_value(st, gen); + })->Arg(1024); + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::ctor(Iterator, Iterator)" + tostr(gen), [=](auto& st) { + BM_ctor_iter_iter(st, gen); + })->Arg(1024); +#if TEST_STD_VER >= 23 + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::ctor(Range)" + tostr(gen), [=](auto& st) { + BM_ctor_from_range(st, gen); + })->Arg(1024); +#endif + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::ctor(const&)" + tostr(gen), [=](auto& st) { + BM_ctor_copy(st, gen); + })->Arg(1024); + + // assignment + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::operator=(const&)" + tostr(gen), [=](auto& st) { + BM_assignment(st, gen); + })->Arg(1024); + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::assign(input-iter, input-iter) (full container)" + tostr(gen), + [=](auto& st) { BM_assign_input_iter_full(st, gen); }) + ->Arg(1024); + + // insert + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::insert(begin)" + tostr(gen), [=](auto& st) { + BM_insert_begin(st, gen); + })->Arg(1024); + if constexpr (std::random_access_iterator) { + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::insert(middle)" + tostr(gen), [=](auto& st) { + BM_insert_middle(st, gen); + })->Arg(1024); + } + if constexpr (requires(Container c) { c.reserve(0); }) { + for (auto gen : generators) + benchmark::RegisterBenchmark( + container + "::insert(begin, input-iter, input-iter) (no realloc)" + tostr(gen), + [=](auto& st) { BM_insert_begin_input_iter_with_reserve_no_realloc(st, gen); }) + ->Arg(1024); + for (auto gen : generators) + benchmark::RegisterBenchmark( + container + "::insert(begin, input-iter, input-iter) (half filled)" + tostr(gen), + [=](auto& st) { BM_insert_begin_input_iter_with_reserve_almost_no_realloc(st, gen); }) + ->Arg(1024); + for (auto gen : generators) + benchmark::RegisterBenchmark( + container + "::insert(begin, input-iter, input-iter) (near full)" + tostr(gen), + [=](auto& st) { BM_insert_begin_input_iter_with_reserve_near_full(st, gen); }) + ->Arg(1024); + } + + // erase + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::erase(begin)" + tostr(gen), [=](auto& st) { + BM_erase_begin(st, gen); + })->Arg(1024); + if constexpr (std::random_access_iterator) { + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::erase(middle)" + tostr(gen), [=](auto& st) { + BM_erase_middle(st, gen); + })->Arg(1024); + } + + // push_back (optional) + if constexpr (requires(Container c, ValueType v) { c.push_back(v); }) { + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::push_back()" + tostr(gen), [=](auto& st) { + BM_push_back(st, gen); + })->Arg(1024); + if constexpr (requires(Container c) { c.reserve(0); }) { + for (auto gen : generators) + benchmark::RegisterBenchmark(container + "::push_back() (with reserve)" + tostr(gen), [=](auto& st) { + BM_push_back_with_reserve(st, gen); + })->Arg(1024); + } + } +} + +// +// Misc operations +// +template +void BM_InsertValue(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + const auto end = in.end(); + while (st.KeepRunning()) { + c.clear(); + for (auto it = in.begin(); it != end; ++it) { + benchmark::DoNotOptimize(&(*c.insert(*it).first)); + } + benchmark::ClobberMemory(); + } +} + +template +void BM_InsertValueRehash(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + const auto end = in.end(); + while (st.KeepRunning()) { + c.clear(); + c.rehash(16); + for (auto it = in.begin(); it != end; ++it) { + benchmark::DoNotOptimize(&(*c.insert(*it).first)); + } + benchmark::ClobberMemory(); + } +} + +template +void BM_InsertDuplicate(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + const auto end = in.end(); + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(in); + while (st.KeepRunning()) { + for (auto it = in.begin(); it != end; ++it) { + benchmark::DoNotOptimize(&(*c.insert(*it).first)); + } + benchmark::ClobberMemory(); + } +} + +template +void BM_EmplaceDuplicate(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + const auto end = in.end(); + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(in); + while (st.KeepRunning()) { + for (auto it = in.begin(); it != end; ++it) { + benchmark::DoNotOptimize(&(*c.emplace(*it).first)); + } + benchmark::ClobberMemory(); + } +} + +template +void BM_Find(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(&(*c.begin())); + const auto end = in.data() + in.size(); + while (st.KeepRunning()) { + for (auto it = in.data(); it != end; ++it) { + benchmark::DoNotOptimize(&(*c.find(*it))); + } + benchmark::ClobberMemory(); + } +} + +template +void BM_FindRehash(benchmark::State& st, Container c, GenInputs gen) { + c.rehash(8); + auto in = gen(st.range(0)); + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(&(*c.begin())); + const auto end = in.data() + in.size(); + while (st.KeepRunning()) { + for (auto it = in.data(); it != end; ++it) { + benchmark::DoNotOptimize(&(*c.find(*it))); + } + benchmark::ClobberMemory(); + } +} + +template +void BM_Rehash(benchmark::State& st, Container c, GenInputs gen) { + auto in = gen(st.range(0)); + c.max_load_factor(3.0); + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(c); + const auto bucket_count = c.bucket_count(); + while (st.KeepRunning()) { + c.rehash(bucket_count + 1); + c.rehash(bucket_count); + benchmark::ClobberMemory(); + } +} + +template +void BM_Compare_same_container(benchmark::State& st, Container, GenInputs gen) { + auto in = gen(st.range(0)); + Container c1(in.begin(), in.end()); + Container c2 = c1; + + benchmark::DoNotOptimize(&(*c1.begin())); + benchmark::DoNotOptimize(&(*c2.begin())); + while (st.KeepRunning()) { + bool res = c1 == c2; + benchmark::DoNotOptimize(&res); + benchmark::ClobberMemory(); + } +} + +template +void BM_Compare_different_containers(benchmark::State& st, Container, GenInputs gen) { + auto in1 = gen(st.range(0)); + auto in2 = gen(st.range(0)); + Container c1(in1.begin(), in1.end()); + Container c2(in2.begin(), in2.end()); + + benchmark::DoNotOptimize(&(*c1.begin())); + benchmark::DoNotOptimize(&(*c2.begin())); + while (st.KeepRunning()) { + bool res = c1 == c2; + benchmark::DoNotOptimize(&res); + benchmark::ClobberMemory(); + } +} + +} // namespace ContainerBenchmarks + +#endif // TEST_BENCHMARKS_CONTAINERS_CONTAINER_BENCHMARKS_H diff --git a/libcxx/test/benchmarks/containers/deque.bench.cpp b/libcxx/test/benchmarks/containers/deque.bench.cpp index 7ff1093a9391c..6a650fa4dce2a 100644 --- a/libcxx/test/benchmarks/containers/deque.bench.cpp +++ b/libcxx/test/benchmarks/containers/deque.bench.cpp @@ -6,50 +6,20 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include +#include "container_benchmarks.h" #include "benchmark/benchmark.h" -#include "ContainerBenchmarks.h" -#include "../GenerateInput.h" +int main(int argc, char** argv) { + ContainerBenchmarks::sequence_container_benchmarks>("std::deque"); + ContainerBenchmarks::sequence_container_benchmarks>("std::deque"); -using namespace ContainerBenchmarks; - -constexpr std::size_t TestNumInputs = 1024; - -BENCHMARK_CAPTURE(BM_ConstructSize, deque_byte, std::deque{})->Arg(5140480); - -BENCHMARK_CAPTURE(BM_ConstructSizeValue, deque_byte, std::deque{}, 0)->Arg(5140480); - -BENCHMARK_CAPTURE(BM_ConstructIterIter, deque_char, std::deque{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructIterIter, deque_size_t, std::deque{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructIterIter, deque_string, std::deque{}, getRandomStringInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructFromRange, deque_char, std::deque{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructFromRange, deque_size_t, std::deque{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructFromRange, deque_string, std::deque{}, getRandomStringInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_erase_iter_in_middle, deque_int, std::deque{}, getRandomIntegerInputs) - ->Range(TestNumInputs, TestNumInputs * 10); -BENCHMARK_CAPTURE(BM_erase_iter_in_middle, deque_string, std::deque{}, getRandomStringInputs) - ->Range(TestNumInputs, TestNumInputs * 10); - -BENCHMARK_CAPTURE(BM_erase_iter_at_start, deque_int, std::deque{}, getRandomIntegerInputs) - ->Range(TestNumInputs, TestNumInputs * 10); -BENCHMARK_CAPTURE(BM_erase_iter_at_start, deque_string, std::deque{}, getRandomStringInputs) - ->Range(TestNumInputs, TestNumInputs * 10); - -BENCHMARK_MAIN(); + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/containers/list.bench.cpp b/libcxx/test/benchmarks/containers/list.bench.cpp new file mode 100644 index 0000000000000..2212affa02ba4 --- /dev/null +++ b/libcxx/test/benchmarks/containers/list.bench.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include + +#include "container_benchmarks.h" +#include "benchmark/benchmark.h" + +int main(int argc, char** argv) { + ContainerBenchmarks::sequence_container_benchmarks>("std::list"); + ContainerBenchmarks::sequence_container_benchmarks>("std::list"); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp index 0b62c87acf7a2..aeff6ad6f6333 100644 --- a/libcxx/test/benchmarks/containers/string.bench.cpp +++ b/libcxx/test/benchmarks/containers/string.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include diff --git a/libcxx/test/benchmarks/containers/unordered_set_operations.bench.cpp b/libcxx/test/benchmarks/containers/unordered_set.bench.cpp similarity index 99% rename from libcxx/test/benchmarks/containers/unordered_set_operations.bench.cpp rename to libcxx/test/benchmarks/containers/unordered_set.bench.cpp index a8448ef5a0cfb..ad8d0feaa0436 100644 --- a/libcxx/test/benchmarks/containers/unordered_set_operations.bench.cpp +++ b/libcxx/test/benchmarks/containers/unordered_set.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include @@ -17,7 +17,7 @@ #include "benchmark/benchmark.h" -#include "ContainerBenchmarks.h" +#include "container_benchmarks.h" #include "../GenerateInput.h" #include "test_macros.h" diff --git a/libcxx/test/benchmarks/containers/vector.bench.cpp b/libcxx/test/benchmarks/containers/vector.bench.cpp new file mode 100644 index 0000000000000..eef23d2981642 --- /dev/null +++ b/libcxx/test/benchmarks/containers/vector.bench.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include + +#include "container_benchmarks.h" +#include "benchmark/benchmark.h" + +int main(int argc, char** argv) { + ContainerBenchmarks::sequence_container_benchmarks>("std::vector"); + ContainerBenchmarks::sequence_container_benchmarks>("std::vector"); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/containers/vector_operations.bench.cpp b/libcxx/test/benchmarks/containers/vector_operations.bench.cpp deleted file mode 100644 index 1cd754ca7e780..0000000000000 --- a/libcxx/test/benchmarks/containers/vector_operations.bench.cpp +++ /dev/null @@ -1,108 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "benchmark/benchmark.h" -#include "ContainerBenchmarks.h" -#include "../GenerateInput.h" - -using namespace ContainerBenchmarks; - -constexpr std::size_t TestNumInputs = 1024; - -BENCHMARK_CAPTURE(BM_ConstructSize, vector_byte, std::vector{})->Arg(5140480); - -BENCHMARK_CAPTURE(BM_CopyConstruct, vector_int, std::vector{})->Arg(5140480); - -BENCHMARK_CAPTURE(BM_Assignment, vector_int, std::vector{})->Arg(5140480); - -BENCHMARK_CAPTURE(BM_ConstructSizeValue, vector_byte, std::vector{}, 0)->Arg(5140480); - -BENCHMARK_CAPTURE(BM_ConstructIterIter, vector_char, std::vector{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructIterIter, vector_size_t, std::vector{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructIterIter, vector_string, std::vector{}, getRandomStringInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructFromRange, vector_char, std::vector{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructFromRange, vector_size_t, std::vector{}, getRandomIntegerInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_ConstructFromRange, vector_string, std::vector{}, getRandomStringInputs) - ->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_Pushback_no_grow, vector_int, std::vector{})->Arg(TestNumInputs); - -BENCHMARK_CAPTURE(BM_erase_iter_in_middle, vector_int, std::vector{}, getRandomIntegerInputs) - ->Range(TestNumInputs, TestNumInputs * 10); -BENCHMARK_CAPTURE(BM_erase_iter_in_middle, vector_string, std::vector{}, getRandomStringInputs) - ->Range(TestNumInputs, TestNumInputs * 10); - -BENCHMARK_CAPTURE(BM_erase_iter_at_start, vector_int, std::vector{}, getRandomIntegerInputs) - ->Range(TestNumInputs, TestNumInputs * 10); -BENCHMARK_CAPTURE(BM_erase_iter_at_start, vector_string, std::vector{}, getRandomStringInputs) - ->Range(TestNumInputs, TestNumInputs * 10); - -template -void bm_grow(benchmark::State& state) { - for (auto _ : state) { - std::vector vec; - benchmark::DoNotOptimize(vec); - for (size_t i = 0; i != 2048; ++i) - vec.emplace_back(); - benchmark::DoNotOptimize(vec); - } -} -BENCHMARK(bm_grow); -BENCHMARK(bm_grow); -BENCHMARK(bm_grow>); -BENCHMARK(bm_grow>); - -BENCHMARK_CAPTURE(BM_AssignInputIterIter, vector_int, std::vector{}, getRandomIntegerInputs) - ->Args({TestNumInputs, TestNumInputs}); - -BENCHMARK_CAPTURE( - BM_AssignInputIterIter<32>, vector_string, std::vector{}, getRandomStringInputsWithLength) - ->Args({TestNumInputs, TestNumInputs}); - -BENCHMARK_CAPTURE(BM_AssignInputIterIter<100>, - vector_vector_int, - std::vector>{}, - getRandomIntegerInputsWithLength) - ->Args({TestNumInputs, TestNumInputs}); - -BENCHMARK_CAPTURE(BM_Insert_InputIterIter_NoRealloc, vector_int, std::vector(100, 1), getRandomIntegerInputs) - ->Arg(514048); -BENCHMARK_CAPTURE( - BM_Insert_InputIterIter_Realloc_HalfFilled, vector_int, std::vector{}, getRandomIntegerInputs) - ->Arg(514048); -BENCHMARK_CAPTURE(BM_Insert_InputIterIter_Realloc_NearFull, vector_int, std::vector{}, getRandomIntegerInputs) - ->Arg(514048); -BENCHMARK_CAPTURE( - BM_Insert_InputIterIter_Realloc_HalfFilled, vector_string, std::vector{}, getSSORandomStringInputs) - ->Arg(514048); -BENCHMARK_CAPTURE( - BM_Insert_InputIterIter_Realloc_NearFull, vector_string, std::vector{}, getSSORandomStringInputs) - ->Arg(514048); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/filesystem.bench.cpp b/libcxx/test/benchmarks/filesystem.bench.cpp index 83a87c86d3de0..dc6b0ac537f7e 100644 --- a/libcxx/test/benchmarks/filesystem.bench.cpp +++ b/libcxx/test/benchmarks/filesystem.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include diff --git a/libcxx/test/benchmarks/hash.bench.cpp b/libcxx/test/benchmarks/hash.bench.cpp index 1e1a0f36ec116..ca958765dc210 100644 --- a/libcxx/test/benchmarks/hash.bench.cpp +++ b/libcxx/test/benchmarks/hash.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include diff --git a/libcxx/test/benchmarks/variant_visit_1.bench.cpp b/libcxx/test/benchmarks/variant_visit_1.bench.cpp index 42b22aabaee04..f1b702530bed3 100644 --- a/libcxx/test/benchmarks/variant_visit_1.bench.cpp +++ b/libcxx/test/benchmarks/variant_visit_1.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include "benchmark/benchmark.h" diff --git a/libcxx/test/benchmarks/variant_visit_2.bench.cpp b/libcxx/test/benchmarks/variant_visit_2.bench.cpp index 328048cabc443..7dd8d02b358be 100644 --- a/libcxx/test/benchmarks/variant_visit_2.bench.cpp +++ b/libcxx/test/benchmarks/variant_visit_2.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include "benchmark/benchmark.h" diff --git a/libcxx/test/benchmarks/variant_visit_3.bench.cpp b/libcxx/test/benchmarks/variant_visit_3.bench.cpp index 40f8c1b5fa262..0fe42b0d8e009 100644 --- a/libcxx/test/benchmarks/variant_visit_3.bench.cpp +++ b/libcxx/test/benchmarks/variant_visit_3.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include "benchmark/benchmark.h" diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_backward.pass.cpp index 928903de1ade2..445c7718e1111 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_backward.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_backward.pass.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -36,47 +37,63 @@ class Derived : public PaddedBase { }; template -TEST_CONSTEXPR_CXX20 void -test_copy_backward() -{ +TEST_CONSTEXPR_CXX20 void test_copy_backward() { { const unsigned N = 1000; - int ia[N] = {}; + int ia[N] = {}; for (unsigned i = 0; i < N; ++i) - ia[i] = i; + ia[i] = i; int ib[N] = {0}; - OutIter r = std::copy_backward(InIter(ia), InIter(ia+N), OutIter(ib+N)); + OutIter r = std::copy_backward(InIter(ia), InIter(ia + N), OutIter(ib + N)); assert(base(r) == ib); for (unsigned i = 0; i < N; ++i) - assert(ia[i] == ib[i]); + assert(ia[i] == ib[i]); } } -TEST_CONSTEXPR_CXX20 bool -test() -{ - test_copy_backward, bidirectional_iterator >(); - test_copy_backward, random_access_iterator >(); - test_copy_backward, int*>(); +TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) { + std::vector in(N, false); + for (std::size_t i = 0; i < N; i += 2) + in[i] = true; - test_copy_backward, bidirectional_iterator >(); - test_copy_backward, random_access_iterator >(); - test_copy_backward, int*>(); + { // Test copy_backward with aligned bytes + std::vector out(N); + std::copy_backward(in.begin(), in.end(), out.end()); + assert(in == out); + } + { // Test copy_backward with unaligned bytes + std::vector out(N + 8); + std::copy_backward(in.begin(), in.end(), out.end() - 4); + for (std::size_t i = 0; i < N; ++i) + assert(out[i + 4] == in[i]); + } + + return true; +}; - test_copy_backward >(); - test_copy_backward >(); - test_copy_backward(); +TEST_CONSTEXPR_CXX20 bool test() { + test_copy_backward, bidirectional_iterator >(); + test_copy_backward, random_access_iterator >(); + test_copy_backward, int*>(); + + test_copy_backward, bidirectional_iterator >(); + test_copy_backward, random_access_iterator >(); + test_copy_backward, int*>(); + + test_copy_backward >(); + test_copy_backward >(); + test_copy_backward(); #if TEST_STD_VER > 17 - test_copy_backward, bidirectional_iterator>(); - test_copy_backward, random_access_iterator>(); - test_copy_backward, int*>(); - - test_copy_backward, contiguous_iterator>(); - test_copy_backward, contiguous_iterator>(); - test_copy_backward, contiguous_iterator>(); - test_copy_backward>(); + test_copy_backward, bidirectional_iterator>(); + test_copy_backward, random_access_iterator>(); + test_copy_backward, int*>(); + + test_copy_backward, contiguous_iterator>(); + test_copy_backward, contiguous_iterator>(); + test_copy_backward, contiguous_iterator>(); + test_copy_backward>(); #endif { // Make sure that padding bits aren't copied @@ -96,15 +113,24 @@ test() assert(std::equal(a, a + 10, expected)); } - return true; + { // Test vector::iterator optimization + assert(test_vector_bool(8)); + assert(test_vector_bool(19)); + assert(test_vector_bool(32)); + assert(test_vector_bool(49)); + assert(test_vector_bool(64)); + assert(test_vector_bool(199)); + assert(test_vector_bool(256)); + } + + return true; } -int main(int, char**) -{ - test(); +int main(int, char**) { + test(); #if TEST_STD_VER > 17 - static_assert(test()); + static_assert(test()); #endif return 0; diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp index 343447446ab2d..a7fa3db23e6ba 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp @@ -29,6 +29,7 @@ #include "almost_satisfies_types.h" #include "test_iterators.h" +#include "test_macros.h" template > concept HasCopyBackwardIt = requires(In in, Sent sent, Out out) { std::ranges::copy_backward(in, sent, out); }; @@ -61,16 +62,16 @@ template constexpr void test_iterators() { { // simple test { - std::array in {1, 2, 3, 4}; + std::array in{1, 2, 3, 4}; std::array out; std::same_as> auto ret = - std::ranges::copy_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size())); + std::ranges::copy_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size())); assert(in == out); assert(base(ret.in) == in.data() + in.size()); assert(base(ret.out) == out.data()); } { - std::array in {1, 2, 3, 4}; + std::array in{1, 2, 3, 4}; std::array out; auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); std::same_as> auto ret = @@ -94,7 +95,7 @@ constexpr void test_iterators() { std::array in; std::array out; auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size()))); - auto ret = std::ranges::copy_backward(range, Out(out.data())); + auto ret = std::ranges::copy_backward(range, Out(out.data())); assert(base(ret.in) == in.data() + in.size()); assert(base(ret.out) == out.data()); } @@ -104,16 +105,16 @@ constexpr void test_iterators() { template constexpr void test_containers() { { - InContainer in {1, 2, 3, 4}; + InContainer in{1, 2, 3, 4}; OutContainer out(4); std::same_as> auto ret = - std::ranges::copy_backward(In(in.begin()), Sent(In(in.end())), Out(out.end())); + std::ranges::copy_backward(In(in.begin()), Sent(In(in.end())), Out(out.end())); assert(std::ranges::equal(in, out)); assert(base(ret.in) == in.end()); assert(base(ret.out) == out.begin()); } { - InContainer in {1, 2, 3, 4}; + InContainer in{1, 2, 3, 4}; OutContainer out(4); auto range = std::ranges::subrange(In(in.begin()), Sent(In(in.end()))); std::same_as> auto ret = std::ranges::copy_backward(range, Out(out.end())); @@ -125,13 +126,12 @@ constexpr void test_containers() { template constexpr void test_join_view() { - auto to_subranges = std::views::transform([](auto& vec) { - return std::ranges::subrange(Iter(vec.begin()), Sent(Iter(vec.end()))); - }); + auto to_subranges = + std::views::transform([](auto& vec) { return std::ranges::subrange(Iter(vec.begin()), Sent(Iter(vec.end()))); }); { // segmented -> contiguous std::vector> vectors = {}; - auto range = vectors | to_subranges; + auto range = vectors | to_subranges; std::vector> subrange_vector(range.begin(), range.end()); std::array arr; @@ -140,7 +140,7 @@ constexpr void test_join_view() { } { // segmented -> contiguous std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; - auto range = vectors | to_subranges; + auto range = vectors | to_subranges; std::vector> subrange_vector(range.begin(), range.end()); std::array arr; @@ -149,7 +149,7 @@ constexpr void test_join_view() { } { // contiguous -> segmented std::vector> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}}; - auto range = vectors | to_subranges; + auto range = vectors | to_subranges; std::vector> subrange_vector(range.begin(), range.end()); std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; @@ -158,10 +158,10 @@ constexpr void test_join_view() { } { // segmented -> segmented std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; - auto range1 = vectors | to_subranges; + auto range1 = vectors | to_subranges; std::vector> subrange_vector(range1.begin(), range1.end()); std::vector> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}}; - auto range2 = to_vectors | to_subranges; + auto range2 = to_vectors | to_subranges; std::vector> to_subrange_vector(range2.begin(), range2.end()); std::ranges::copy_backward(subrange_vector | std::views::join, (to_subrange_vector | std::views::join).end()); @@ -224,6 +224,30 @@ constexpr void test_proxy_in_iterators() { test_sentinels(); } +#if TEST_STD_VER >= 23 + +constexpr bool test_vector_bool(std::size_t N) { + std::vector in(N, false); + for (std::size_t i = 0; i < N; i += 2) + in[i] = true; + + { // Test copy_backward with aligned bytes + std::vector out(N); + std::ranges::copy_backward(in, out.end()); + assert(in == out); + } + { // Test copy_backward with unaligned bytes + std::vector out(N + 8); + std::ranges::copy_backward(in, out.end() - 4); + for (std::size_t i = 0; i < N; ++i) + assert(out[i + 4] == in[i]); + } + + return true; +}; + +#endif + constexpr bool test() { test_in_iterators(); test_in_iterators(); @@ -237,13 +261,13 @@ constexpr bool test() { { // check that ranges::dangling is returned std::array out; std::same_as> auto ret = - std::ranges::copy_backward(std::array {1, 2, 3, 4}, out.data() + out.size()); + std::ranges::copy_backward(std::array{1, 2, 3, 4}, out.data() + out.size()); assert(ret.out == out.data()); assert((out == std::array{1, 2, 3, 4})); } { // check that an iterator is returned with a borrowing range - std::array in {1, 2, 3, 4}; + std::array in{1, 2, 3, 4}; std::array out; std::same_as::iterator, int*>> auto ret = std::ranges::copy_backward(std::views::all(in), out.data() + out.size()); @@ -254,8 +278,8 @@ constexpr bool test() { { // check that every element is copied exactly once struct CopyOnce { - bool copied = false; - constexpr CopyOnce() = default; + bool copied = false; + constexpr CopyOnce() = default; constexpr CopyOnce(const CopyOnce& other) = delete; constexpr CopyOnce& operator=(const CopyOnce& other) { assert(!other.copied); @@ -264,16 +288,16 @@ constexpr bool test() { } }; { - std::array in {}; - std::array out {}; + std::array in{}; + std::array out{}; auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end()); assert(ret.in == in.end()); assert(ret.out == out.begin()); assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; })); } { - std::array in {}; - std::array out {}; + std::array in{}; + std::array out{}; auto ret = std::ranges::copy_backward(in, out.end()); assert(ret.in == in.end()); assert(ret.out == out.begin()); @@ -284,8 +308,8 @@ constexpr bool test() { { // check that the range is copied backwards struct OnlyBackwardsCopyable { OnlyBackwardsCopyable* next = nullptr; - bool canCopy = false; - OnlyBackwardsCopyable() = default; + bool canCopy = false; + OnlyBackwardsCopyable() = default; constexpr OnlyBackwardsCopyable& operator=(const OnlyBackwardsCopyable&) { assert(canCopy); if (next != nullptr) @@ -294,12 +318,12 @@ constexpr bool test() { } }; { - std::array in {}; - std::array out {}; - out[1].next = &out[0]; - out[2].next = &out[1]; + std::array in{}; + std::array out{}; + out[1].next = &out[0]; + out[2].next = &out[1]; out[2].canCopy = true; - auto ret = std::ranges::copy_backward(in, out.end()); + auto ret = std::ranges::copy_backward(in, out.end()); assert(ret.in == in.end()); assert(ret.out == out.begin()); assert(out[0].canCopy); @@ -307,12 +331,12 @@ constexpr bool test() { assert(out[2].canCopy); } { - std::array in {}; - std::array out {}; - out[1].next = &out[0]; - out[2].next = &out[1]; + std::array in{}; + std::array out{}; + out[1].next = &out[0]; + out[2].next = &out[1]; out[2].canCopy = true; - auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end()); + auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end()); assert(ret.in == in.end()); assert(ret.out == out.begin()); assert(out[0].canCopy); @@ -321,6 +345,18 @@ constexpr bool test() { } } +#if TEST_STD_VER >= 23 + { // Test vector::iterator optimization + assert(test_vector_bool(8)); + assert(test_vector_bool(19)); + assert(test_vector_bool(32)); + assert(test_vector_bool(49)); + assert(test_vector_bool(64)); + assert(test_vector_bool(199)); + assert(test_vector_bool(256)); + } +#endif + return true; } diff --git a/libcxx/test/std/containers/associative/map/map.cons/move_assign_noexcept.compile.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/move_assign_noexcept.compile.pass.cpp new file mode 100644 index 0000000000000..a4c8ef1c5b429 --- /dev/null +++ b/libcxx/test/std/containers/associative/map/map.cons/move_assign_noexcept.compile.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// map& operator=(map&& c) +// noexcept( +// allocator_type::propagate_on_container_move_assignment::value && +// is_nothrow_move_assignable::value && +// is_nothrow_move_assignable::value); + +// This tests a conforming extension + +// UNSUPPORTED: c++03 + +#include + +#include "test_macros.h" +#include "MoveOnly.h" +#include "test_allocator.h" + +template +struct some_comp { + using value_type = T; + some_comp& operator=(const some_comp&); + bool operator()(const T&, const T&) const { return false; } +}; + +template +struct always_equal_alloc { + using value_type = T; + always_equal_alloc(const always_equal_alloc&); + void allocate(std::size_t); +}; + +template +struct not_always_equal_alloc { + int i; + using value_type = T; + not_always_equal_alloc(const not_always_equal_alloc&); + void allocate(std::size_t); +}; + +template