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