Skip to content

Commit c0d6f85

Browse files
committed
[C++20] [Modules] Serialize the evaluated constant values for VarDecl
Close #62796. Previously, we didn't serialize the evaluated result for VarDecl. This caused the compilation of template metaprogramming become slower than expect. This patch fixes the issue.
1 parent c8466ab commit c0d6f85

File tree

4 files changed

+154
-2
lines changed

4 files changed

+154
-2
lines changed

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,10 @@ void ASTDeclReader::ReadVarDeclInit(VarDecl *VD) {
16591659
EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
16601660
Eval->HasConstantInitialization = (Val & 2) != 0;
16611661
Eval->HasConstantDestruction = (Val & 4) != 0;
1662+
Eval->WasEvaluated = (Val & 8) != 0;
1663+
if (Eval->WasEvaluated)
1664+
Eval->Evaluated = Record.readAPValue();
1665+
16621666
// Store the offset of the initializer. Don't deserialize it yet: it might
16631667
// not be needed, and might refer back to the variable, for example if it
16641668
// contains a lambda.

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5987,13 +5987,20 @@ void ASTRecordWriter::AddVarDeclInit(const VarDecl *VD) {
59875987
return;
59885988
}
59895989

5990-
unsigned Val = 1;
5990+
uint64_t Val = 1;
59915991
if (EvaluatedStmt *ES = VD->getEvaluatedStmt()) {
59925992
Val |= (ES->HasConstantInitialization ? 2 : 0);
59935993
Val |= (ES->HasConstantDestruction ? 4 : 0);
5994-
// FIXME: Also emit the constant initializer value.
5994+
APValue *Evaluated = VD->getEvaluatedValue();
5995+
// If the evaluted result is constant, emit it.
5996+
if (Evaluated && (Evaluated->isInt() || Evaluated->isFloat()))
5997+
Val |= 8;
59955998
}
59965999
push_back(Val);
6000+
if (Val & 8) {
6001+
AddAPValue(*VD->getEvaluatedValue());
6002+
}
6003+
59976004
writeStmtRef(Init);
59986005
}
59996006

clang/unittests/Serialization/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_clang_unittest(SerializationTests
88
InMemoryModuleCacheTest.cpp
99
ModuleCacheTest.cpp
1010
SourceLocationEncodingTest.cpp
11+
VarDeclConstantInitTest.cpp
1112
)
1213

