From b579b056e302686c377e682be31a18b89d9c5249 Mon Sep 17 00:00:00 2001 From: Richard Smith <richard-llvm@metafoo.co.uk> Date: Mon, 15 Aug 2016 01:33:41 +0000 Subject: [PATCH] P0217R3: code generation support for decomposition declarations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@278642 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTContext.cpp | 8 ++ lib/AST/ItaniumMangle.cpp | 25 ++--- lib/AST/MicrosoftMangle.cpp | 18 +++- lib/CodeGen/CGDecl.cpp | 10 +- lib/CodeGen/CGExpr.cpp | 6 ++ lib/CodeGen/CodeGenModule.cpp | 4 + lib/Sema/SemaDeclCXX.cpp | 6 +- test/CodeGenCXX/cxx1z-decomposition.cpp | 118 ++++++++++++++++++++++++ test/SemaCXX/cxx1z-decomposition.cpp | 1 - 9 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 test/CodeGenCXX/cxx1z-decomposition.cpp diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4b42141452f..49948a5d418 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -8721,6 +8721,14 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { !VD->evaluateValue()) return true; + // Likewise, variables with tuple-like bindings are required if their + // bindings have side-effects. + if (auto *DD = dyn_cast<DecompositionDecl>(VD)) + for (auto *BD : DD->bindings()) + if (auto *BindingVD = BD->getHoldingVar()) + if (DeclMustBeEmitted(BindingVD)) + return true; + return false; } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 5b278314aee..67d217e7a66 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -1195,18 +1195,21 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, case DeclarationName::Identifier: { const IdentifierInfo *II = Name.getAsIdentifierInfo(); - // We mangle decomposition declarations as the name of their first binding. + // We mangle decomposition declarations as the names of their bindings. if (auto *DD = dyn_cast<DecompositionDecl>(ND)) { - auto B = DD->bindings(); - if (B.begin() == B.end()) { - // FIXME: This is ill-formed but we accept it as an extension. - DiagnosticsEngine &Diags = Context.getDiags(); - unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, - "cannot mangle global empty decomposition decl"); - Diags.Report(DD->getLocation(), DiagID); - break; - } - II = (*B.begin())->getIdentifier(); + // FIXME: Non-standard mangling for decomposition declarations: + // + // <unqualified-name> ::= DC <source-name>* E + // + // These can never be referenced across translation units, so we do + // not need a cross-vendor mangling for anything other than demanglers. + // Proposed on cxx-abi-dev on 2016-08-12 + Out << "DC"; + for (auto *BD : DD->bindings()) + mangleSourceName(BD->getDeclName().getAsIdentifierInfo()); + Out << 'E'; + writeAbiTags(ND, AdditionalAbiTags); + break; } if (II) { diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 351997e02a9..479ac44aa09 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -394,7 +394,8 @@ bool MicrosoftMangleContextImpl::shouldMangleCXXName(const NamedDecl *D) { if (!getASTContext().getLangOpts().CPlusPlus) return false; - if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + const VarDecl *VD = dyn_cast<VarDecl>(D); + if (VD && !isa<DecompositionDecl>(D)) { // C variables are not mangled. if (VD->isExternC()) return false; @@ -780,6 +781,21 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, } } + if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(ND)) { + // FIXME: Invented mangling for decomposition declarations: + // [X,Y,Z] + // where X,Y,Z are the names of the bindings. + llvm::SmallString<128> Name("["); + for (auto *BD : DD->bindings()) { + if (Name.size() > 1) + Name += ','; + Name += BD->getDeclName().getAsIdentifierInfo()->getName(); + } + Name += ']'; + mangleSourceName(Name); + break; + } + if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) { // We must have an anonymous union or struct declaration. const CXXRecordDecl *RD = VD->getType()->getAsCXXRecordDecl(); diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index e0cb07b98dd..037b1351c0f 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -87,6 +87,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::UsingShadow: case Decl::ConstructorUsingShadow: case Decl::ObjCTypeParam: + case Decl::Binding: llvm_unreachable("Declaration should not be in declstmts!"); case Decl::Function: // void X(); case Decl::Record: // struct/union/class X; @@ -119,10 +120,13 @@ void CodeGenFunction::EmitDecl(const Decl &D) { const VarDecl &VD = cast<VarDecl>(D); assert(VD.isLocalVarDecl() && "Should not see file-scope variables inside a function!"); - return EmitVarDecl(VD); + EmitVarDecl(VD); + if (auto *DD = dyn_cast<DecompositionDecl>(&VD)) + for (auto *B : DD->bindings()) + if (auto *HD = B->getHoldingVar()) + EmitVarDecl(*HD); + return; } - case Decl::Binding: - return CGM.ErrorUnsupported(&D, "structured binding"); case Decl::OMPDeclareReduction: return CGM.EmitOMPDeclareReduction(cast<OMPDeclareReductionDecl>(&D), this); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 039936a3a64..89df63d8bf3 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -2206,6 +2206,12 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { if (const auto *FD = dyn_cast<FunctionDecl>(ND)) return EmitFunctionDeclLValue(*this, E, FD); + // FIXME: While we're emitting a binding from an enclosing scope, all other + // DeclRefExprs we see should be implicitly treated as if they also refer to + // an enclosing scope. + if (const auto *BD = dyn_cast<BindingDecl>(ND)) + return EmitLValue(BD->getBinding()); + llvm_unreachable("Unhandled DeclRefExpr"); } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 888af29d403..f20baab784b 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -3772,6 +3772,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { return; case Decl::VarTemplateSpecialization: EmitGlobal(cast<VarDecl>(D)); + if (auto *DD = dyn_cast<DecompositionDecl>(D)) + for (auto *B : DD->bindings()) + if (auto *HD = B->getHoldingVar()) + EmitGlobal(HD); break; // Indirect fields from global anonymous structs and unions can be diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 6193cd0eac2..0dc68769235 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1160,18 +1160,20 @@ static bool checkTupleLikeDecomposition(Sema &S, RefVD->setImplicit(); if (Src->isInlineSpecified()) RefVD->setInlineSpecified(); + RefVD->getLexicalDeclContext()->addHiddenDecl(RefVD); InitializedEntity Entity = InitializedEntity::InitializeBinding(RefVD); InitializationKind Kind = InitializationKind::CreateCopy(Loc, Loc); InitializationSequence Seq(S, Entity, Kind, Init); E = Seq.Perform(S, Entity, Kind, Init); + if (E.isInvalid()) + return true; + E = S.ActOnFinishFullExpr(E.get(), Loc); if (E.isInvalid()) return true; RefVD->setInit(E.get()); RefVD->checkInitIsICE(); - RefVD->getLexicalDeclContext()->addHiddenDecl(RefVD); - E = S.BuildDeclarationNameExpr(CXXScopeSpec(), DeclarationNameInfo(B->getDeclName(), Loc), RefVD); diff --git a/test/CodeGenCXX/cxx1z-decomposition.cpp b/test/CodeGenCXX/cxx1z-decomposition.cpp new file mode 100644 index 00000000000..ee6dbf2ff70 --- /dev/null +++ b/test/CodeGenCXX/cxx1z-decomposition.cpp @@ -0,0 +1,118 @@ +// RUN: %clang_cc1 -std=c++1z -emit-llvm -o - %s | FileCheck %s + +namespace std { + using size_t = decltype(sizeof(0)); + template<typename> struct tuple_size; + template<size_t, typename> struct tuple_element; +} + +struct Y { int n; }; +struct X { X(); X(Y); X(const X&); ~X(); }; + +struct A { int a : 13; bool b; }; + +struct B {}; +template<> struct std::tuple_size<B> { enum { value = 2 }; }; +template<> struct std::tuple_element<0,B> { using type = X; }; +template<> struct std::tuple_element<1,B> { using type = const int&; }; +template<int N> auto get(B) { + if constexpr (N == 0) + return Y(); + else + return 0.0; +} + +using C = int[2]; + +typedef int D __attribute__((ext_vector_type(2))); + +using E = _Complex int; + +template<typename T> T &make(); + +// CHECK: @_ZDC2a12a2E = global {{.*}} zeroinitializer, align 4 +auto [a1, a2] = make<A>(); +// CHECK: @_ZDC2b12b2E = global {{.*}} zeroinitializer, align 1 +// CHECK: @b1 = global {{.*}}* null, align 8 +// CHECK: @_ZGR2b1_ = internal global {{.*}} zeroinitializer, align 1 +// CHECK: @b2 = global i32* null, align 8 +// CHECK: @_ZGR2b2_ = internal global i32 0, align 4 +auto [b1, b2] = make<B>(); +// CHECK: @_ZDC2c12c2E = global [2 x i32]* null, align 8 +auto &[c1, c2] = make<C>(); +// CHECK: @_ZDC2d12d2E = global <2 x i32> zeroinitializer, align 8 +auto [d1, d2] = make<D>(); +// CHECK: @_ZDC2e12e2E = global { i32, i32 } zeroinitializer, align 4 +auto [e1, e2] = make<E>(); + +// CHECK: call {{.*}}* @_Z4makeI1AERT_v() +// CHECK: call {{.*}}memcpy{{.*}}@_ZDC2a12a2E + +// CHECK: @_Z4makeI1BERT_v() +// CHECK: call i32 @_Z3getILi0EEDa1B() +// CHECK: call void @_ZN1XC1E1Y({{.*}}* @_ZGR2b1_, i32 +// CHECK: call i32 @__cxa_atexit({{.*}}@_ZN1XD1Ev{{.*}}@_ZGR2b1_ +// CHECK: store {{.*}}* @_ZGR2b1_, +// +// CHECK: call double @_Z3getILi1EEDa1B() +// CHECK: fptosi double %{{.*}} to i32 +// CHECK: store i32 %{{.*}}, i32* @_ZGR2b2_ +// CHECK: store i32* @_ZGR2b2_, i32** @b2 + +// CHECK: call {{.*}}* @_Z4makeIA2_iERT_v() +// CHECK: store {{.*}}, [2 x i32]** @_ZDC2c12c2E + +// CHECK: call {{.*}}* @_Z4makeIDv2_iERT_v() +// CHECK: store {{.*}}, <2 x i32>* @_ZDC2d12d2E, align 8 + +// CHECK: call {{.*}}* @_Z4makeICiERT_v() +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({ i32, i32 }, { i32, i32 }* @_ZDC2e12e2E, i32 0, i32 0) +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({ i32, i32 }, { i32, i32 }* @_ZDC2e12e2E, i32 0, i32 1) + +// CHECK: define i32 @_Z12test_globalsv() +int test_globals() { + return a2 + b2 + c2 + d2 + e2; + // CHECK: load i8, i8* getelementptr inbounds (%struct.A, %struct.A* @_ZDC2a12a2E, i32 0, i32 1) + // + // CHECK: %[[b2:.*]] = load i32*, i32** @b2 + // CHECK: load i32, i32* %[[b2]] + // + // CHECK: %[[c1c2:.*]] = load [2 x i32]*, [2 x i32]** @_ZDC2c12c2E + // CHECK: %[[c2:.*]] = getelementptr inbounds [2 x i32], [2 x i32]* %[[c1c2]], i64 0, i64 1 + // CHECK: load i32, i32* %[[c2]] + // + // CHECK: %[[d1d2:.*]] = load <2 x i32>, <2 x i32>* @_ZDC2d12d2E + // CHECK: extractelement <2 x i32> %[[d1d2]], i32 1 + // + // CHECK: load i32, i32* getelementptr inbounds ({ i32, i32 }, { i32, i32 }* @_ZDC2e12e2E, i32 0, i32 1) +} + +// CHECK: define i32 @_Z11test_localsv() +int test_locals() { + auto [b1, b2] = make<B>(); + + // CHECK: @_Z4makeI1BERT_v() + // CHECK: call i32 @_Z3getILi0EEDa1B() + // CHECK: call void @_ZN1XC1E1Y({{.*}}* %[[b1:.*]], i32 + // + // CHECK: call double @_Z3getILi1EEDa1B() + // CHECK: %[[cvt:.*]] = fptosi double %{{.*}} to i32 + // CHECK: store i32 %[[cvt]], i32* %[[b2:.*]], + // CHECK: store i32* %[[b2]], i32** %[[b2ref:.*]], + + return b2; + // CHECK: %[[b2:.*]] = load i32*, i32** %[[b2ref]] + // CHECK: load i32, i32* %[[b2]] + + // CHECK: call {{.*}}@_ZN1XD1Ev({{.*}}%[[b1]]) +} + +// CHECK: define void @_Z13test_bitfieldR1A( +void test_bitfield(A &a) { + auto &[a1, a2] = a; + a1 = 5; + // CHECK: load i16, i16* %[[BITFIELD:.*]], + // CHECK: and i16 %{{.*}}, -8192 + // CHECK: or i16 %{{.*}}, 5 + // CHECK: store i16 %{{.*}}, i16* %[[BITFIELD]], +} diff --git a/test/SemaCXX/cxx1z-decomposition.cpp b/test/SemaCXX/cxx1z-decomposition.cpp index 9bb8eeca504..0ddd9ea3046 100644 --- a/test/SemaCXX/cxx1z-decomposition.cpp +++ b/test/SemaCXX/cxx1z-decomposition.cpp @@ -38,4 +38,3 @@ constexpr bool g(S &&s) { static_assert(g({1, 2})); // FIXME: by-value array copies -// FIXME: code generation -- GitLab