diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml index 6627aee6082b3..7cbf0a33e2c97 100644 --- a/.github/workflows/release-tasks.yml +++ b/.github/workflows/release-tasks.yml @@ -22,7 +22,7 @@ jobs: - name: Install Dependencies run: | - sudo apt-get update \ + sudo apt-get update sudo apt-get install -y \ doxygen \ graphviz \ diff --git a/bolt/CMakeLists.txt b/bolt/CMakeLists.txt index 5c85fef162e18..91b62cf4e2503 100644 --- a/bolt/CMakeLists.txt +++ b/bolt/CMakeLists.txt @@ -46,6 +46,10 @@ endif() if (BOLT_ENABLE_RUNTIME) message(STATUS "Building BOLT runtime libraries for X86") + set(extra_args "") + if(CMAKE_SYSROOT) + list(APPEND extra_args -DCMAKE_SYSROOT=${CMAKE_SYSROOT}) + endif() ExternalProject_Add(bolt_rt SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/runtime" STAMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-stamps @@ -54,8 +58,10 @@ if (BOLT_ENABLE_RUNTIME) -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} - -DCMAKE_INSTALL_PREFIX=${LLVM_BINARY_DIR} -DLLVM_LIBDIR_SUFFIX=${LLVM_LIBDIR_SUFFIX} + -DLLVM_LIBRARY_DIR=${LLVM_LIBRARY_DIR} + ${extra_args} + INSTALL_COMMAND "" BUILD_ALWAYS True ) install(CODE "execute_process\(COMMAND \${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=\${CMAKE_INSTALL_PREFIX} -P ${CMAKE_CURRENT_BINARY_DIR}/bolt_rt-bins/cmake_install.cmake \)" diff --git a/bolt/runtime/CMakeLists.txt b/bolt/runtime/CMakeLists.txt index 7d177f7c598b1..6aa8edb8424a3 100644 --- a/bolt/runtime/CMakeLists.txt +++ b/bolt/runtime/CMakeLists.txt @@ -15,12 +15,12 @@ add_library(bolt_rt_instr STATIC instr.cpp ${CMAKE_CURRENT_BINARY_DIR}/config.h ) -set_target_properties(bolt_rt_instr PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lib${LLVM_LIBDIR_SUFFIX}") +set_target_properties(bolt_rt_instr PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${LLVM_LIBRARY_DIR}") add_library(bolt_rt_hugify STATIC hugify.cpp ${CMAKE_CURRENT_BINARY_DIR}/config.h ) -set_target_properties(bolt_rt_hugify PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lib${LLVM_LIBDIR_SUFFIX}") +set_target_properties(bolt_rt_hugify PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${LLVM_LIBRARY_DIR}") set(BOLT_RT_FLAGS -ffreestanding @@ -44,7 +44,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*") instr.cpp ${CMAKE_CURRENT_BINARY_DIR}/config.h ) - set_target_properties(bolt_rt_instr_osx PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lib${LLVM_LIBDIR_SUFFIX}") + set_target_properties(bolt_rt_instr_osx PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${LLVM_LIBRARY_DIR}") target_include_directories(bolt_rt_instr_osx PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_compile_options(bolt_rt_instr_osx PRIVATE -target x86_64-apple-darwin19.6.0 diff --git a/clang-tools-extra/clangd/test/CMakeLists.txt b/clang-tools-extra/clangd/test/CMakeLists.txt index d073267066e0b..6bb578263ffc8 100644 --- a/clang-tools-extra/clangd/test/CMakeLists.txt +++ b/clang-tools-extra/clangd/test/CMakeLists.txt @@ -28,13 +28,6 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py ) -# Copy an empty compile_flags.txt to make sure tests don't pick up arbitrary -# commands from parents. -file( - TOUCH - ${CMAKE_CURRENT_BINARY_DIR}/compile_flags.txt -) - add_lit_testsuite(check-clangd "Running the Clangd regression tests" # clangd doesn't put unittest configs in test/unit like every other project. # Because of that, this needs to pass two folders here, while every other diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/trivially-destructible.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/trivially-destructible.cpp index 2ff3eda559a52..927a0905ee424 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/trivially-destructible.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/trivially-destructible.cpp @@ -1,7 +1,7 @@ // RUN: %check_clang_tidy %s performance-trivially-destructible %t // RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-tidy %t.cpp -checks='-*,performance-trivially-destructible' -fix -- -// RUN: clang-tidy %t.cpp -checks='-*,performance-trivially-destructible' -warnings-as-errors='-*,performance-trivially-destructible' -- +// RUN: clang-tidy %t.cpp -checks='-*,performance-trivially-destructible' -fix +// RUN: clang-tidy %t.cpp -checks='-*,performance-trivially-destructible' -warnings-as-errors='-*,performance-trivially-destructible' struct TriviallyDestructible1 { int a; diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake index 9694a0f598565..a4984f095a2ae 100644 --- a/clang/cmake/caches/Fuchsia-stage2.cmake +++ b/clang/cmake/caches/Fuchsia-stage2.cmake @@ -253,7 +253,6 @@ set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "") set(LLVM_TOOLCHAIN_TOOLS dsymutil llvm-ar - llvm-bolt llvm-cov llvm-cxxfilt llvm-debuginfod-find @@ -287,6 +286,7 @@ set(LLVM_TOOLCHAIN_TOOLS CACHE STRING "") set(LLVM_DISTRIBUTION_COMPONENTS + bolt clang lld LTO diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 105cfd9d74113..8d67ff9044693 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -718,6 +718,12 @@ Bug Fixes in This Version - Fix crash when attempting to perform parenthesized initialization of an aggregate with a base class with only non-public constructors. (`#62296 `_) +- Fix crash when handling nested immediate invocations in initializers of global + variables. + (`#58207 `_) +- Fix crash when passing a braced initializer list to a parentehsized aggregate + initialization expression. + (`#63008 `_). Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -824,6 +830,9 @@ Bug Fixes to C++ Support - Fix default member initializers sometimes being ignored when performing parenthesized aggregate initialization of templated types. (`#62266 `_) +- Fix overly aggressive lifetime checks for parenthesized aggregate + initialization. + (`#61567 `_) Concepts Specific Fixes: diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h index f02c140c14c19..746a5b2fbfc6c 100644 --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -64,6 +64,7 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference { const ConstraintSatisfaction *Satisfaction); ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction, bool Dependent, @@ -85,6 +86,13 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference { const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack); + static ConceptSpecializationExpr * + Create(const ASTContext &C, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ImplicitConceptSpecializationDecl *SpecDecl, + const ConstraintSatisfaction *Satisfaction, bool Dependent, + bool ContainsUnexpandedParameterPack); + ArrayRef getTemplateArguments() const { return SpecDecl->getTemplateArguments(); } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index bfe582d8252f0..6d72040c4b878 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2124,7 +2124,8 @@ def err_init_conversion_failed : Error< "exception object|a member subobject|an array element|a new value|a value|a " "base class|a constructor delegation|a vector element|a block element|a " "block element|a complex element|a lambda capture|a compound literal " - "initializer|a related result|a parameter of CF audited function}0 " + "initializer|a related result|a parameter of CF audited function|a " + "structured binding|a member subobject}0 " "%diff{of type $ with an %select{rvalue|lvalue}2 of type $|" "with an %select{rvalue|lvalue}2 of incompatible type}1,3" "%select{|: different classes%diff{ ($ vs $)|}5,6" diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index e5a98ba97f4f1..e1bbea0d118df 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -123,6 +123,10 @@ class alignas(8) InitializedEntity { /// decomposition declaration. EK_Binding, + /// The entity being initialized is a non-static data member subobject of an + /// object initialized via parenthesized aggregate initialization. + EK_ParenAggInitMember, + // Note: err_init_conversion_failed in DiagnosticSemaKinds.td uses this // enum as an index for its first %select. When modifying this list, // that diagnostic text needs to be updated as well. @@ -227,8 +231,10 @@ class alignas(8) InitializedEntity { /// Create the initialization entity for a member subobject. InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent, - bool Implicit, bool DefaultMemberInit) - : Kind(EK_Member), Parent(Parent), Type(Member->getType()), + bool Implicit, bool DefaultMemberInit, + bool IsParenAggInit = false) + : Kind(IsParenAggInit ? EK_ParenAggInitMember : EK_Member), + Parent(Parent), Type(Member->getType()), Variable{Member, Implicit, DefaultMemberInit} {} /// Create the initialization entity for an array element. @@ -388,6 +394,14 @@ class alignas(8) InitializedEntity { return InitializedEntity(Member->getAnonField(), Parent, Implicit, false); } + /// Create the initialization entity for a member subobject initialized via + /// parenthesized aggregate init. + static InitializedEntity InitializeMemberFromParenAggInit(FieldDecl *Member) { + return InitializedEntity(Member, /*Parent=*/nullptr, /*Implicit=*/false, + /*DefaultMemberInit=*/false, + /*IsParenAggInit=*/true); + } + /// Create the initialization entity for a default member initializer. static InitializedEntity InitializeMemberFromDefaultMemberInitializer(FieldDecl *Member) { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d40dacb5aa0f8..e57955f16bdd4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1394,6 +1394,9 @@ class Sema final { /// A stack of expression evaluation contexts. SmallVector ExprEvalContexts; + // Set of failed immediate invocations to avoid double diagnosing. + llvm::SmallPtrSet FailedImmediateInvocations; + /// Emit a warning for all pending noderef expressions that we recorded. void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2884fe6604228..8054eb2e12d3b 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -765,7 +765,8 @@ canonicalizeImmediatelyDeclaredConstraint(const ASTContext &C, Expr *IDC, CSE->getNamedConcept()->getLocation(), NewConverted); Expr *NewIDC = ConceptSpecializationExpr::Create( - C, CSE->getNamedConcept(), CSD, nullptr, CSE->isInstantiationDependent(), + C, CSE->getNamedConcept(), CSE->getTemplateArgsAsWritten(), CSD, + /*Satisfaction=*/nullptr, CSE->isInstantiationDependent(), CSE->containsUnexpandedParameterPack()); if (auto *OrigFold = dyn_cast(IDC)) diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp index fc8f1eb2abf14..cdc13c2d39694 100644 --- a/clang/lib/AST/ExprConcepts.cpp +++ b/clang/lib/AST/ExprConcepts.cpp @@ -58,6 +58,15 @@ ConceptSpecializationExpr::ConceptSpecializationExpr( ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty) : Expr(ConceptSpecializationExprClass, Empty) {} +ConceptSpecializationExpr *ConceptSpecializationExpr::Create( + const ASTContext &C, ConceptDecl *NamedConcept, + ImplicitConceptSpecializationDecl *SpecDecl, + const ConstraintSatisfaction *Satisfaction, bool Dependent, + bool ContainsUnexpandedParameterPack) { + return Create(C, NamedConcept, /*ArgsAsWritten*/ nullptr, SpecDecl, Satisfaction, + Dependent, ContainsUnexpandedParameterPack); +} + ConceptSpecializationExpr *ConceptSpecializationExpr::Create( const ASTContext &C, NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, @@ -72,13 +81,14 @@ ConceptSpecializationExpr *ConceptSpecializationExpr::Create( ConceptSpecializationExpr::ConceptSpecializationExpr( const ASTContext &C, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack) : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_PRValue, OK_Ordinary), ConceptReference(NestedNameSpecifierLoc(), SourceLocation(), DeclarationNameInfo(), NamedConcept, NamedConcept, - nullptr), + ArgsAsWritten), SpecDecl(SpecDecl), Satisfaction(Satisfaction ? ASTConstraintSatisfaction::Create(C, *Satisfaction) @@ -95,12 +105,13 @@ ConceptSpecializationExpr::ConceptSpecializationExpr( ConceptSpecializationExpr *ConceptSpecializationExpr::Create( const ASTContext &C, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, ImplicitConceptSpecializationDecl *SpecDecl, const ConstraintSatisfaction *Satisfaction, bool Dependent, bool ContainsUnexpandedParameterPack) { - return new (C) - ConceptSpecializationExpr(C, NamedConcept, SpecDecl, Satisfaction, - Dependent, ContainsUnexpandedParameterPack); + return new (C) ConceptSpecializationExpr(C, NamedConcept, ArgsAsWritten, + SpecDecl, Satisfaction, Dependent, + ContainsUnexpandedParameterPack); } const TypeConstraint * diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 238507e063355..77554aa2c462b 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7801,6 +7801,7 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType, } else { D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << GuardArgs; } + A->claim(); } } diff --git a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp index 44034e44adec7..3cc68673cd13c 100644 --- a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp +++ b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp @@ -113,7 +113,11 @@ IntegerLiteralSeparatorFixer::process(const Environment &Env, continue; } if (Style.isCpp()) { - if (const auto Pos = Text.find_first_of("_i"); Pos != StringRef::npos) { + // Hex alpha digits a-f/A-F must be at the end of the string literal. + StringRef Suffixes = "_himnsuyd"; + if (const auto Pos = + Text.find_first_of(IsBase16 ? Suffixes.drop_back() : Suffixes); + Pos != StringRef::npos) { Text = Text.substr(0, Pos); Length = Pos; } diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index cbda62497e6a6..4a39c2d065e60 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -1651,7 +1651,8 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, << Entity.getBaseSpecifier()->getType() << getSpecialMember(Constructor); break; - case InitializedEntity::EK_Member: { + case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: { const FieldDecl *Field = cast(Entity.getDecl()); PD = PDiag(diag::err_access_field_ctor); PD << Field->getType() << getSpecialMember(Constructor); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 051fad04219fd..0fbef1cc8b520 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8693,7 +8693,7 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { } // Check that SVE types are only used in functions with SVE available. - if (T->isSVESizelessBuiltinType() && CurContext->isFunctionOrMethod()) { + if (T->isSVESizelessBuiltinType() && isa(CurContext)) { const FunctionDecl *FD = cast(CurContext); llvm::StringMap CallerFeatureMap; Context.getFunctionFeatureMap(CallerFeatureMap, FD); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2842add2cc4af..9d865f487098d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17805,6 +17805,7 @@ static void EvaluateAndDiagnoseImmediateInvocation( bool Result = CE->EvaluateAsConstantExpr( Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); if (!Result || !Notes.empty()) { + SemaRef.FailedImmediateInvocations.insert(CE); Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); if (auto *FunctionalCast = dyn_cast(InnerExpr)) InnerExpr = FunctionalCast->getSubExpr(); @@ -17849,10 +17850,16 @@ static void RemoveNestedImmediateInvocation( [E](Sema::ImmediateInvocationCandidate Elem) { return Elem.getPointer() == E; }); - assert(It != IISet.rend() && - "ConstantExpr marked IsImmediateInvocation should " - "be present"); - It->setInt(1); // Mark as deleted + // It is possible that some subexpression of the current immediate + // invocation was handled from another expression evaluation context. Do + // not handle the current immediate invocation if some of its + // subexpressions failed before. + if (It == IISet.rend()) { + if (SemaRef.FailedImmediateInvocations.contains(E)) + CurrentII->setInt(1); + } else { + It->setInt(1); // Mark as deleted + } } ExprResult TransformConstantExpr(ConstantExpr *E) { if (!E->isImmediateInvocation()) @@ -17925,10 +17932,13 @@ HandleImmediateInvocations(Sema &SemaRef, SemaRef.RebuildingImmediateInvocation) return; - /// When we have more then 1 ImmediateInvocationCandidates we need to check - /// for nested ImmediateInvocationCandidates. when we have only 1 we only - /// need to remove ReferenceToConsteval in the immediate invocation. - if (Rec.ImmediateInvocationCandidates.size() > 1) { + /// When we have more than 1 ImmediateInvocationCandidates or previously + /// failed immediate invocations, we need to check for nested + /// ImmediateInvocationCandidates in order to avoid duplicate diagnostics. + /// Otherwise we only need to remove ReferenceToConsteval in the immediate + /// invocation. + if (Rec.ImmediateInvocationCandidates.size() > 1 || + !SemaRef.FailedImmediateInvocations.empty()) { /// Prevent sema calls during the tree transform from adding pointers that /// are already in the sets. diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 44adb167dcc02..cc8d1405ec55a 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -1152,6 +1152,7 @@ static void warnBracedScalarInit(Sema &S, const InitializedEntity &Entity, case InitializedEntity::EK_Parameter_CF_Audited: case InitializedEntity::EK_TemplateParameter: case InitializedEntity::EK_Result: + case InitializedEntity::EK_ParenAggInitMember: // Extra braces here are suspicious. DiagID = diag::warn_braces_around_init; break; @@ -3348,6 +3349,7 @@ DeclarationName InitializedEntity::getName() const { case EK_Variable: case EK_Member: + case EK_ParenAggInitMember: case EK_Binding: case EK_TemplateParameter: return Variable.VariableOrMember->getDeclName(); @@ -3379,6 +3381,7 @@ ValueDecl *InitializedEntity::getDecl() const { switch (getKind()) { case EK_Variable: case EK_Member: + case EK_ParenAggInitMember: case EK_Binding: case EK_TemplateParameter: return Variable.VariableOrMember; @@ -3420,6 +3423,7 @@ bool InitializedEntity::allowsNRVO() const { case EK_Parameter_CF_Audited: case EK_TemplateParameter: case EK_Member: + case EK_ParenAggInitMember: case EK_Binding: case EK_New: case EK_Temporary: @@ -3454,7 +3458,10 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { case EK_Result: OS << "Result"; break; case EK_StmtExprResult: OS << "StmtExprResult"; break; case EK_Exception: OS << "Exception"; break; - case EK_Member: OS << "Member"; break; + case EK_Member: + case EK_ParenAggInitMember: + OS << "Member"; + break; case EK_Binding: OS << "Binding"; break; case EK_New: OS << "New"; break; case EK_Temporary: OS << "Temporary"; break; @@ -5274,179 +5281,224 @@ static void TryOrBuildParenListInitialization( Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, ArrayRef Args, InitializationSequence &Sequence, bool VerifyOnly, ExprResult *Result = nullptr) { - unsigned ArgIndexToProcess = 0; + unsigned EntityIndexToProcess = 0; SmallVector InitExprs; QualType ResultType; Expr *ArrayFiller = nullptr; FieldDecl *InitializedFieldInUnion = nullptr; - // Process entities (i.e. array members, base classes, or class fields) by - // adding an initialization expression to InitExprs for each entity to - // initialize. - auto ProcessEntities = [&](auto Range) -> bool { - bool IsUnionType = Entity.getType()->isUnionType(); - for (InitializedEntity SubEntity : Range) { - // Unions should only have one initializer expression. - // If there are more initializers than it will be caught when we check - // whether Index equals Args.size(). - if (ArgIndexToProcess == 1 && IsUnionType) - return true; - - bool IsMember = SubEntity.getKind() == InitializedEntity::EK_Member; - - // Unnamed bitfields should not be initialized at all, either with an arg - // or by default. - if (IsMember && cast(SubEntity.getDecl())->isUnnamedBitfield()) - continue; - - if (ArgIndexToProcess < Args.size()) { - // There are still expressions in Args that haven't been processed. - // Let's match them to the current entity to initialize. - Expr *E = Args[ArgIndexToProcess++]; - - // Incomplete array types indicate flexible array members. Do not allow - // paren list initializations of structs with these members, as GCC - // doesn't either. - if (IsMember) { - auto *FD = cast(SubEntity.getDecl()); - if (FD->getType()->isIncompleteArrayType()) { - if (!VerifyOnly) { - S.Diag(E->getBeginLoc(), diag::err_flexible_array_init) - << SourceRange(E->getBeginLoc(), E->getEndLoc()); - S.Diag(FD->getLocation(), diag::note_flexible_array_member) << FD; - } - Sequence.SetFailed( - InitializationSequence::FK_ParenthesizedListInitFailed); - return false; - } - } - - InitializationKind SubKind = InitializationKind::CreateForInit( - E->getExprLoc(), /*isDirectInit=*/false, E); - InitializationSequence SubSeq(S, SubEntity, SubKind, E); - - if (SubSeq.Failed()) { - if (!VerifyOnly) - SubSeq.Diagnose(S, SubEntity, SubKind, E); - else - Sequence.SetFailed( - InitializationSequence::FK_ParenthesizedListInitFailed); + auto HandleInitializedEntity = [&](const InitializedEntity &SubEntity, + const InitializationKind &SubKind, + Expr *Arg, Expr **InitExpr = nullptr) { + InitializationSequence IS = [&]() { + if (Arg) + return InitializationSequence(S, SubEntity, SubKind, Arg); + return InitializationSequence(S, SubEntity, SubKind, std::nullopt); + }(); - return false; - } - if (!VerifyOnly) { - ExprResult ER = SubSeq.Perform(S, SubEntity, SubKind, E); - InitExprs.push_back(ER.get()); - if (IsMember && IsUnionType) - InitializedFieldInUnion = cast(SubEntity.getDecl()); - } + if (IS.Failed()) { + if (!VerifyOnly) { + if (Arg) + IS.Diagnose(S, SubEntity, SubKind, Arg); + else + IS.Diagnose(S, SubEntity, SubKind, std::nullopt); } else { - // We've processed all of the args, but there are still entities that - // have to be initialized. - if (IsMember) { - // C++ [dcl.init]p17.6.2.2 - // The remaining elements are initialized with their default member - // initializers, if any - auto *FD = cast(SubEntity.getDecl()); - if (FD->hasInClassInitializer()) { - if (!VerifyOnly) { - ExprResult DIE = S.BuildCXXDefaultInitExpr(FD->getLocation(), FD); - if (DIE.isInvalid()) - return false; - S.checkInitializerLifetime(SubEntity, DIE.get()); - InitExprs.push_back(DIE.get()); - } - continue; - } - } - // Remaining class elements without default member initializers and - // array elements are value initialized: - // - // C++ [dcl.init]p17.6.2.2 - // The remaining elements...otherwise are value initialzed - // - // C++ [dcl.init]p17.5 - // if the destination type is an array, the object is initialized as - // . follows. Let x1, . . . , xk be the elements of the expression-list - // ...Let n denote the array size...the ith array element is...value- - // initialized for each k < i <= n. - InitializationKind SubKind = InitializationKind::CreateValue( - Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); - InitializationSequence SubSeq(S, SubEntity, SubKind, std::nullopt); - if (SubSeq.Failed()) { - if (!VerifyOnly) - SubSeq.Diagnose(S, SubEntity, SubKind, std::nullopt); - return false; - } - if (!VerifyOnly) { - ExprResult ER = SubSeq.Perform(S, SubEntity, SubKind, std::nullopt); - if (SubEntity.getKind() == InitializedEntity::EK_ArrayElement) { - ArrayFiller = ER.get(); - return true; - } - InitExprs.push_back(ER.get()); - } + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); } + + return false; + } + if (!VerifyOnly) { + ExprResult ER; + if (Arg) + ER = IS.Perform(S, SubEntity, SubKind, Arg); + else + ER = IS.Perform(S, SubEntity, SubKind, std::nullopt); + if (InitExpr) + *InitExpr = ER.get(); + else + InitExprs.push_back(ER.get()); } return true; }; if (const ArrayType *AT = S.getASTContext().getAsArrayType(Entity.getType())) { - SmallVector ElementEntities; uint64_t ArrayLength; - // C++ [dcl.init]p17.5 + // C++ [dcl.init]p16.5 // if the destination type is an array, the object is initialized as // follows. Let x1, . . . , xk be the elements of the expression-list. If - // the destination type is an array of unknown bound, it is define as + // the destination type is an array of unknown bound, it is defined as // having k elements. if (const ConstantArrayType *CAT = - S.getASTContext().getAsConstantArrayType(Entity.getType())) + S.getASTContext().getAsConstantArrayType(Entity.getType())) { ArrayLength = CAT->getSize().getZExtValue(); - else + ResultType = Entity.getType(); + } else if (const VariableArrayType *VAT = + S.getASTContext().getAsVariableArrayType(Entity.getType())) { + // Braced-initialization of variable array types is not allowed, even if + // the size is greater than or equal to the number of args, so we don't + // allow them to be initialized via parenthesized aggregate initialization + // either. + const Expr *SE = VAT->getSizeExpr(); + S.Diag(SE->getBeginLoc(), diag::err_variable_object_no_init) + << SE->getSourceRange(); + return; + } else { + assert(isa(Entity.getType())); ArrayLength = Args.size(); + } + EntityIndexToProcess = ArrayLength; - if (ArrayLength >= Args.size()) { - for (uint64_t I = 0; I < ArrayLength; ++I) - ElementEntities.push_back( - InitializedEntity::InitializeElement(S.getASTContext(), I, Entity)); - - if (!ProcessEntities(ElementEntities)) + // ...the ith array element is copy-initialized with xi for each + // 1 <= i <= k + for (Expr *E : Args) { + InitializedEntity SubEntity = InitializedEntity::InitializeElement( + S.getASTContext(), EntityIndexToProcess, Entity); + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit=*/false, E); + if (!HandleInitializedEntity(SubEntity, SubKind, E)) + return; + } + // ...and value-initialized for each k < i <= n; + if (ArrayLength > Args.size()) { + InitializedEntity SubEntity = InitializedEntity::InitializeElement( + S.getASTContext(), Args.size(), Entity); + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr, &ArrayFiller)) return; + } + if (ResultType.isNull()) { ResultType = S.Context.getConstantArrayType( AT->getElementType(), llvm::APInt(/*numBits=*/32, ArrayLength), - nullptr, ArrayType::Normal, 0); + /*SizeExpr=*/nullptr, ArrayType::Normal, 0); } } else if (auto *RT = Entity.getType()->getAs()) { + bool IsUnion = RT->isUnionType(); const CXXRecordDecl *RD = cast(RT->getDecl()); - auto BaseRange = map_range(RD->bases(), [&](auto &base) { - return InitializedEntity::InitializeBase(S.getASTContext(), &base, false, - &Entity); - }); - auto FieldRange = map_range(RD->fields(), [](auto *field) { - return InitializedEntity::InitializeMember(field); - }); + if (!IsUnion) { + for (const CXXBaseSpecifier &Base : RD->bases()) { + InitializedEntity SubEntity = InitializedEntity::InitializeBase( + S.getASTContext(), &Base, false, &Entity); + if (EntityIndexToProcess < Args.size()) { + // C++ [dcl.init]p16.6.2.2. + // ...the object is initialized is follows. Let e1, ..., en be the + // elements of the aggregate([dcl.init.aggr]). Let x1, ..., xk be + // the elements of the expression-list...The element ei is + // copy-initialized with xi for 1 <= i <= k. + Expr *E = Args[EntityIndexToProcess]; + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit=*/false, E); + if (!HandleInitializedEntity(SubEntity, SubKind, E)) + return; + } else { + // We've processed all of the args, but there are still base classes + // that have to be initialized. + // C++ [dcl.init]p17.6.2.2 + // The remaining elements...otherwise are value initialzed + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), + /*IsImplicit=*/true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr)) + return; + } + EntityIndexToProcess++; + } + } - if (!ProcessEntities(BaseRange)) - return; + for (FieldDecl *FD : RD->fields()) { + // Unnamed bitfields should not be initialized at all, either with an arg + // or by default. + if (FD->isUnnamedBitfield()) + continue; - if (!ProcessEntities(FieldRange)) - return; + InitializedEntity SubEntity = + InitializedEntity::InitializeMemberFromParenAggInit(FD); + if (EntityIndexToProcess < Args.size()) { + // ...The element ei is copy-initialized with xi for 1 <= i <= k. + Expr *E = Args[EntityIndexToProcess]; + + // Incomplete array types indicate flexible array members. Do not allow + // paren list initializations of structs with these members, as GCC + // doesn't either. + if (FD->getType()->isIncompleteArrayType()) { + if (!VerifyOnly) { + S.Diag(E->getBeginLoc(), diag::err_flexible_array_init) + << SourceRange(E->getBeginLoc(), E->getEndLoc()); + S.Diag(FD->getLocation(), diag::note_flexible_array_member) << FD; + } + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + return; + } + + InitializationKind SubKind = InitializationKind::CreateForInit( + E->getExprLoc(), /*isDirectInit=*/false, E); + if (!HandleInitializedEntity(SubEntity, SubKind, E)) + return; + + // Unions should have only one initializer expression, so we bail out + // after processing the first field. If there are more initializers then + // it will be caught when we later check whether EntityIndexToProcess is + // less than Args.size(); + if (IsUnion) { + InitializedFieldInUnion = FD; + EntityIndexToProcess = 1; + break; + } + } else { + // We've processed all of the args, but there are still members that + // have to be initialized. + if (FD->hasInClassInitializer()) { + if (!VerifyOnly) { + // C++ [dcl.init]p16.6.2.2 + // The remaining elements are initialized with their default + // member initializers, if any + ExprResult DIE = S.BuildCXXDefaultInitExpr(FD->getLocation(), FD); + if (DIE.isInvalid()) + return; + S.checkInitializerLifetime(SubEntity, DIE.get()); + InitExprs.push_back(DIE.get()); + } + } else { + // C++ [dcl.init]p17.6.2.2 + // The remaining elements...otherwise are value initialzed + if (FD->getType()->isReferenceType()) { + Sequence.SetFailed( + InitializationSequence::FK_ParenthesizedListInitFailed); + if (!VerifyOnly) { + SourceRange SR = Kind.getParenOrBraceRange(); + S.Diag(SR.getEnd(), diag::err_init_reference_member_uninitialized) + << FD->getType() << SR; + S.Diag(FD->getLocation(), diag::note_uninit_reference_member); + } + return; + } + InitializationKind SubKind = InitializationKind::CreateValue( + Kind.getLocation(), Kind.getLocation(), Kind.getLocation(), true); + if (!HandleInitializedEntity(SubEntity, SubKind, nullptr)) + return; + } + } + EntityIndexToProcess++; + } ResultType = Entity.getType(); } // Not all of the args have been processed, so there must've been more args - // then were required to initialize the element. - if (ArgIndexToProcess < Args.size()) { + // than were required to initialize the element. + if (EntityIndexToProcess < Args.size()) { Sequence.SetFailed(InitializationSequence::FK_ParenthesizedListInitFailed); if (!VerifyOnly) { QualType T = Entity.getType(); int InitKind = T->isArrayType() ? 0 : T->isUnionType() ? 3 : 4; - SourceRange ExcessInitSR(Args[ArgIndexToProcess]->getBeginLoc(), + SourceRange ExcessInitSR(Args[EntityIndexToProcess]->getBeginLoc(), Args.back()->getEndLoc()); S.Diag(Kind.getLocation(), diag::err_excess_initializers) << InitKind << ExcessInitSR; @@ -6412,6 +6464,7 @@ getAssignmentAction(const InitializedEntity &Entity, bool Diagnose = false) { return Sema::AA_Converting; case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Binding: case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_VectorElement: @@ -6432,6 +6485,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) { switch (Entity.getKind()) { case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Result: case InitializedEntity::EK_StmtExprResult: case InitializedEntity::EK_New: @@ -6476,6 +6530,7 @@ static bool shouldDestroyEntity(const InitializedEntity &Entity) { return false; case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Binding: case InitializedEntity::EK_Variable: case InitializedEntity::EK_Parameter: @@ -6512,6 +6567,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity, case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Member: + case InitializedEntity::EK_ParenAggInitMember: case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Parameter_CF_Audited: case InitializedEntity::EK_TemplateParameter: @@ -7080,7 +7136,15 @@ static LifetimeResult getEntityLifetime( case InitializedEntity::EK_Exception: // FIXME: Can we diagnose lifetime problems with exceptions? return {nullptr, LK_FullExpression}; + + case InitializedEntity::EK_ParenAggInitMember: + // -- A temporary object bound to a reference element of an aggregate of + // class type initialized from a parenthesized expression-list + // [dcl.init, 9.3] persists until the completion of the full-expression + // containing the expression-list. + return {nullptr, LK_FullExpression}; } + llvm_unreachable("unknown entity kind"); } @@ -9196,7 +9260,9 @@ ExprResult InitializationSequence::Perform(Sema &S, S.checkInitializerLifetime(Entity, Init); // Diagnose non-fatal problems with the completed initialization. - if (Entity.getKind() == InitializedEntity::EK_Member && + if (InitializedEntity::EntityKind EK = Entity.getKind(); + (EK == InitializedEntity::EK_Member || + EK == InitializedEntity::EK_ParenAggInitMember) && cast(Entity.getDecl())->isBitField()) S.CheckBitFieldInitialization(Kind.getLocation(), cast(Entity.getDecl()), @@ -9650,7 +9716,8 @@ bool InitializationSequence::Diagnose(Sema &S, case OR_No_Viable_Function: if (Kind.getKind() == InitializationKind::IK_Default && (Entity.getKind() == InitializedEntity::EK_Base || - Entity.getKind() == InitializedEntity::EK_Member) && + Entity.getKind() == InitializedEntity::EK_Member || + Entity.getKind() == InitializedEntity::EK_ParenAggInitMember) && isa(S.CurContext)) { // This is implicit default initialization of a member or // base within a constructor. If no viable function was diff --git a/clang/test/CodeGen/paren-list-agg-init.cpp b/clang/test/CodeGen/paren-list-agg-init.cpp index 7e06a466b6c0a..0e68beb5c3706 100644 --- a/clang/test/CodeGen/paren-list-agg-init.cpp +++ b/clang/test/CodeGen/paren-list-agg-init.cpp @@ -99,6 +99,14 @@ namespace gh62266 { }; } +namespace gh61567 { + // CHECK-DAG: [[STRUCT_I:%.*I.*]] = type { i32, ptr } + struct I { + int a; + int&& r = 2; + }; +} + // CHECK-DAG: [[A1:@.*a1.*]] = internal constant [[STRUCT_A]] { i8 3, double 2.000000e+00 }, align 8 constexpr A a1(3.1, 2.0); // CHECK-DAG: [[A2:@.*a2.*]] = internal constant [[STRUCT_A]] { i8 99, double 0.000000e+00 }, align 8 @@ -444,3 +452,64 @@ namespace gh62266 { H<2> h(1); } } + +namespace gh61567 { + int foo20(); + + // CHECK: define {{.*}} void @{{.*foo21.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: [[REF_TMP:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: store i32 1, ptr [[REF_TMP]], align 4 + // CHECK-NEXT: store ptr [[REF_TMP]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo21() { + I(0, 1); + } + + // CHECK: define {{.*}} void @{{.*foo22.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: [[REF_TMP:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: [[CALL:%.*call*]] = call noundef i32 @{{.*foo20.*}} + // CHECK-NEXT: store i32 [[CALL]], ptr [[REF_TMP]], align 4 + // CHECK-NEXT: store ptr [[REF_TMP]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo22() { + I(0, foo20()); + } + + // CHECK: define {{.*}} void @{{.*foo23.*}}(i32 noundef [[I:%.*i.*]]) + // CHECK-NEXT: entry + // CHECK-NEXT: [[I_ADDR:%.*i.*]] = alloca i32, align 4 + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: store ptr [[I_ADDR]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo23(int i) { + I(0, static_cast(i)); + } + + // CHECK: define {{.*}} void @{{.*foo24.*}} { + // CHECK-NEXT: entry + // CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_I]], align 8 + // CHECK-NEXT: [[REF_TMP:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[A:%.*a.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 + // CHECK-NEXT: store i32 0, ptr [[A]], align 8 + // CHECK-NEXT: [[R:%.*r.*]] = getelementptr inbounds [[STRUCT_I]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 + // CHECK-NEXT: store i32 2, ptr [[REF_TMP]], align 4 + // CHECK-NEXT: store ptr [[REF_TMP]], ptr [[R]], align 8 + // CHECK-NEXT: ret void + void foo24() { + I(0); + } +} diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c index d96b887b7d484..326bc11627143 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -647,9 +647,12 @@ // RUN: %clang_cl /guard:ehcont -### -- %s 2>&1 | FileCheck -check-prefix=EHCONTGUARD %s // EHCONTGUARD: -ehcontguard -// RUN: %clang_cl /guard:cf /guard:ehcont -### -- %s 2>&1 | FileCheck -check-prefix=BOTHGUARD %s +// RUN: %clang_cl /guard:cf /guard:ehcont -Wall -Wno-msvc-not-found -### -- %s 2>&1 | \ +// RUN: FileCheck -check-prefix=BOTHGUARD %s --implicit-check-not=warning: // BOTHGUARD: -cfguard // BOTHGUARD-SAME: -ehcontguard +// BOTHGUARD: -guard:cf +// BOTHGUARD-SAME: -guard:ehcont // RUN: %clang_cl /guard:foo -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDINVALID %s // CFGUARDINVALID: invalid value 'foo' in '/guard:' diff --git a/clang/test/PCH/cxx2a-constraints.cpp b/clang/test/PCH/cxx2a-constraints.cpp index 3f3b5e536cc93..a7fd2a7816372 100644 --- a/clang/test/PCH/cxx2a-constraints.cpp +++ b/clang/test/PCH/cxx2a-constraints.cpp @@ -26,6 +26,8 @@ template void i(T) {} void j(SizedLike auto ...ints) {} +template class P> struct S1 { }; + #else /*included pch*/ int main() { diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp index 6ae2664046d71..b3c722dce53a5 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -1029,3 +1029,41 @@ int f() { int x = A{}; } } + +namespace GH58207 { +struct tester { + consteval tester(const char* name) noexcept { } +}; +consteval const char* make_name(const char* name) { return name;} +consteval const char* pad(int P) { return "thestring"; } + +int bad = 10; // expected-note 6{{declared here}} + +tester glob1(make_name("glob1")); +tester glob2(make_name("glob2")); +constexpr tester cglob(make_name("cglob")); +tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} + +constexpr tester glob3 = { make_name("glob3") }; +constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \ + // expected-note 2{{read of non-const variable 'bad' is not allowed in a constant expression}} + +auto V = make_name(pad(3)); +auto V1 = make_name(pad(bad)); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} + + +void foo() { + static tester loc1(make_name("loc1")); + static constexpr tester loc2(make_name("loc2")); + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} +} + +void bar() { + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} +} +} diff --git a/clang/test/SemaCXX/paren-list-agg-init.cpp b/clang/test/SemaCXX/paren-list-agg-init.cpp index c9d73327025cf..042ce3b3ddce2 100644 --- a/clang/test/SemaCXX/paren-list-agg-init.cpp +++ b/clang/test/SemaCXX/paren-list-agg-init.cpp @@ -9,7 +9,7 @@ struct A { // expected-note 4{{candidate constructor}} struct B { A a; int b[20]; - int &&c; // expected-note {{reference member declared here}} + int &&c; }; struct C { // expected-note 5{{candidate constructor}} @@ -21,9 +21,9 @@ struct D : public C, public A { int a; }; -struct E { // expected-note 3{{candidate constructor}} - struct F { - F(int, int); +struct E { + struct F { // expected-note 2{{candidate constructor}} + F(int, int); // expected-note {{candidate constructor}} }; int a; F f; @@ -56,6 +56,22 @@ struct J { int b[]; // expected-note {{initialized flexible array member 'b' is here}} }; +enum K { K0, K1, K2 }; + +struct L { + K k : 1; +}; + +struct M { + struct N { + private: + N(int); + // expected-note@-1 {{declared private here}} + }; + int i; + N n; +}; + union U { int a; char* b; @@ -74,7 +90,7 @@ T Construct(Args... args) { // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} } -void foo() { +void foo(int n) { A a1(1954, 9, 21); // expected-error@-1 {{excess elements in struct initializer}} A a2(2.1); @@ -96,9 +112,8 @@ void foo() { B b1(2022, {7, 8}); // expected-error@-1 {{no viable conversion from 'int' to 'A'}} B b2(A(1), {}, 1); - // expected-error@-1 {{reference member 'c' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} - // beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} - // beforecxx20-warning@-3 {{aggregate initialization of type 'B' from a parenthesized list of values is a C++20 extension}} + // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'B' from a parenthesized list of values is a C++20 extension}} C c(A(1), 1, 2, 3, 4); // expected-error@-1 {{array initializer must be an initializer list}} @@ -130,7 +145,7 @@ void foo() { // expected-error@-1 {{excess elements in union initializer}} E e1(1); - // expected-error@-1 {{no matching constructor for initialization of 'E'}} + // expected-error@-1 {{no matching constructor for initialization of 'F'}} constexpr F f1(1); // expected-error@-1 {{constexpr variable 'f1' must be initialized by a constant expression}} @@ -148,18 +163,29 @@ void foo() { A a7 = Construct('i', 2.2); // beforecxx20-note@-1 {{in instantiation of function template specialization 'Construct' requested here}} + L l(K::K2); + // expected-warning@-1 {{implicit truncation}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'L' from a parenthesized list of values is a C++20 extension}} + int arr4[](1, 2); // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}} int arr5[2](1, 2); // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}} + int arr6[n](1, 2, 3); + // expected-error@-1 {{variable-sized object may not be initialized}} + I i(1, 2); // expected-error@-1 {{no matching constructor for initialization of 'I'}} J j(1, {2, 3}); // expected-error@-1 {{initialization of flexible array member is not allowed}} + M m(1, 1); + // expected-error@-1 {{field of type 'N' has private constructor}} + // beforecxx20-warning@-2 {{aggregate initialization of type 'M' from a parenthesized list of values is a C++20 extension}} + static_assert(__is_trivially_constructible(A, char, double)); static_assert(__is_trivially_constructible(A, char, int)); static_assert(__is_trivially_constructible(A, char)); @@ -221,5 +247,28 @@ M m(42); N n(43); // expected-error@-1 {{field of type 'L' has protected constructor}} // beforecxx20-warning@-2 {{aggregate initialization of type 'N' from a parenthesized list of values is a C++20 extension}} +} + +namespace gh61567 { +struct O { + int i; + int &&j; + // expected-note@-1 {{uninitialized reference member is here}} + int &&k = 1; +}; + +O o1(0, 0, 0); // no-error +// beforecxx20-warning@-1 {{aggregate initialization of type 'O' from a parenthesized list of values is a C++20 extension}} + +O o2(0, 0); // no-error +// beforecxx20-warning@-1 {{aggregate initialization of type 'O' from a parenthesized list of values is a C++20 extension}} + +O o3(0); +// expected-error@-1 {{reference member of type 'int &&' uninitialized}} +} +namespace gh63008 { +auto a = new A('a', {1.1}); +// expected-warning@-1 {{braces around scalar init}} +// beforecxx20-warning@-2 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} } diff --git a/clang/test/SemaOpenMP/arm-sve-acle-types.cpp b/clang/test/SemaOpenMP/arm-sve-acle-types.cpp new file mode 100644 index 0000000000000..7afa6e9da24da --- /dev/null +++ b/clang/test/SemaOpenMP/arm-sve-acle-types.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fopenmp -fsyntax-only -triple aarch64-arm-none-eabi -target-feature +sve -verify %s +// expected-no-diagnostics + +__SVBool_t foo(int); + +void test() { +#pragma omp parallel + { + __SVBool_t pg = foo(1); + } +} diff --git a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp index f3bcacbf02544..72dc6acad4495 100644 --- a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp +++ b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp @@ -110,6 +110,24 @@ TEST_F(IntegerLiteralSeparatorTest, SingleQuoteAsSeparator) { "hil = 0xABCil;", Style); + verifyFormat("bd = 0b1'0000d;\n" + "dh = 1'234h;\n" + "dmin = 1'234min;\n" + "dns = 1'234ns;\n" + "ds = 1'234s;\n" + "dus = 1'234us;\n" + "hy = 0xA'BCy;", + "bd = 0b10000d;\n" + "dh = 1234h;\n" + "dmin = 1234min;\n" + "dns = 1234ns;\n" + "ds = 1234s;\n" + "dus = 1234us;\n" + "hy = 0xABCy;", + Style); + + verifyFormat("hd = 0xAB'Cd;", "hd = 0xABCd;", Style); + verifyFormat("d = 5'678_km;\n" "h = 0xD'EF_u16;", "d = 5678_km;\n" diff --git a/compiler-rt/cmake/Modules/AddCompilerRT.cmake b/compiler-rt/cmake/Modules/AddCompilerRT.cmake index 2771a4063c1df..045221312a820 100644 --- a/compiler-rt/cmake/Modules/AddCompilerRT.cmake +++ b/compiler-rt/cmake/Modules/AddCompilerRT.cmake @@ -392,8 +392,8 @@ function(add_compiler_rt_runtime name type) target_link_libraries(${libname} PRIVATE ${builtins_${libname}}) endif() if(${type} STREQUAL "SHARED") - if(COMMAND llvm_setup_rpath) - llvm_setup_rpath(${libname}) + if(APPLE OR WIN32) + set_property(TARGET ${libname} PROPERTY BUILD_WITH_INSTALL_RPATH ON) endif() if(WIN32 AND NOT CYGWIN AND NOT MINGW) set_target_properties(${libname} PROPERTIES IMPORT_PREFIX "") diff --git a/libcxx/include/__config b/libcxx/include/__config index 663774b0da38a..e2d5ccdada1ac 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -38,7 +38,7 @@ // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM. // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 16.0.1 == 16.00.01), _LIBCPP_VERSION is // defined to XXYYZZ. -# define _LIBCPP_VERSION 160004 +# define _LIBCPP_VERSION 160006 # define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y # define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y) diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst index c40d4b322080a..dad3177e2c7df 100644 --- a/lld/docs/WebAssembly.rst +++ b/lld/docs/WebAssembly.rst @@ -75,6 +75,11 @@ WebAssembly-specific options: flag which corresponds to ``--unresolve-symbols=ignore`` + ``--import-undefined``. +.. option:: --allow-undefined-file= + + Like ``--allow-undefined``, but the filename specified a flat list of + symbols, one per line, which are allowed to be undefined. + .. option:: --unresolved-symbols= This is a more full featured version of ``--allow-undefined``. @@ -182,11 +187,39 @@ Imports By default no undefined symbols are allowed in the final binary. The flag ``--allow-undefined`` results in a WebAssembly import being defined for each undefined symbol. It is then up to the runtime to provide such symbols. +``--allow-undefined-file`` is the same but allows a list of symbols to be +specified. Alternatively symbols can be marked in the source code as with the ``import_name`` and/or ``import_module`` clang attributes which signals that they are expected to be undefined at static link time. +Stub Libraries +~~~~~~~~~~~~~~ + +Another way to specify imports and exports is via a "stub library". This +feature is inspired by the ELF stub objects which are supported by the Solaris +linker. Stub libraries are text files that can be passed as normal linker +inputs, similar to how linker scripts can be passed to the ELF linker. The stub +library is a stand-in for a set of symbols that will be available at runtime, +but doesn't contain any actual code or data. Instead it contains just a list of +symbols, one per line. Each symbol can specify zero or more dependencies. +These dependencies are symbols that must be defined, and exported, by the output +module if the symbol is question is imported/required by the output module. + +For example, imagine the runtime provides an external symbol ``foo`` that +depends on the ``malloc`` and ``free``. This can be expressed simply as:: + + #STUB + foo: malloc,free + +Here we are saying that ``foo`` is allowed to be imported (undefined) but that +if it is imported, then the output module must also export ``malloc`` and +``free`` to the runtime. If ``foo`` is imported (undefined), but the output +module does not define ``malloc`` and ``free`` then the link will fail. + +Stub libraries must begin with ``#STUB`` on a line by itself. + Garbage Collection ~~~~~~~~~~~~~~~~~~ diff --git a/lld/test/wasm/Inputs/.gitattributes b/lld/test/wasm/Inputs/.gitattributes new file mode 100644 index 0000000000000..6622904075b27 --- /dev/null +++ b/lld/test/wasm/Inputs/.gitattributes @@ -0,0 +1,3 @@ +# ensures that we test parsing of stub libraries that contain +# windows line endings +libstub.so text eol=crlf diff --git a/lld/test/wasm/Inputs/libstub-missing-dep.so b/lld/test/wasm/Inputs/libstub-missing-dep.so new file mode 100644 index 0000000000000..f2345b766f099 --- /dev/null +++ b/lld/test/wasm/Inputs/libstub-missing-dep.so @@ -0,0 +1,2 @@ +#STUB +foo: missing_dep,missing_dep2 diff --git a/lld/test/wasm/Inputs/libstub-missing-sym.so b/lld/test/wasm/Inputs/libstub-missing-sym.so new file mode 100644 index 0000000000000..2120b948511e9 --- /dev/null +++ b/lld/test/wasm/Inputs/libstub-missing-sym.so @@ -0,0 +1,3 @@ +#STUB +# Symbol `foo` is missing from this file which causes stub_object.s to fail +bar diff --git a/lld/test/wasm/Inputs/libstub.so b/lld/test/wasm/Inputs/libstub.so new file mode 100644 index 0000000000000..57e61f632b101 --- /dev/null +++ b/lld/test/wasm/Inputs/libstub.so @@ -0,0 +1,5 @@ +#STUB +# This is a comment +foo: foodep1,foodep2 +# This symbols as no dependencies +bar diff --git a/lld/test/wasm/stub_library.s b/lld/test/wasm/stub_library.s new file mode 100644 index 0000000000000..9cbf2505ea9e7 --- /dev/null +++ b/lld/test/wasm/stub_library.s @@ -0,0 +1,48 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld %t.o %p/Inputs/libstub.so -o %t.wasm +# RUN: obj2yaml %t.wasm | FileCheck %s + +# When the dependencies are missing the link fails +# RUN: not wasm-ld %t.o %p/Inputs/libstub-missing-dep.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING-DEP %s + +# When the dependencies are missing the link fails +# RUN: not wasm-ld %t.o %p/Inputs/libstub-missing-sym.so -o %t.wasm 2>&1 | FileCheck --check-prefix=MISSING-SYM %s + +# MISSING-DEP: libstub-missing-dep.so: undefined symbol: missing_dep. Required by foo +# MISSING-DEP: libstub-missing-dep.so: undefined symbol: missing_dep2. Required by foo + +# MISSING-SYM: undefined symbol: foo + +# The function foo is defined in libstub.so but depend on foodep1 and foodep2 +.functype foo () -> () + +.globl foodep1 +foodep1: + .functype foodep1 () -> () + end_function + +.globl foodep2 +foodep2: + .functype foodep2 () -> () + end_function + +.globl _start +_start: + .functype _start () -> () + call foo + end_function + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: memory +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: - Name: foodep1 +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: - Name: foodep2 +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 2 +# CHECK-NEXT: - Name: _start +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 3 diff --git a/lld/test/wasm/why-extract.s b/lld/test/wasm/why-extract.s new file mode 100644 index 0000000000000..88be4b3fcae51 --- /dev/null +++ b/lld/test/wasm/why-extract.s @@ -0,0 +1,87 @@ +# RUN: rm -rf %t && split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %t/main.s -o %t/main.o +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %t/a.s -o %t/a.o +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %t/a_b.s -o %t/a_b.o +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %t/b.s -o %t/b.o +# RUN: llvm-ar rc %t/a.a %t/a.o +# RUN: llvm-ar rc %t/a_b.a %t/a_b.o +# RUN: llvm-ar rc %t/b.a %t/b.o +# RUN: cd %t + +## Nothing is extracted from an archive. The file is created with just a header. +# RUN: wasm-ld main.o a.o b.a -o /dev/null --why-extract=why1.txt +# RUN: FileCheck %s --input-file=why1.txt --check-prefix=CHECK1 --match-full-lines --strict-whitespace + +# CHECK1:reference extracted symbol +# CHECK1-NOT:{{.}} + +## Some archive members are extracted. +# RUN: wasm-ld main.o a_b.a b.a -o /dev/null --why-extract=why2.txt +# RUN: FileCheck %s --input-file=why2.txt --check-prefix=CHECK2 --match-full-lines --strict-whitespace + +# CHECK2:reference extracted symbol +# CHECK2-NEXT:main.o a_b.a(a_b.o) a +# CHECK2-NEXT:a_b.a(a_b.o) b.a(b.o) b() + +## An undefined symbol error does not suppress the output. +# RUN: not wasm-ld main.o a_b.a -o /dev/null --why-extract=why3.txt +# RUN: FileCheck %s --input-file=why3.txt --check-prefix=CHECK3 --match-full-lines --strict-whitespace + +## Check that backward references are supported. +## - means stdout. +# RUN: wasm-ld b.a a_b.a main.o -o /dev/null --why-extract=- | FileCheck %s --check-prefix=CHECK4 + +# CHECK3:reference extracted symbol +# CHECK3-NEXT:main.o a_b.a(a_b.o) a + +# CHECK4:reference extracted symbol +# CHECK4-NEXT:a_b.a(a_b.o) b.a(b.o) b() +# CHECK4-NEXT:main.o a_b.a(a_b.o) a + +# RUN: wasm-ld main.o a_b.a b.a -o /dev/null --no-demangle --why-extract=- | FileCheck %s --check-prefix=MANGLED + +# MANGLED: a_b.a(a_b.o) b.a(b.o) _Z1bv + +# RUN: wasm-ld main.o a.a b.a -o /dev/null -u _Z1bv --why-extract=- | FileCheck %s --check-prefix=UNDEFINED + +## We insert -u symbol before processing other files, so its name is . +## This is not ideal. +# UNDEFINED: b.a(b.o) b() + +# RUN: wasm-ld main.o a.a b.a -o /dev/null -e _Z1bv --why-extract=- | FileCheck %s --check-prefix=ENTRY + +# ENTRY: --entry b.a(b.o) b() + +# SCRIPT: b.a(b.o) b() + +# RUN: not wasm-ld -shared main.o -o /dev/null --why-extract=/ 2>&1 | FileCheck %s --check-prefix=ERR + +# ERR: error: cannot open --why-extract= file /: {{.*}} + +#--- main.s +.globl _start +.functype a () -> () +_start: + .functype _start () -> () + call a + end_function + +#--- a.s +.globl a +a: + .functype a () -> () + end_function + +#--- a_b.s +.functype _Z1bv () -> () +.globl a +a: + .functype a () -> () + call _Z1bv + end_function + +#--- b.s +.globl _Z1bv +_Z1bv: + .functype _Z1bv () -> () + end_function diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h index 858a97860dbe3..dba4a5c2d8fbf 100644 --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -19,6 +19,9 @@ namespace lld { namespace wasm { +class InputFile; +class Symbol; + // For --unresolved-symbols. enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic }; @@ -72,6 +75,7 @@ struct Configuration { llvm::StringRef mapFile; llvm::StringRef outputFile; llvm::StringRef thinLTOCacheDir; + llvm::StringRef whyExtract; llvm::StringSet<> allowUndefinedSymbols; llvm::StringSet<> exportedSymbols; @@ -82,7 +86,8 @@ struct Configuration { std::optional> extraFeatures; // The following config options do not directly correspond to any - // particular command line options. + // particular command line options, and should probably be moved to seperate + // Ctx struct as in ELF/Config.h // True if we are creating position-independent code. bool isPic; @@ -100,6 +105,11 @@ struct Configuration { // Will be set to true if bss data segments should be emitted. In most cases // this is not necessary. bool emitBssSegments = false; + + // A tuple of (reference, extractedFile, sym). Used by --why-extract=. + llvm::SmallVector, + 0> + whyExtractRecords; }; // The only instance of Configuration struct. diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 2f2837ddbdcd8..762d4c97b0995 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -280,6 +280,12 @@ void LinkerDriver::addFile(StringRef path) { case file_magic::wasm_object: files.push_back(createObjectFile(mbref)); break; + case file_magic::unknown: + if (mbref.getBuffer().starts_with("#STUB")) { + files.push_back(make(mbref)); + break; + } + [[fallthrough]]; default: error("unknown file type: " + mbref.getBufferIdentifier()); } @@ -450,6 +456,7 @@ static void readConfigs(opt::InputArgList &args) { parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); config->unresolvedSymbols = getUnresolvedSymbolPolicy(args); + config->whyExtract = args.getLastArgValue(OPT_why_extract); errorHandler().verbose = args.hasArg(OPT_verbose); LLVM_DEBUG(errorHandler().verbose = true); @@ -631,7 +638,7 @@ static const char *getReproduceOption(opt::InputArgList &args) { } // Force Sym to be entered in the output. Used for -u or equivalent. -static Symbol *handleUndefined(StringRef name) { +static Symbol *handleUndefined(StringRef name, const char *option) { Symbol *sym = symtab->find(name); if (!sym) return nullptr; @@ -640,8 +647,11 @@ static Symbol *handleUndefined(StringRef name) { // eliminate it. Mark the symbol as "used" to prevent it. sym->isUsedInRegularObj = true; - if (auto *lazySym = dyn_cast(sym)) + if (auto *lazySym = dyn_cast(sym)) { lazySym->fetch(); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(option, sym->getFile(), *sym); + } return sym; } @@ -653,8 +663,31 @@ static void handleLibcall(StringRef name) { if (auto *lazySym = dyn_cast(sym)) { MemoryBufferRef mb = lazySym->getMemberBuffer(); - if (isBitcode(mb)) + if (isBitcode(mb)) { + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back("", sym->getFile(), + *sym); lazySym->fetch(); + } + } +} + +static void writeWhyExtract() { + if (config->whyExtract.empty()) + return; + + std::error_code ec; + raw_fd_ostream os(config->whyExtract, ec, sys::fs::OF_None); + if (ec) { + error("cannot open --why-extract= file " + config->whyExtract + ": " + + ec.message()); + return; + } + + os << "reference\textracted\tsymbol\n"; + for (auto &entry : config->whyExtractRecords) { + os << std::get<0>(entry) << '\t' << toString(std::get<1>(entry)) << '\t' + << toString(std::get<2>(entry)) << '\n'; } } @@ -807,6 +840,53 @@ static void createOptionalSymbols() { WasmSym::tlsBase = createOptionalGlobal("__tls_base", false); } +static void processStubLibraries() { + log("-- processStubLibraries"); + for (auto &stub_file : symtab->stubFiles) { + LLVM_DEBUG(llvm::dbgs() + << "processing stub file: " << stub_file->getName() << "\n"); + for (auto [name, deps]: stub_file->symbolDependencies) { + auto* sym = symtab->find(name); + if (!sym || !sym->isUndefined() || !sym->isUsedInRegularObj || + sym->forceImport) { + LLVM_DEBUG(llvm::dbgs() << "stub not in needed: " << name << "\n"); + continue; + } + // The first stub library to define a given symbol sets this and + // definitions in later stub libraries are ignored. + sym->forceImport = true; + if (sym->traced) + message(toString(stub_file) + ": importing " + name); + else + LLVM_DEBUG(llvm::dbgs() + << toString(stub_file) << ": importing " << name << "\n"); + for (const auto dep : deps) { + auto* needed = symtab->find(dep); + if (!needed) { + error(toString(stub_file) + ": undefined symbol: " + dep + + ". Required by " + toString(*sym)); + } else if (needed->isUndefined()) { + error(toString(stub_file) + + ": undefined symbol: " + toString(*needed) + + ". Required by " + toString(*sym)); + } else { + LLVM_DEBUG(llvm::dbgs() + << "force export: " << toString(*needed) << "\n"); + needed->forceExport = true; + needed->isUsedInRegularObj = true; + if (auto *lazy = dyn_cast(needed)) { + lazy->fetch(); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(stub_file->getName(), + sym->getFile(), *sym); + } + } + } + } + } + log("-- done processStubLibraries"); +} + // Reconstructs command line arguments so that so that you can re-run // the same command with the same inputs. This is for --reproduce. static std::string createResponseFile(const opt::InputArgList &args) { @@ -1035,16 +1115,16 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle the `--undefined ` options. for (auto *arg : args.filtered(OPT_undefined)) - handleUndefined(arg->getValue()); + handleUndefined(arg->getValue(), ""); // Handle the `--export ` options // This works like --undefined but also exports the symbol if its found for (auto &iter : config->exportedSymbols) - handleUndefined(iter.first()); + handleUndefined(iter.first(), "--export"); Symbol *entrySym = nullptr; if (!config->relocatable && !config->entry.empty()) { - entrySym = handleUndefined(config->entry); + entrySym = handleUndefined(config->entry, "--entry"); if (entrySym && entrySym->isDefined()) entrySym->forceExport = true; else @@ -1061,7 +1141,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { !WasmSym::callCtors->isUsedInRegularObj && WasmSym::callCtors->getName() != config->entry && !config->exportedSymbols.count(WasmSym::callCtors->getName())) { - if (Symbol *callDtors = handleUndefined("__wasm_call_dtors")) { + if (Symbol *callDtors = + handleUndefined("__wasm_call_dtors", "")) { if (auto *callDtorsFunc = dyn_cast(callDtors)) { if (callDtorsFunc->signature && (!callDtorsFunc->signature->Params.empty() || @@ -1096,12 +1177,16 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (errorCount()) return; + writeWhyExtract(); + // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. symtab->compileBitcodeFiles(); if (errorCount()) return; + processStubLibraries(); + createOptionalSymbols(); // Resolve any variant symbols that were created due to signature diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index e8a3701232fb1..a03166da973ac 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -12,6 +12,7 @@ #include "InputElement.h" #include "OutputSegment.h" #include "SymbolTable.h" +#include "lld/Common/Args.h" #include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Reproduce.h" #include "llvm/Object/Binary.h" @@ -678,6 +679,52 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &sym, bool isCalledDirectly) { llvm_unreachable("unknown symbol kind"); } + +StringRef strip(StringRef s) { + while (s.starts_with(" ")) { + s = s.drop_front(); + } + while (s.ends_with(" ")) { + s = s.drop_back(); + } + return s; +} + +void StubFile::parse() { + bool first = true; + + SmallVector lines; + mb.getBuffer().split(lines, '\n'); + for (StringRef line : lines) { + line = line.trim(); + + // File must begin with #STUB + if (first) { + assert(line == "#STUB"); + first = false; + } + + // Lines starting with # are considered comments + if (line.startswith("#")) + continue; + + StringRef sym; + StringRef rest; + std::tie(sym, rest) = line.split(':'); + sym = strip(sym); + rest = strip(rest); + + symbolDependencies[sym] = {}; + + while (rest.size()) { + StringRef dep; + std::tie(dep, rest) = rest.split(','); + dep = strip(dep); + symbolDependencies[sym].push_back(dep); + } + } +} + void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. LLVM_DEBUG(dbgs() << "Parsing library: " << toString(this) << "\n"); diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h index 22066cb9d1555..4c46ae88d4e16 100644 --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -47,6 +47,7 @@ class InputFile { SharedKind, ArchiveKind, BitcodeKind, + StubKind, }; virtual ~InputFile() {} @@ -183,6 +184,18 @@ class BitcodeFile : public InputFile { static bool doneLTO; }; +// Stub libray (See docs/WebAssembly.rst) +class StubFile : public InputFile { +public: + explicit StubFile(MemoryBufferRef m) : InputFile(StubKind, m) {} + + static bool classof(const InputFile *f) { return f->kind() == StubKind; } + + void parse(); + + llvm::DenseMap> symbolDependencies; +}; + inline bool isBitcode(MemoryBufferRef mb) { return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; } diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td index 2871ae6d576be..b0c727cc5339a 100644 --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -221,6 +221,8 @@ defm whole_archive: B<"whole-archive", "Force load of all members in a static library", "Do not force load of all members in a static library (default)">; +def why_extract: JJ<"why-extract=">, HelpText<"Print to a file about why archive members are extracted">; + defm check_features: BB<"check-features", "Check feature compatibility of linked objects (default)", "Ignore feature compatibility of linked objects">; diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp index c7710a9baf320..a725b6df75d26 100644 --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -33,9 +33,9 @@ static bool requiresGOTAccess(const Symbol *sym) { } static bool allowUndefined(const Symbol* sym) { - // Symbols with explicit import names are always allowed to be undefined at + // Symbols that are explicitly imported are always allowed to be undefined at // link time. - if (sym->importName) + if (sym->isImported()) return true; if (isa(sym) && config->importUndefined) return true; diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index 8ca5e94a2a2a9..e5898c5e11571 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -39,6 +39,13 @@ void SymbolTable::addFile(InputFile *file) { return; } + // stub file + if (auto *f = dyn_cast(file)) { + f->parse(); + stubFiles.push_back(f); + return; + } + if (config->trace) message(toString(file)); @@ -524,6 +531,9 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef name, lazy->signature = sig; } else { lazy->fetch(); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(toString(file), s->getFile(), + *s); } } else { auto existingFunction = dyn_cast(s); @@ -748,7 +758,10 @@ void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) { } LLVM_DEBUG(dbgs() << "replacing existing undefined\n"); + const InputFile *oldFile = s->getFile(); file->addMember(sym); + if (!config->whyExtract.empty()) + config->whyExtractRecords.emplace_back(toString(oldFile), s->getFile(), *s); } bool SymbolTable::addComdat(StringRef name) { diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h index f624b8bdfd86a..311d4188c4f4e 100644 --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -103,6 +103,7 @@ class SymbolTable { DefinedFunction *createUndefinedStub(const WasmSignature &sig); std::vector objectFiles; + std::vector stubFiles; std::vector sharedFiles; std::vector bitcodeFiles; std::vector syntheticFunctions; diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index 5ef92dde7cc58..9a9235584259f 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -220,6 +220,10 @@ void Symbol::setHidden(bool isHidden) { flags |= WASM_SYMBOL_VISIBILITY_DEFAULT; } +bool Symbol::isImported() const { + return isUndefined() && (importName.has_value() || forceImport); +} + bool Symbol::isExported() const { if (!isDefined() || isLocal()) return false; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index 6bdf587f90e76..232339fc37407 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -114,6 +114,7 @@ class Symbol { void setOutputSymbolIndex(uint32_t index); WasmSymbolType getWasmType() const; + bool isImported() const; bool isExported() const; bool isExportedExplicit() const; @@ -135,7 +136,8 @@ class Symbol { Symbol(StringRef name, Kind k, uint32_t flags, InputFile *f) : name(name), file(f), symbolKind(k), referenced(!config->gcSections), requiresGOT(false), isUsedInRegularObj(false), forceExport(false), - canInline(false), traced(false), isStub(false), flags(flags) {} + forceImport(false), canInline(false), traced(false), isStub(false), + flags(flags) {} StringRef name; InputFile *file; @@ -160,6 +162,8 @@ class Symbol { // -e/--export command line flag) bool forceExport : 1; + bool forceImport : 1; + // False if LTO shouldn't inline whatever this symbol points to. If a symbol // is overwritten after LTO, LTO shouldn't inline the symbol because it // doesn't know the final contents of the symbol. @@ -656,6 +660,7 @@ T *replaceSymbol(Symbol *s, ArgT &&... arg) { T *s2 = new (s) T(std::forward(arg)...); s2->isUsedInRegularObj = symCopy.isUsedInRegularObj; s2->forceExport = symCopy.forceExport; + s2->forceImport = symCopy.forceImport; s2->canInline = symCopy.canInline; s2->traced = symCopy.traced; s2->referenced = symCopy.referenced; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 36005037cd17e..304897bfa4d04 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -647,7 +647,7 @@ static bool shouldImport(Symbol *sym) { if (config->allowUndefinedSymbols.count(sym->getName()) != 0) return true; - return sym->importName.has_value(); + return sym->isImported(); } void Writer::calculateImports() { @@ -1570,7 +1570,7 @@ void Writer::run() { sym->forceExport = true; } - // Delay reporting error about explicit exports until after + // Delay reporting errors about explicit exports until after // addStartStopSymbols which can create optional symbols. for (auto &name : config->requiredExports) { Symbol *sym = symtab->find(name); diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 8e9bc28001c5b..2b9315c22ed95 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -22,7 +22,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 0) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 4) + set(LLVM_VERSION_PATCH 6) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake index 94fc83db93445..93e6d67551de7 100644 --- a/llvm/cmake/modules/AddLLVM.cmake +++ b/llvm/cmake/modules/AddLLVM.cmake @@ -2320,7 +2320,8 @@ function(llvm_setup_rpath name) # FIXME: update this when there is better solution. set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) elseif(UNIX) - set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + set(_build_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}") if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)") set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-z,origin ") @@ -2334,9 +2335,16 @@ function(llvm_setup_rpath name) return() endif() - # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set. + # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set and not + # building for macOS or AIX, as those platforms seemingly require it. + # On AIX, the tool chain doesn't support modifying rpaths/libpaths for XCOFF + # on install at the moment, so BUILD_WITH_INSTALL_RPATH is required. if("${CMAKE_BUILD_RPATH}" STREQUAL "") - set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin|AIX") + set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) + else() + set_property(TARGET ${name} APPEND PROPERTY BUILD_RPATH "${_build_rpath}") + endif() endif() set_target_properties(${name} PROPERTIES diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake index 78be03d2bc13a..6119ecdce0f4b 100644 --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -582,16 +582,6 @@ if ( LLVM_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL" ) add_flag_if_supported("-Werror=unguarded-availability-new" WERROR_UNGUARDED_AVAILABILITY_NEW) endif( LLVM_COMPILER_IS_GCC_COMPATIBLE OR CMAKE_CXX_COMPILER_ID MATCHES "XL" ) -if ( LLVM_COMPILER_IS_GCC_COMPATIBLE ) - # LLVM data structures like llvm::User and llvm::MDNode rely on - # the value of object storage persisting beyond the lifetime of the - # object (#24952). This is not standard compliant and causes a runtime - # crash if LLVM is built with GCC and LTO enabled (#57740). Until - # these bugs are fixed, we need to disable dead store eliminations - # based on object lifetime. - add_flag_if_supported("-fno-lifetime-dse" CMAKE_CXX_FLAGS) -endif ( LLVM_COMPILER_IS_GCC_COMPATIBLE ) - # Modules enablement for GCC-compatible compilers: if ( LLVM_COMPILER_IS_GCC_COMPATIBLE AND LLVM_ENABLE_MODULES ) set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) diff --git a/llvm/cmake/modules/LLVM-Config.cmake b/llvm/cmake/modules/LLVM-Config.cmake index 37eab12e7460b..ae082c6ae5202 100644 --- a/llvm/cmake/modules/LLVM-Config.cmake +++ b/llvm/cmake/modules/LLVM-Config.cmake @@ -1,3 +1,6 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) + function(get_system_libs return_var) message(AUTHOR_WARNING "get_system_libs no longer needed") set(${return_var} "" PARENT_SCOPE) @@ -343,3 +346,5 @@ function(explicit_map_components_to_libraries out_libs) endforeach(c) set(${out_libs} ${result} PARENT_SCOPE) endfunction(explicit_map_components_to_libraries) + +cmake_policy(POP) diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h index 953e15e358f12..8ac6e7dac63e1 100644 --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -116,6 +116,15 @@ class AliasResult { operator Kind() const { return static_cast(Alias); } + bool operator==(const AliasResult &Other) const { + return Alias == Other.Alias && HasOffset == Other.HasOffset && + Offset == Other.Offset; + } + bool operator!=(const AliasResult &Other) const { return !(*this == Other); } + + bool operator==(Kind K) const { return Alias == K; } + bool operator!=(Kind K) const { return !(*this == K); } + constexpr bool hasOffset() const { return HasOffset; } constexpr int32_t getOffset() const { assert(HasOffset && "No offset!"); diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h index 8fcfbdbd6665c..951945f7b765d 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h @@ -408,14 +408,14 @@ class TargetLibraryInfo { ShouldExtI32Param = true; ShouldExtI32Return = true; } - // Mips and riscv64, on the other hand, needs signext on i32 parameters - // corresponding to both signed and unsigned ints. - if (T.isMIPS() || T.isRISCV64()) { + // LoongArch, Mips, and riscv64, on the other hand, need signext on i32 + // parameters corresponding to both signed and unsigned ints. + if (T.isLoongArch() || T.isMIPS() || T.isRISCV64()) { ShouldSignExtI32Param = true; } - // riscv64 needs signext on i32 returns corresponding to both signed and - // unsigned ints. - if (T.isRISCV64()) { + // LoongArch and riscv64 need signext on i32 returns corresponding to both + // signed and unsigned ints. + if (T.isLoongArch() || T.isRISCV64()) { ShouldSignExtI32Return = true; } } diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp index 202fc473f9e49..609a383426d66 100644 --- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp +++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp @@ -1628,7 +1628,7 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM, ISD::UADDO, ISD::SSUBO, ISD::USUBO, ISD::SMUL_LOHI, ISD::UMUL_LOHI, // Logical/bit: ISD::AND, ISD::OR, ISD::XOR, ISD::ROTL, ISD::ROTR, - ISD::CTPOP, ISD::CTLZ, ISD::CTTZ, + ISD::CTPOP, ISD::CTLZ, ISD::CTTZ, ISD::BSWAP, ISD::BITREVERSE, // Floating point arithmetic/math functions: ISD::FADD, ISD::FSUB, ISD::FMUL, ISD::FMA, ISD::FDIV, ISD::FREM, ISD::FNEG, ISD::FABS, ISD::FSQRT, ISD::FSIN, @@ -1701,8 +1701,11 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM, setOperationAction(ISD::OR, NativeVT, Legal); setOperationAction(ISD::XOR, NativeVT, Legal); - if (NativeVT.getVectorElementType() != MVT::i1) + if (NativeVT.getVectorElementType() != MVT::i1) { setOperationAction(ISD::SPLAT_VECTOR, NativeVT, Legal); + setOperationAction(ISD::BSWAP, NativeVT, Legal); + setOperationAction(ISD::BITREVERSE, NativeVT, Legal); + } } for (MVT VT : {MVT::v8i8, MVT::v4i16, MVT::v2i32}) { diff --git a/llvm/lib/Target/Hexagon/HexagonPatterns.td b/llvm/lib/Target/Hexagon/HexagonPatterns.td index a75ac0e1378ef..375e519a6848b 100644 --- a/llvm/lib/Target/Hexagon/HexagonPatterns.td +++ b/llvm/lib/Target/Hexagon/HexagonPatterns.td @@ -117,8 +117,8 @@ def usat: PatFrag<(ops node:$V, node:$Ty), (HexagonUSAT node:$V, node:$Ty)>; // Pattern fragments to extract the low and high subregisters from a // 64-bit value. -def LoReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG (i64 $Rs), isub_lo)>; -def HiReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG (i64 $Rs), isub_hi)>; +def LoReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG $Rs, isub_lo)>; +def HiReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG $Rs, isub_hi)>; def IsOrAdd: PatFrag<(ops node:$A, node:$B), (or node:$A, node:$B), [{ return isOrEquivalentToAdd(N); @@ -1123,6 +1123,12 @@ def: Pat<(bswap I32:$Rs), (A2_swiz I32:$Rs)>; def: Pat<(bswap I64:$Rss), (Combinew (A2_swiz (LoReg $Rss)), (A2_swiz (HiReg $Rss)))>; +def: Pat<(bswap V2I16:$Rs), (A2_combine_lh (A2_swiz $Rs), (A2_swiz $Rs))>; +def: Pat<(bswap V2I32:$Rs), (Combinew (A2_swiz (HiReg $Rs)), + (A2_swiz (LoReg $Rs)))>; +def: Pat<(bswap V4I16:$Rs), (A2_orp (S2_lsr_i_vh $Rs, 8), + (S2_asl_i_vh $Rs, 8))>; + def: Pat<(shl s6_0ImmPred:$s6, I32:$Rt), (S4_lsli imm:$s6, I32:$Rt)>; def: Pat<(shl I32:$Rs, (i32 16)), (A2_aslh I32:$Rs)>; def: Pat<(sra I32:$Rs, (i32 16)), (A2_asrh I32:$Rs)>; @@ -1854,6 +1860,20 @@ def: Pat<(i32 (ctpop I32:$Rs)), (S5_popcountp (A4_combineir 0, I32:$Rs))>; def: Pat<(bitreverse I32:$Rs), (S2_brev I32:$Rs)>; def: Pat<(bitreverse I64:$Rss), (S2_brevp I64:$Rss)>; +def: Pat<(bitreverse V4I8:$Rs), (A2_swiz (S2_brev $Rs))>; +def: Pat<(bitreverse V8I8:$Rs), (Combinew (A2_swiz (LoReg (S2_brevp $Rs))), + (A2_swiz (HiReg (S2_brevp $Rs))))>; +def: Pat<(bitreverse V2I16:$Rs), (A2_combine_lh (S2_brev $Rs), + (S2_brev $Rs))>; +def: Pat<(bitreverse V4I16:$Rs), + (Combinew (A2_combine_lh (LoReg (S2_brevp $Rs)), + (LoReg (S2_brevp $Rs))), + (A2_combine_lh (HiReg (S2_brevp $Rs)), + (HiReg (S2_brevp $Rs))))>; +def: Pat<(bitreverse V2I32:$Rs), + (Combinew (i32 (LoReg (S2_brevp $Rs))), + (i32 (HiReg (S2_brevp $Rs))))>; + let AddedComplexity = 20 in { // Complexity greater than and/or/xor def: Pat<(and I32:$Rs, IsNPow2_32:$V), (S2_clrbit_i IntRegs:$Rs, (LogN2_32 $V))>; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index cf17c51b04fcc..e43b33eed4707 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -499,7 +499,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, } if (Subtarget.hasSSEPrefetch() || Subtarget.hasThreeDNow()) - setOperationAction(ISD::PREFETCH , MVT::Other, Legal); + setOperationAction(ISD::PREFETCH , MVT::Other, Custom); setOperationAction(ISD::ATOMIC_FENCE , MVT::Other, Custom); @@ -2195,6 +2195,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, setOperationAction(ISD::FMUL, VT, Expand); setOperationAction(ISD::FDIV, VT, Expand); setOperationAction(ISD::BUILD_VECTOR, VT, Custom); + setOperationAction(ISD::VECTOR_SHUFFLE, VT, Custom); } addLegalFPImmediate(APFloat::getZero(APFloat::BFloat())); } @@ -2207,6 +2208,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, setOperationAction(ISD::FMUL, MVT::v32bf16, Expand); setOperationAction(ISD::FDIV, MVT::v32bf16, Expand); setOperationAction(ISD::BUILD_VECTOR, MVT::v32bf16, Custom); + setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v32bf16, Custom); } if (!Subtarget.useSoftFloat() && Subtarget.hasVLX()) { @@ -18773,11 +18775,11 @@ static SDValue lower256BitShuffle(const SDLoc &DL, ArrayRef Mask, MVT VT, return DAG.getBitcast(VT, DAG.getVectorShuffle(FpVT, DL, V1, V2, Mask)); } - if (VT == MVT::v16f16) { - V1 = DAG.getBitcast(MVT::v16i16, V1); - V2 = DAG.getBitcast(MVT::v16i16, V2); - return DAG.getBitcast(MVT::v16f16, - DAG.getVectorShuffle(MVT::v16i16, DL, V1, V2, Mask)); + if (VT == MVT::v16f16 || VT.getVectorElementType() == MVT::bf16) { + MVT IVT = VT.changeVectorElementTypeToInteger(); + V1 = DAG.getBitcast(IVT, V1); + V2 = DAG.getBitcast(IVT, V2); + return DAG.getBitcast(VT, DAG.getVectorShuffle(IVT, DL, V1, V2, Mask)); } switch (VT.SimpleTy) { @@ -33093,6 +33095,18 @@ static SDValue LowerCVTPS2PH(SDValue Op, SelectionDAG &DAG) { return DAG.getNode(ISD::CONCAT_VECTORS, dl, VT, Lo, Hi); } +static SDValue LowerPREFETCH(SDValue Op, const X86Subtarget &Subtarget, + SelectionDAG &DAG) { + unsigned IsData = cast(Op.getOperand(4))->getZExtValue(); + + // We don't support non-data prefetch without PREFETCHI. + // Just preserve the chain. + if (!IsData && !Subtarget.hasPREFETCHI()) + return Op.getOperand(0); + + return Op; +} + static StringRef getInstrStrFromOpNo(const SmallVectorImpl &AsmStrs, unsigned OpNo) { const APInt Operand(32, OpNo); @@ -33294,6 +33308,7 @@ SDValue X86TargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { case ISD::GC_TRANSITION_END: return LowerGC_TRANSITION(Op, DAG); case ISD::ADDRSPACECAST: return LowerADDRSPACECAST(Op, DAG); case X86ISD::CVTPS2PH: return LowerCVTPS2PH(Op, DAG); + case ISD::PREFETCH: return LowerPREFETCH(Op, Subtarget, DAG); } } diff --git a/llvm/lib/Target/X86/X86InstrAVX512.td b/llvm/lib/Target/X86/X86InstrAVX512.td index 6da4dd2b942ce..888e69ac4ac05 100644 --- a/llvm/lib/Target/X86/X86InstrAVX512.td +++ b/llvm/lib/Target/X86/X86InstrAVX512.td @@ -12969,6 +12969,27 @@ let Predicates = [HasBF16, HasVLX] in { (VCVTNEPS2BF16Z256rr VR256X:$src)>; def : Pat<(v8bf16 (int_x86_vcvtneps2bf16256 (loadv8f32 addr:$src))), (VCVTNEPS2BF16Z256rm addr:$src)>; + + def : Pat<(v8bf16 (X86VBroadcastld16 addr:$src)), + (VPBROADCASTWZ128rm addr:$src)>; + def : Pat<(v16bf16 (X86VBroadcastld16 addr:$src)), + (VPBROADCASTWZ256rm addr:$src)>; + + def : Pat<(v8bf16 (X86VBroadcast (v8bf16 VR128X:$src))), + (VPBROADCASTWZ128rr VR128X:$src)>; + def : Pat<(v16bf16 (X86VBroadcast (v8bf16 VR128X:$src))), + (VPBROADCASTWZ256rr VR128X:$src)>; + + // TODO: No scalar broadcast due to we don't support legal scalar bf16 so far. +} + +let Predicates = [HasBF16] in { + def : Pat<(v32bf16 (X86VBroadcastld16 addr:$src)), + (VPBROADCASTWZrm addr:$src)>; + + def : Pat<(v32bf16 (X86VBroadcast (v8bf16 VR128X:$src))), + (VPBROADCASTWZrr VR128X:$src)>; + // TODO: No scalar broadcast due to we don't support legal scalar bf16 so far. } let Constraints = "$src1 = $dst" in { diff --git a/llvm/test/CodeGen/Hexagon/bitmanip.ll b/llvm/test/CodeGen/Hexagon/bitmanip.ll index 2044a2fdd083b..9ce7f0576506c 100644 --- a/llvm/test/CodeGen/Hexagon/bitmanip.ll +++ b/llvm/test/CodeGen/Hexagon/bitmanip.ll @@ -1,135 +1,370 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: llc -march=hexagon < %s | FileCheck %s -; CHECK-LABEL: popcount_16 -; CHECK: zxth -; CHECK: popcount -define i16 @popcount_16(i16 %p) #0 { - %t = call i16 @llvm.ctpop.i16(i16 %p) #0 - ret i16 %t +define i16 @popcount_i16(i16 %a0) #0 { +; CHECK-LABEL: popcount_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: r0 = zxth(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = popcount(r1:0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.ctpop.i16(i16 %a0) #1 + ret i16 %v0 } -; CHECK-LABEL: popcount_32 -; CHECK: popcount -define i32 @popcount_32(i32 %p) #0 { - %t = call i32 @llvm.ctpop.i32(i32 %p) #0 - ret i32 %t +define i32 @popcount_i32(i32 %a0) #0 { +; CHECK-LABEL: popcount_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = popcount(r1:0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.ctpop.i32(i32 %a0) #1 + ret i32 %v0 } -; CHECK-LABEL: popcount_64 -; CHECK: popcount -define i64 @popcount_64(i64 %p) #0 { - %t = call i64 @llvm.ctpop.i64(i64 %p) #0 - ret i64 %t +define i64 @popcount_i64(i64 %a0) #0 { +; CHECK-LABEL: popcount_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = popcount(r1:0) +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.ctpop.i64(i64 %a0) #1 + ret i64 %v0 } -; CHECK-LABEL: ctlz_16 -; CHECK: [[REG0:r[0-9]+]] = zxth -; CHECK: [[REG1:r[0-9]+]] = cl0([[REG0]]) -; CHECK: add([[REG1]],#-16) -define i16 @ctlz_16(i16 %p) #0 { - %t = call i16 @llvm.ctlz.i16(i16 %p, i1 true) #0 - ret i16 %t +define i16 @ctlz_i16(i16 %a0) #0 { +; CHECK-LABEL: ctlz_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = zxth(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = cl0(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = add(r0,#-16) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.ctlz.i16(i16 %a0, i1 true) #1 + ret i16 %v0 } -; CHECK-LABEL: ctlz_32 -; CHECK: cl0 -define i32 @ctlz_32(i32 %p) #0 { - %t = call i32 @llvm.ctlz.i32(i32 %p, i1 true) #0 - ret i32 %t +define i32 @ctlz_i32(i32 %a0) #0 { +; CHECK-LABEL: ctlz_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = cl0(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.ctlz.i32(i32 %a0, i1 true) #1 + ret i32 %v0 } -; CHECK-LABEL: ctlz_64 -; CHECK: cl0 -define i64 @ctlz_64(i64 %p) #0 { - %t = call i64 @llvm.ctlz.i64(i64 %p, i1 true) #0 - ret i64 %t +define i64 @ctlz_i64(i64 %a0) #0 { +; CHECK-LABEL: ctlz_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = cl0(r1:0) +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.ctlz.i64(i64 %a0, i1 true) #1 + ret i64 %v0 } -; CHECK-LABEL: cttz_16 -; CHECK: ct0 -define i16 @cttz_16(i16 %p) #0 { - %t = call i16 @llvm.cttz.i16(i16 %p, i1 true) #0 - ret i16 %t +define i16 @cttz_i16(i16 %a0) #0 { +; CHECK-LABEL: cttz_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = ct0(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.cttz.i16(i16 %a0, i1 true) #1 + ret i16 %v0 } -; CHECK-LABEL: cttz_32 -; CHECK: ct0 -define i32 @cttz_32(i32 %p) #0 { - %t = call i32 @llvm.cttz.i32(i32 %p, i1 true) #0 - ret i32 %t +define i32 @cttz_i32(i32 %a0) #0 { +; CHECK-LABEL: cttz_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = ct0(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.cttz.i32(i32 %a0, i1 true) #1 + ret i32 %v0 } -; CHECK-LABEL: cttz_64 -; CHECK: ct0 -define i64 @cttz_64(i64 %p) #0 { - %t = call i64 @llvm.cttz.i64(i64 %p, i1 true) #0 - ret i64 %t +define i64 @cttz_i64(i64 %a0) #0 { +; CHECK-LABEL: cttz_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = ct0(r1:0) +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.cttz.i64(i64 %a0, i1 true) #1 + ret i64 %v0 } -; CHECK-LABEL: brev_16 -; CHECK: [[REG:r[0-9]+]] = brev -; CHECK: lsr([[REG]],#16) -define i16 @brev_16(i16 %p) #0 { - %t = call i16 @llvm.bitreverse.i16(i16 %p) #0 - ret i16 %t +define i16 @bswap_i16(i16 %a0) #0 { +; CHECK-LABEL: bswap_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = lsr(r0,#16) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.bswap.i16(i16 %a0) #1 + ret i16 %v0 } -; CHECK-LABEL: brev_32 -; CHECK: brev -define i32 @brev_32(i32 %p) #0 { - %t = call i32 @llvm.bitreverse.i32(i32 %p) #0 - ret i32 %t +define i32 @bswap_i32(i32 %a0) #0 { +; CHECK-LABEL: bswap_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.bswap.i32(i32 %a0) #1 + ret i32 %v0 } -; CHECK-LABEL: brev_64 -; CHECK: brev -define i64 @brev_64(i64 %p) #0 { - %t = call i64 @llvm.bitreverse.i64(i64 %p) #0 - ret i64 %t +define i64 @bswap_i64(i64 %a0) #0 { +; CHECK-LABEL: bswap_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r2 = swiz(r1) +; CHECK-NEXT: r3 = swiz(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = combine(r3,r2) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.bswap.i64(i64 %a0) #1 + ret i64 %v0 } -; CHECK-LABEL: bswap_16 -; CHECK: [[REG:r[0-9]+]] = swiz -; CHECK: lsr([[REG]],#16) -define i16 @bswap_16(i16 %p) #0 { - %t = call i16 @llvm.bswap.i16(i16 %p) #0 - ret i16 %t +define <2 x i16> @bswap_v2i16(<2 x i16> %a0) #0 { +; CHECK-LABEL: bswap_v2i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = combine(r0.l,r0.h) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i16> @llvm.bswap.v2i16(<2 x i16> %a0) + ret <2 x i16> %v0 } -; CHECK-LABEL: bswap_32 -; CHECK: swiz -define i32 @bswap_32(i32 %p) #0 { - %t = call i32 @llvm.bswap.i32(i32 %p) #0 - ret i32 %t +define <4 x i16> @bswap_v4i16(<4 x i16> %a0) #0 { +; CHECK-LABEL: bswap_v4i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = vlsrh(r1:0,#8) +; CHECK-NEXT: r5:4 = vaslh(r1:0,#8) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = or(r3:2,r5:4) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <4 x i16> @llvm.bswap.v4i16(<4 x i16> %a0) + ret <4 x i16> %v0 } -; CHECK-LABEL: bswap_64 -; CHECK: swiz -; CHECK: swiz -; CHECK: combine -define i64 @bswap_64(i64 %p) #0 { - %t = call i64 @llvm.bswap.i64(i64 %p) #0 - ret i64 %t +define <2 x i32> @bswap_v2i32(<2 x i32> %a0) #0 { +; CHECK-LABEL: bswap_v2i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: r1 = swiz(r1) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i32> @llvm.bswap.v2i32(<2 x i32> %a0) + ret <2 x i32> %v0 } -declare i16 @llvm.ctpop.i16(i16) #0 -declare i32 @llvm.ctpop.i32(i32) #0 -declare i64 @llvm.ctpop.i64(i64) #0 +define i16 @brev_i16(i16 %a0) #0 { +; CHECK-LABEL: brev_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = lsr(r0,#16) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.bitreverse.i16(i16 %a0) #1 + ret i16 %v0 +} + +define i32 @brev_i32(i32 %a0) #0 { +; CHECK-LABEL: brev_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.bitreverse.i32(i32 %a0) #1 + ret i32 %v0 +} + +define i64 @brev_i64(i64 %a0) #0 { +; CHECK-LABEL: brev_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = brev(r1:0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.bitreverse.i64(i64 %a0) #1 + ret i64 %v0 +} + +define <4 x i8> @brev_v4i8(<4 x i8> %a0) #0 { +; CHECK-LABEL: brev_v4i8: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <4 x i8> @llvm.bitreverse.v4i8(<4 x i8> %a0) + ret <4 x i8> %v0 +} + +define <8 x i8> @brev_v8i8(<8 x i8> %a0) #0 { +; CHECK-LABEL: brev_v8i8: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = brev(r1:0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r3) +; CHECK-NEXT: r1 = swiz(r2) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <8 x i8> @llvm.bitreverse.v8i8(<8 x i8> %a0) + ret <8 x i8> %v0 +} + +define <2 x i16> @brev_v2i16(<2 x i16> %a0) #0 { +; CHECK-LABEL: brev_v2i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = combine(r0.l,r0.h) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i16> @llvm.bitreverse.v2i16(<2 x i16> %a0) + ret <2 x i16> %v0 +} + +define <4 x i16> @brev_v4i16(<4 x i16> %a0) #0 { +; CHECK-LABEL: brev_v4i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = brev(r1:0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = combine(r3.l,r3.h) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r1 = combine(r2.l,r2.h) +; CHECK-NEXT: } + %v0 = tail call <4 x i16> @llvm.bitreverse.v4i16(<4 x i16> %a0) + ret <4 x i16> %v0 +} + +define <2 x i32> @brev_v2i32(<2 x i32> %a0) #0 { +; CHECK-LABEL: brev_v2i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = brev(r1:0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = combine(r2,r3) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i32> @llvm.bitreverse.v2i32(<2 x i32> %a0) + ret <2 x i32> %v0 +} + + +declare i16 @llvm.ctpop.i16(i16) #1 +declare i32 @llvm.ctpop.i32(i32) #1 +declare i64 @llvm.ctpop.i64(i64) #1 + +declare i16 @llvm.ctlz.i16(i16, i1) #1 +declare i32 @llvm.ctlz.i32(i32, i1) #1 +declare i64 @llvm.ctlz.i64(i64, i1) #1 + +declare i16 @llvm.cttz.i16(i16, i1) #1 +declare i32 @llvm.cttz.i32(i32, i1) #1 +declare i64 @llvm.cttz.i64(i64, i1) #1 + +declare i16 @llvm.bswap.i16(i16) #1 +declare i32 @llvm.bswap.i32(i32) #1 +declare i64 @llvm.bswap.i64(i64) #1 + +declare <2 x i16> @llvm.bswap.v2i16(<2 x i16>) #1 +declare <4 x i16> @llvm.bswap.v4i16(<4 x i16>) #1 +declare <2 x i32> @llvm.bswap.v2i32(<2 x i32>) #1 + +declare i16 @llvm.bitreverse.i16(i16) #1 +declare i32 @llvm.bitreverse.i32(i32) #1 +declare i64 @llvm.bitreverse.i64(i64) #1 -declare i16 @llvm.ctlz.i16(i16, i1) #0 -declare i32 @llvm.ctlz.i32(i32, i1) #0 -declare i64 @llvm.ctlz.i64(i64, i1) #0 +declare <4 x i8> @llvm.bitreverse.v4i8(<4 x i8>) #1 +declare <8 x i8> @llvm.bitreverse.v8i8(<8 x i8>) #1 -declare i16 @llvm.cttz.i16(i16, i1) #0 -declare i32 @llvm.cttz.i32(i32, i1) #0 -declare i64 @llvm.cttz.i64(i64, i1) #0 +declare <2 x i16> @llvm.bitreverse.v2i16(<2 x i16>) #1 +declare <4 x i16> @llvm.bitreverse.v4i16(<4 x i16>) #1 +declare <2 x i32> @llvm.bitreverse.v2i32(<2 x i32>) #1 -declare i16 @llvm.bitreverse.i16(i16) #0 -declare i32 @llvm.bitreverse.i32(i32) #0 -declare i64 @llvm.bitreverse.i64(i64) #0 -declare i16 @llvm.bswap.i16(i16) #0 -declare i32 @llvm.bswap.i32(i32) #0 -declare i64 @llvm.bswap.i64(i64) #0 +attributes #0 = { "target-features"="+v68,-long-calls" } +attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } -attributes #0 = { nounwind readnone } diff --git a/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll b/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll index 3cdc5de871e21..40b512d68be81 100644 --- a/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll +++ b/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll @@ -356,3 +356,49 @@ entry: %2 = select <4 x i1> %1, <4 x float> %0, <4 x float> %E ret <4 x float> %2 } + +define <16 x i16> @test_no_vbroadcast1() { +; CHECK-LABEL: test_no_vbroadcast1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: vcvtneps2bf16 %xmm0, %xmm0 # encoding: [0x62,0xf2,0x7e,0x08,0x72,0xc0] +; CHECK-NEXT: vpbroadcastw %xmm0, %ymm0 # EVEX TO VEX Compression encoding: [0xc4,0xe2,0x7d,0x79,0xc0] +; CHECK-NEXT: ret{{[l|q]}} # encoding: [0xc3] +entry: + %0 = tail call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> poison, <8 x bfloat> zeroinitializer, <4 x i1> ) + %1 = bitcast <8 x bfloat> %0 to <8 x i16> + %2 = shufflevector <8 x i16> %1, <8 x i16> undef, <16 x i32> zeroinitializer + ret <16 x i16> %2 +} + +;; FIXME: This should generate the same output as above, but let's fix the crash first. +define <16 x bfloat> @test_no_vbroadcast2() nounwind { +; X86-LABEL: test_no_vbroadcast2: +; X86: # %bb.0: # %entry +; X86-NEXT: pushl %ebp # encoding: [0x55] +; X86-NEXT: movl %esp, %ebp # encoding: [0x89,0xe5] +; X86-NEXT: andl $-32, %esp # encoding: [0x83,0xe4,0xe0] +; X86-NEXT: subl $64, %esp # encoding: [0x83,0xec,0x40] +; X86-NEXT: vcvtneps2bf16 %xmm0, %xmm0 # encoding: [0x62,0xf2,0x7e,0x08,0x72,0xc0] +; X86-NEXT: vmovaps %xmm0, (%esp) # EVEX TO VEX Compression encoding: [0xc5,0xf8,0x29,0x04,0x24] +; X86-NEXT: vpbroadcastw (%esp), %ymm0 # EVEX TO VEX Compression encoding: [0xc4,0xe2,0x7d,0x79,0x04,0x24] +; X86-NEXT: movl %ebp, %esp # encoding: [0x89,0xec] +; X86-NEXT: popl %ebp # encoding: [0x5d] +; X86-NEXT: retl # encoding: [0xc3] +; +; X64-LABEL: test_no_vbroadcast2: +; X64: # %bb.0: # %entry +; X64-NEXT: pushq %rbp # encoding: [0x55] +; X64-NEXT: movq %rsp, %rbp # encoding: [0x48,0x89,0xe5] +; X64-NEXT: andq $-32, %rsp # encoding: [0x48,0x83,0xe4,0xe0] +; X64-NEXT: subq $64, %rsp # encoding: [0x48,0x83,0xec,0x40] +; X64-NEXT: vcvtneps2bf16 %xmm0, %xmm0 # encoding: [0x62,0xf2,0x7e,0x08,0x72,0xc0] +; X64-NEXT: vmovaps %xmm0, (%rsp) # EVEX TO VEX Compression encoding: [0xc5,0xf8,0x29,0x04,0x24] +; X64-NEXT: vpbroadcastw (%rsp), %ymm0 # EVEX TO VEX Compression encoding: [0xc4,0xe2,0x7d,0x79,0x04,0x24] +; X64-NEXT: movq %rbp, %rsp # encoding: [0x48,0x89,0xec] +; X64-NEXT: popq %rbp # encoding: [0x5d] +; X64-NEXT: retq # encoding: [0xc3] +entry: + %0 = tail call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> poison, <8 x bfloat> zeroinitializer, <4 x i1> ) + %1 = shufflevector <8 x bfloat> %0, <8 x bfloat> undef, <16 x i32> zeroinitializer + ret <16 x bfloat> %1 +} diff --git a/llvm/test/CodeGen/X86/prefetchi.ll b/llvm/test/CodeGen/X86/prefetchi.ll index 8f97e077e535b..442819ea20d04 100644 --- a/llvm/test/CodeGen/X86/prefetchi.ll +++ b/llvm/test/CodeGen/X86/prefetchi.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=x86_64-- -mattr=+prefetchi | FileCheck %s +; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s --check-prefix=NOPREFETCHI define dso_local void @t(ptr %ptr) nounwind { ; CHECK-LABEL: t: @@ -9,6 +10,10 @@ define dso_local void @t(ptr %ptr) nounwind { ; CHECK-NEXT: prefetchit1 t(%rip) ; CHECK-NEXT: prefetchit0 ext(%rip) ; CHECK-NEXT: retq +; +; NOPREFETCHI-LABEL: t: +; NOPREFETCHI: # %bb.0: # %entry +; NOPREFETCHI-NEXT: retq entry: tail call void @llvm.prefetch(ptr %ptr, i32 0, i32 2, i32 0) tail call void @llvm.prefetch(ptr %ptr, i32 0, i32 3, i32 0) diff --git a/llvm/test/Instrumentation/AddressSanitizer/experiment.ll b/llvm/test/Instrumentation/AddressSanitizer/experiment.ll index 5d1c5a2d5014f..e18df618ebca8 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/experiment.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/experiment.ll @@ -3,7 +3,8 @@ ; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S | FileCheck %s ; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S -mtriple=s390x-unknown-linux | FileCheck %s --check-prefix=EXT ; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S -mtriple=mips-linux-gnu | FileCheck %s --check-prefix=MIPS_EXT -; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target +; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S -mtriple=loongarch64-unknown-linux-gnu | FileCheck %s --check-prefix=LA_EXT +; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target, loongarch-registered-target target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" @@ -119,15 +120,19 @@ entry: ; CHECK: declare void @__asan_report_exp_load_n(i64, i64, i32) ; EXT: declare void @__asan_report_exp_load_n(i64, i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_report_exp_load_n(i64, i64, i32 signext) +; LA_EXT: declare void @__asan_report_exp_load_n(i64, i64, i32 signext) ; CHECK: declare void @__asan_exp_loadN(i64, i64, i32) ; EXT: declare void @__asan_exp_loadN(i64, i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_exp_loadN(i64, i64, i32 signext) +; LA_EXT: declare void @__asan_exp_loadN(i64, i64, i32 signext) ; CHECK: declare void @__asan_report_exp_load1(i64, i32) ; EXT: declare void @__asan_report_exp_load1(i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_report_exp_load1(i64, i32 signext) +; LA_EXT: declare void @__asan_report_exp_load1(i64, i32 signext) ; CHECK: declare void @__asan_exp_load1(i64, i32) ; EXT: declare void @__asan_exp_load1(i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_exp_load1(i64, i32 signext) +; LA_EXT: declare void @__asan_exp_load1(i64, i32 signext) diff --git a/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll b/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll index c9d3132a777cb..67ef8d1cd6753 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll @@ -5,7 +5,8 @@ ; RUN: opt < %s -passes=asan -asan-kernel -asan-kernel-mem-intrinsic-prefix -S | FileCheck --check-prefixes=CHECK,CHECK-PREFIX %s ; RUN: opt < %s -passes=asan -S -mtriple=s390x-unknown-linux | FileCheck --check-prefix=EXT %s ; RUN: opt < %s -passes=asan -S -mtriple=mips-linux-gnu | FileCheck --check-prefix=MIPS_EXT %s -; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target +; RUN: opt < %s -passes=asan -S -mtriple=loongarch64-unknown-linux-gnu | FileCheck --check-prefix=LA_EXT %s +; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target, loongarch-registered-target target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" @@ -80,3 +81,4 @@ define void @memintr_element_atomic_test(ptr %a, ptr %b) nounwind uwtable saniti ; CHECK-PREFIX: declare ptr @__asan_memset(ptr, i32, i64) ; EXT: declare ptr @__asan_memset(ptr, i32 zeroext, i64) ; MIPS_EXT: declare ptr @__asan_memset(ptr, i32 signext, i64) +; LA_EXT: declare ptr @__asan_memset(ptr, i32 signext, i64) diff --git a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll index 0312aa1592f86..76afc4bf007c2 100644 --- a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll +++ b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll @@ -1,7 +1,8 @@ ; RUN: opt < %s -passes=tsan -S | FileCheck %s ; RUN: opt < %s -passes=tsan -S -mtriple=s390x-unknown-linux | FileCheck --check-prefix=EXT %s ; RUN: opt < %s -passes=tsan -S -mtriple=mips-linux-gnu | FileCheck --check-prefix=MIPS_EXT %s -; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target +; RUN: opt < %s -passes=tsan -S -mtriple=loongarch64-unknown-linux-gnu | FileCheck --check-prefix=LA_EXT %s +; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target, loongarch-registered-target ; Check that atomic memory operations are converted to calls into ThreadSanitizer runtime. target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" @@ -2102,38 +2103,47 @@ entry: ; CHECK: declare void @__tsan_atomic32_store(ptr, i32, i32) ; EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext) ; MIPS_EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext) +; LA_EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext) ; CHECK: declare i32 @__tsan_atomic32_compare_exchange_val(ptr, i32, i32, i32, i32) ; EXT: declare signext i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext) ; MIPS_EXT: declare i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext) +; LA_EXT: declare signext i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext) ; CHECK: declare i64 @__tsan_atomic64_load(ptr, i32) ; EXT: declare i64 @__tsan_atomic64_load(ptr, i32 signext) ; MIPS_EXT: declare i64 @__tsan_atomic64_load(ptr, i32 signext) +; LA_EXT: declare i64 @__tsan_atomic64_load(ptr, i32 signext) ; CHECK: declare void @__tsan_atomic64_store(ptr, i64, i32) ; EXT: declare void @__tsan_atomic64_store(ptr, i64, i32 signext) ; MIPS_EXT: declare void @__tsan_atomic64_store(ptr, i64, i32 signext) +; LA_EXT: declare void @__tsan_atomic64_store(ptr, i64, i32 signext) ; CHECK: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32) ; EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext) ; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext) +; LA_EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext) ; CHECK: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32, i32) ; EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext) ; MIPS_EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext) +; LA_EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext) ; CHECK: declare void @__tsan_atomic_thread_fence(i32) ; EXT: declare void @__tsan_atomic_thread_fence(i32 signext) ; MIPS_EXT: declare void @__tsan_atomic_thread_fence(i32 signext) +; LA_EXT: declare void @__tsan_atomic_thread_fence(i32 signext) ; CHECK: declare void @__tsan_atomic_signal_fence(i32) ; EXT: declare void @__tsan_atomic_signal_fence(i32 signext) ; MIPS_EXT: declare void @__tsan_atomic_signal_fence(i32 signext) +; LA_EXT: declare void @__tsan_atomic_signal_fence(i32 signext) ; CHECK: declare ptr @__tsan_memset(ptr, i32, i64) ; EXT: declare ptr @__tsan_memset(ptr, i32 signext, i64) ; MIPS_EXT: declare ptr @__tsan_memset(ptr, i32 signext, i64) +; LA_EXT: declare ptr @__tsan_memset(ptr, i32 signext, i64) !llvm.module.flags = !{!0, !1, !2} !llvm.dbg.cu = !{!8} diff --git a/llvm/test/Transforms/GVN/pr63019.ll b/llvm/test/Transforms/GVN/pr63019.ll new file mode 100644 index 0000000000000..f2628a32be5ed --- /dev/null +++ b/llvm/test/Transforms/GVN/pr63019.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -S -passes=gvn < %s | FileCheck %s + +; Make sure the two offsets from the phi don't get merged incorrectly. +define i8 @test(i1 %c, i64 %offset, ptr %ptr) { +; CHECK-LABEL: define i8 @test +; CHECK-SAME: (i1 [[C:%.*]], i64 [[OFFSET:%.*]], ptr [[PTR:%.*]]) { +; CHECK-NEXT: start: +; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [8 x i8], align 8 +; CHECK-NEXT: store i64 1234605616436508552, ptr [[ALLOCA]], align 8 +; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 2 +; CHECK-NEXT: [[GEP_UNKNOWN:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 [[OFFSET]] +; CHECK-NEXT: br i1 [[C]], label [[JOIN:%.*]], label [[IF:%.*]] +; CHECK: if: +; CHECK-NEXT: br label [[JOIN]] +; CHECK: join: +; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP_UNKNOWN]], [[START:%.*]] ], [ [[GEP_2]], [[IF]] ] +; CHECK-NEXT: store i8 0, ptr [[ALLOCA]], align 8 +; CHECK-NEXT: [[LOAD1:%.*]] = load i64, ptr [[ALLOCA]], align 8 +; CHECK-NEXT: store i64 [[LOAD1]], ptr [[PTR]], align 8 +; CHECK-NEXT: [[LOAD2:%.*]] = load i8, ptr [[PHI]], align 1 +; CHECK-NEXT: ret i8 [[LOAD2]] +; +start: + %alloca = alloca [8 x i8], align 8 + store i64 u0x1122334455667788, ptr %alloca, align 8 + %gep.2 = getelementptr i8, ptr %alloca, i64 2 + %gep.unknown = getelementptr i8, ptr %alloca, i64 %offset + br i1 %c, label %join, label %if + +if: + br label %join + +join: + %phi = phi ptr [ %gep.unknown, %start ], [ %gep.2, %if ] + store i8 0, ptr %alloca, align 8 + %load1 = load i64, ptr %alloca, align 8 + store i64 %load1, ptr %ptr, align 8 + %load2 = load i8, ptr %phi, align 1 + ret i8 %load2 +} diff --git a/llvm/test/tools/llvm-mca/X86/Generic/no-duplicate-symbols.s b/llvm/test/tools/llvm-mca/X86/Generic/no-duplicate-symbols.s new file mode 100644 index 0000000000000..8b8144c08b857 --- /dev/null +++ b/llvm/test/tools/llvm-mca/X86/Generic/no-duplicate-symbols.s @@ -0,0 +1,8 @@ +# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=x86-64 < %s 2>&1 | FileCheck %s + +# This test checks that https://github.com/llvm/llvm-project/issues/62528 is resolved. +foo: + pushq %rbp + +# CHECK-NOT: :4:1: error: symbol 'foo' is already defined + diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp index 73c341891ab7b..33adf15fccaf2 100644 --- a/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/llvm/tools/llvm-mca/llvm-mca.cpp @@ -401,11 +401,6 @@ int main(int argc, char **argv) { // Tell SrcMgr about this buffer, which is what the parser will pick up. SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); - MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); - std::unique_ptr MOFI( - TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); - Ctx.setObjectFileInfo(MOFI.get()); - std::unique_ptr BOS; std::unique_ptr MCII(TheTarget->createMCInstrInfo()); @@ -433,7 +428,11 @@ int main(int argc, char **argv) { } // Parse the input and create CodeRegions that llvm-mca can analyze. - mca::AsmAnalysisRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, + MCContext ACtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr AMOFI( + TheTarget->createMCObjectFileInfo(ACtx, /*PIC=*/false)); + ACtx.setObjectFileInfo(AMOFI.get()); + mca::AsmAnalysisRegionGenerator CRG(*TheTarget, SrcMgr, ACtx, *MAI, *STI, *MCII); Expected RegionsOrErr = CRG.parseAnalysisRegions(std::move(IPtemp)); @@ -471,7 +470,11 @@ int main(int argc, char **argv) { // Parse the input and create InstrumentRegion that llvm-mca // can use to improve analysis. - mca::AsmInstrumentRegionGenerator IRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, + MCContext ICtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr IMOFI( + TheTarget->createMCObjectFileInfo(ICtx, /*PIC=*/false)); + ICtx.setObjectFileInfo(IMOFI.get()); + mca::AsmInstrumentRegionGenerator IRG(*TheTarget, SrcMgr, ICtx, *MAI, *STI, *MCII, *IM); Expected InstrumentRegionsOrErr = IRG.parseInstrumentRegions(std::move(IPtemp)); @@ -547,7 +550,7 @@ int main(int argc, char **argv) { unsigned RegionIdx = 0; std::unique_ptr MCE( - TheTarget->createMCCodeEmitter(*MCII, Ctx)); + TheTarget->createMCCodeEmitter(*MCII, ACtx)); assert(MCE && "Unable to create code emitter!"); std::unique_ptr MAB(TheTarget->createMCAsmBackend( diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni index f7b2599d590ab..450f04d97f5b8 100644 --- a/llvm/utils/gn/secondary/llvm/version.gni +++ b/llvm/utils/gn/secondary/llvm/version.gni @@ -1,4 +1,4 @@ llvm_version_major = 16 llvm_version_minor = 0 -llvm_version_patch = 4 +llvm_version_patch = 6 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch" diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py index e9ef8e89d45ff..e9d22cdc62094 100644 --- a/llvm/utils/lit/lit/__init__.py +++ b/llvm/utils/lit/lit/__init__.py @@ -2,7 +2,7 @@ __author__ = 'Daniel Dunbar' __email__ = 'daniel@minormatter.com' -__versioninfo__ = (16, 0, 4) +__versioninfo__ = (16, 0, 6) __version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev' __all__ = []