1314
clang_target_link_libraries(SerializationTests
@@ -18,4 +19,5 @@ clang_target_link_libraries(SerializationTests
1819
clangLex
1920
clangSema
2021
clangSerialization
22+
clangTooling
2123
)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//===- unittests/Serialization/VarDeclConstantInitTest.cpp - CI tests -----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/ASTMatchers/ASTMatchFinder.h"
10+
#include "clang/ASTMatchers/ASTMatchers.h"
11+
#include "clang/Basic/FileManager.h"
12+
#include "clang/Frontend/CompilerInstance.h"
13+
#include "clang/Frontend/CompilerInvocation.h"
14+
#include "clang/Frontend/FrontendActions.h"
15+
#include "clang/Frontend/Utils.h"
16+
#include "clang/Lex/HeaderSearch.h"
17+
#include "clang/Tooling/Tooling.h"
18+
#include "llvm/ADT/SmallString.h"
19+
#include "llvm/Support/FileSystem.h"
20+
#include "llvm/Support/raw_ostream.h"
21+
22+
#include "gtest/gtest.h"
23+
24+
using namespace llvm;
25+
using namespace clang;
26+
27+
namespace {
28+
29+
class VarDeclConstantInitTest : public ::testing::Test {
30+
void SetUp() override {
31+
ASSERT_FALSE(sys::fs::createUniqueDirectory("modules-test", TestDir));
32+
}
33+
34+
void TearDown() override { sys::fs::remove_directories(TestDir); }
35+
36+
public:
37+
SmallString<256> TestDir;
38+
39+
void addFile(StringRef Path, StringRef Contents) {
40+
ASSERT_FALSE(sys::path::is_absolute(Path));
41+
42+
SmallString<256> AbsPath(TestDir);
43+
sys::path::append(AbsPath, Path);
44+
45+
ASSERT_FALSE(
46+
sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
47+
48+
std::error_code EC;
49+
llvm::raw_fd_ostream OS(AbsPath, EC);
50+
ASSERT_FALSE(EC);
51+
OS << Contents;
52+
}
53+
};
54+
55+
TEST_F(VarDeclConstantInitTest, CachedConstantInit) {
56+
addFile("Cached.cppm", R"cpp(
57+
export module Fibonacci.Cache;
58+
59+
export namespace Fibonacci
60+
{
61+
constexpr unsigned long Recursive(unsigned long n)
62+
{
63+
if (n == 0)
64+
return 0;
65+
if (n == 1)
66+
return 1;
67+
return Recursive(n - 2) + Recursive(n - 1);
68+
}
69+
70+
template<unsigned long N>
71+
struct Number{};
72+
73+
struct DefaultStrategy
74+
{
75+
constexpr unsigned long operator()(unsigned long n, auto... other) const
76+
{
77+
return (n + ... + other);
78+
}
79+
};
80+
81+
constexpr unsigned long Compute(Number<10ul>, auto strategy)
82+
{
83+
return strategy(Recursive(10ul));
84+
}
85+
86+
template<unsigned long N, typename Strategy = DefaultStrategy>
87+
constexpr unsigned long Cache = Compute(Number<N>{}, Strategy{});
88+
89+
template constexpr unsigned long Cache<10ul>;
90+
}
91+
)cpp");
92+
93+
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
94+
CompilerInstance::createDiagnostics(new DiagnosticOptions());
95+
CreateInvocationOptions CIOpts;
96+
CIOpts.Diags = Diags;
97+
98+
llvm::Twine CacheBMIPath = TestDir + "/Cached.pcm";
99+
const char *Args[] = {"clang++",
100+
"-std=c++20",
101+
"--precompile",
102+
"-working-directory",
103+
TestDir.c_str(),
104+
"Cached.cppm",
105+
"-o",
106+
CacheBMIPath.str().c_str()};
107+
std::shared_ptr<CompilerInvocation> Invocation =
108+
createInvocation(Args, CIOpts);
109+
ASSERT_TRUE(Invocation);
110+
111+
CompilerInstance Instance;
112+
Instance.setDiagnostics(Diags.get());
113+
Instance.setInvocation(Invocation);
114+
GenerateModuleInterfaceAction Action;
115+
ASSERT_TRUE(Instance.ExecuteAction(Action));
116+
ASSERT_FALSE(Diags->hasErrorOccurred());
117+
118+
llvm::Twine DepArg = "-fmodule-file=Fibonacci.Cache=" + CacheBMIPath;
119+
std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs(
120+
R"cpp(
121+
import Fibonacci.Cache;
122+
)cpp",
123+
/*Args=*/{"-std=c++20", DepArg.str().c_str()});
124+
125+
using namespace clang::ast_matchers;
126+
ASTContext &Ctx = AST->getASTContext();
127+
const auto *cached = selectFirst<VarDecl>(
128+
"Cache",
129+
match(varDecl(isTemplateInstantiation(), hasName("Cache")).bind("Cache"),
130+
Ctx));
131+
EXPECT_TRUE(cached);
132+
EXPECT_TRUE(cached->getEvaluatedStmt());
133+
EXPECT_TRUE(cached->getEvaluatedStmt()->WasEvaluated);
134+
EXPECT_TRUE(cached->getEvaluatedValue());
135+
EXPECT_TRUE(cached->getEvaluatedValue()->isInt());
136+
EXPECT_EQ(cached->getEvaluatedValue()->getInt(), 55);
137+
}
138+
139+
} // anonymous namespace

0 commit comments

Comments
 (0)