Clang Project

clang_source_code/unittests/CodeGen/IncrementalProcessingTest.cpp
1//=== unittests/CodeGen/IncrementalProcessingTest.cpp - IncrementalCodeGen ===//
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/AST/ASTConsumer.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/RecursiveASTVisitor.h"
12#include "clang/Basic/TargetInfo.h"
13#include "clang/CodeGen/ModuleBuilder.h"
14#include "clang/Frontend/CompilerInstance.h"
15#include "clang/Lex/Preprocessor.h"
16#include "clang/Parse/Parser.h"
17#include "clang/Sema/Sema.h"
18#include "llvm/ADT/Triple.h"
19#include "llvm/IR/LLVMContext.h"
20#include "llvm/IR/Module.h"
21#include "llvm/Support/Host.h"
22#include "llvm/Support/MemoryBuffer.h"
23#include "gtest/gtest.h"
24
25#include <memory>
26
27using namespace llvm;
28using namespace clang;
29
30namespace {
31
32// Incremental processing produces several modules, all using the same "main
33// file". Make sure CodeGen can cope with that, e.g. for static initializers.
34const char TestProgram1[] =
35    "extern \"C\" int funcForProg1() { return 17; }\n"
36    "struct EmitCXXGlobalInitFunc1 {\n"
37    "   EmitCXXGlobalInitFunc1() {}\n"
38    "} test1;";
39
40const char TestProgram2[] =
41    "extern \"C\" int funcForProg2() { return 42; }\n"
42    "struct EmitCXXGlobalInitFunc2 {\n"
43    "   EmitCXXGlobalInitFunc2() {}\n"
44    "} test2;";
45
46
47/// An incremental version of ParseAST().
48static std::unique_ptr<llvm::Module>
49IncrementalParseAST(CompilerInstanceCIParserP,
50                    CodeGeneratorCGconst charcode) {
51  static int counter = 0;
52  struct IncreaseCounterOnRet {
53    ~IncreaseCounterOnRet() {
54      ++counter;
55    }
56  } ICOR;
57
58  SemaS = CI.getSema();
59  clang::SourceManager &SM = S.getSourceManager();
60  if (!code) {
61    // Main file
62    SM.setMainFileID(SM.createFileID(
63        llvm::MemoryBuffer::getMemBuffer("    "), clang::SrcMgr::C_User));
64
65    S.getPreprocessor().EnterMainSourceFile();
66    P.Initialize();
67  } else {
68    FileID FID = SM.createFileID(
69        llvm::MemoryBuffer::getMemBuffer(code), clang::SrcMgr::C_User);
70    SourceLocation MainStartLoc = SM.getLocForStartOfFile(SM.getMainFileID());
71    SourceLocation InclLoc = MainStartLoc.getLocWithOffset(counter);
72    S.getPreprocessor().EnterSourceFile(FID0InclLoc);
73  }
74
75  ExternalASTSource *External = S.getASTContext().getExternalSource();
76  if (External)
77    External->StartTranslationUnit(&CG);
78
79  Parser::DeclGroupPtrTy ADecl;
80  for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF;
81       AtEOF = P.ParseTopLevelDecl(ADecl)) {
82    // If we got a null return and something *was* parsed, ignore it.  This
83    // is due to a top-level semicolon, an action override, or a parse error
84    // skipping something.
85    if (ADecl && !CG.HandleTopLevelDecl(ADecl.get()))
86      return nullptr;
87  }
88
89  // Process any TopLevelDecls generated by #pragma weak.
90  for (Decl *D : S.WeakTopLevelDecls())
91    CG.HandleTopLevelDecl(DeclGroupRef(D));
92
93  CG.HandleTranslationUnit(S.getASTContext());
94
95  std::unique_ptr<llvm::ModuleM(CG.ReleaseModule());
96  // Switch to next module.
97  CG.StartModule("incremental-module-" + std::to_string(counter),
98                 M->getContext());
99  return M;
100}
101
102const Function* getGlobalInit(llvm::Module& M) {
103  for (const auto& Func: M)
104    if (Func.hasName() && Func.getName().startswith("_GLOBAL__sub_I_"))
105      return &Func;
106
107  return nullptr;
108}
109
110TEST(IncrementalProcessing, EmitCXXGlobalInitFunc) {
111    LLVMContext Context;
112    CompilerInstance compiler;
113
114    compiler.createDiagnostics();
115    compiler.getLangOpts().CPlusPlus = 1;
116    compiler.getLangOpts().CPlusPlus11 = 1;
117
118    compiler.getTargetOpts().Triple = llvm::Triple::normalize(
119        llvm::sys::getProcessTriple());
120    compiler.setTarget(clang::TargetInfo::CreateTargetInfo(
121      compiler.getDiagnostics(),
122      std::make_shared<clang::TargetOptions>(
123        compiler.getTargetOpts())));
124
125    compiler.createFileManager();
126    compiler.createSourceManager(compiler.getFileManager());
127    compiler.createPreprocessor(clang::TU_Prefix);
128    compiler.getPreprocessor().enableIncrementalProcessing();
129
130    compiler.createASTContext();
131
132    CodeGeneratorCG =
133        CreateLLVMCodeGen(
134            compiler.getDiagnostics(),
135            "main-module",
136            compiler.getHeaderSearchOpts(),
137            compiler.getPreprocessorOpts(),
138            compiler.getCodeGenOpts(),
139            Context);
140    compiler.setASTConsumer(std::unique_ptr<ASTConsumer>(CG));
141    compiler.createSema(clang::TU_Prefixnullptr);
142    SemaS = compiler.getSema();
143
144    std::unique_ptr<ParserParseOP(new Parser(S.getPreprocessor(), S,
145                                               /*SkipFunctionBodies*/ false));
146    Parser &P = *ParseOP.get();
147
148    std::array<std::unique_ptr<llvm::Module>, 3M;
149    M[0] = IncrementalParseAST(compilerP*CGnullptr);
150    ASSERT_TRUE(M[0]);
151
152    M[1] = IncrementalParseAST(compilerP*CGTestProgram1);
153    ASSERT_TRUE(M[1]);
154    ASSERT_TRUE(M[1]->getFunction("funcForProg1"));
155
156    M[2] = IncrementalParseAST(compilerP*CGTestProgram2);
157    ASSERT_TRUE(M[2]);
158    ASSERT_TRUE(M[2]->getFunction("funcForProg2"));
159    // First code should not end up in second module:
160    ASSERT_FALSE(M[2]->getFunction("funcForProg1"));
161
162    // Make sure global inits exist and are unique:
163    const Function* GlobalInit1 = getGlobalInit(*M[1]);
164    ASSERT_TRUE(GlobalInit1);
165
166    const Function* GlobalInit2 = getGlobalInit(*M[2]);
167    ASSERT_TRUE(GlobalInit2);
168
169    ASSERT_FALSE(GlobalInit1->getName() == GlobalInit2->getName());
170
171}
172
173// end anonymous namespace
174