1 /*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "slang.h"
18
19 #include <stdlib.h>
20
21 #include <string>
22 #include <vector>
23
24 #include "clang/AST/ASTConsumer.h"
25 #include "clang/AST/ASTContext.h"
26
27 #include "clang/Basic/DiagnosticIDs.h"
28 #include "clang/Basic/FileManager.h"
29 #include "clang/Basic/FileSystemOptions.h"
30 #include "clang/Basic/LangOptions.h"
31 #include "clang/Basic/SourceManager.h"
32 #include "clang/Basic/TargetInfo.h"
33 #include "clang/Basic/TargetOptions.h"
34
35 #include "clang/Frontend/CodeGenOptions.h"
36 #include "clang/Frontend/DiagnosticOptions.h"
37 #include "clang/Frontend/DependencyOutputOptions.h"
38 #include "clang/Frontend/FrontendDiagnostic.h"
39 #include "clang/Frontend/TextDiagnosticPrinter.h"
40 #include "clang/Frontend/Utils.h"
41
42 #include "clang/Lex/Preprocessor.h"
43 #include "clang/Lex/HeaderSearch.h"
44
45 #include "clang/Parse/ParseAST.h"
46
47 #include "llvm/ADT/IntrusiveRefCntPtr.h"
48
49 #include "llvm/Bitcode/ReaderWriter.h"
50
51 // More force linking
52 #include "llvm/Linker.h"
53
54 // Force linking all passes/vmcore stuffs to libslang.so
55 #include "llvm/LinkAllPasses.h"
56 #include "llvm/LinkAllVMCore.h"
57
58 #include "llvm/Support/raw_ostream.h"
59 #include "llvm/Support/MemoryBuffer.h"
60 #include "llvm/Support/ErrorHandling.h"
61 #include "llvm/Support/ManagedStatic.h"
62 #include "llvm/Support/Path.h"
63 #include "llvm/Support/TargetSelect.h"
64 #include "llvm/Support/ToolOutputFile.h"
65
66 #include "slang_assert.h"
67 #include "slang_backend.h"
68 #include "slang_utils.h"
69
70 namespace {
71
72 struct ForceSlangLinking {
ForceSlangLinking__anon9a2efd150111::ForceSlangLinking73 ForceSlangLinking() {
74 // We must reference the functions in such a way that compilers will not
75 // delete it all as dead code, even with whole program optimization,
76 // yet is effectively a NO-OP. As the compiler isn't smart enough
77 // to know that getenv() never returns -1, this will do the job.
78 if (std::getenv("bar") != reinterpret_cast<char*>(-1))
79 return;
80
81 // llvm-rs-link needs following functions existing in libslang.
82 llvm::ParseBitcodeFile(NULL, llvm::getGlobalContext(), NULL);
83 llvm::Linker::LinkModules(NULL, NULL, 0, NULL);
84
85 // llvm-rs-cc need this.
86 new clang::TextDiagnosticPrinter(llvm::errs(),
87 clang::DiagnosticOptions());
88 }
89 } ForceSlangLinking;
90
91 } // namespace
92
93 namespace slang {
94
95 #if defined(__arm__)
96 # define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi"
97 #elif defined(__x86_64__)
98 # define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux"
99 #else
100 // let's use x86 as default target
101 # define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux"
102 #endif
103
104 bool Slang::GlobalInitialized = false;
105
106 // Language option (define the language feature for compiler such as C99)
107 clang::LangOptions Slang::LangOpts;
108
109 // Code generation option for the compiler
110 clang::CodeGenOptions Slang::CodeGenOpts;
111
112 // The named of metadata node that pragma resides (should be synced with
113 // bcc.cpp)
114 const llvm::StringRef Slang::PragmaMetadataName = "#pragma";
115
116 static inline llvm::tool_output_file *
OpenOutputFile(const char * OutputFile,unsigned Flags,std::string * Error,clang::DiagnosticsEngine * DiagEngine)117 OpenOutputFile(const char *OutputFile,
118 unsigned Flags,
119 std::string* Error,
120 clang::DiagnosticsEngine *DiagEngine) {
121 slangAssert((OutputFile != NULL) && (Error != NULL) &&
122 (DiagEngine != NULL) && "Invalid parameter!");
123
124 if (SlangUtils::CreateDirectoryWithParents(
125 llvm::sys::path::parent_path(OutputFile), Error)) {
126 llvm::tool_output_file *F =
127 new llvm::tool_output_file(OutputFile, *Error, Flags);
128 if (F != NULL)
129 return F;
130 }
131
132 // Report error here.
133 DiagEngine->Report(clang::diag::err_fe_error_opening)
134 << OutputFile << *Error;
135
136 return NULL;
137 }
138
GlobalInitialization()139 void Slang::GlobalInitialization() {
140 if (!GlobalInitialized) {
141 // We only support x86, x64 and ARM target
142
143 // For ARM
144 LLVMInitializeARMTargetInfo();
145 LLVMInitializeARMTarget();
146 LLVMInitializeARMAsmPrinter();
147
148 // For x86 and x64
149 LLVMInitializeX86TargetInfo();
150 LLVMInitializeX86Target();
151 LLVMInitializeX86AsmPrinter();
152
153 // Please refer to include/clang/Basic/LangOptions.h to setup
154 // the options.
155 LangOpts.RTTI = 0; // Turn off the RTTI information support
156 LangOpts.NeXTRuntime = 0; // Turn off the NeXT runtime uses
157 LangOpts.C99 = 1;
158 LangOpts.CharIsSigned = 1; // Signed char is our default.
159
160 CodeGenOpts.OptimizationLevel = 3;
161
162 GlobalInitialized = true;
163 }
164 }
165
LLVMErrorHandler(void * UserData,const std::string & Message)166 void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
167 clang::DiagnosticsEngine* DiagEngine =
168 static_cast<clang::DiagnosticsEngine *>(UserData);
169
170 DiagEngine->Report(clang::diag::err_fe_error_backend) << Message;
171 exit(1);
172 }
173
createTarget(const std::string & Triple,const std::string & CPU,const std::vector<std::string> & Features)174 void Slang::createTarget(const std::string &Triple, const std::string &CPU,
175 const std::vector<std::string> &Features) {
176 if (!Triple.empty())
177 mTargetOpts.Triple = Triple;
178 else
179 mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING;
180
181 if (!CPU.empty())
182 mTargetOpts.CPU = CPU;
183
184 if (!Features.empty())
185 mTargetOpts.Features = Features;
186
187 mTarget.reset(clang::TargetInfo::CreateTargetInfo(*mDiagEngine,
188 mTargetOpts));
189 }
190
createFileManager()191 void Slang::createFileManager() {
192 mFileSysOpt.reset(new clang::FileSystemOptions());
193 mFileMgr.reset(new clang::FileManager(*mFileSysOpt));
194 }
195
createSourceManager()196 void Slang::createSourceManager() {
197 mSourceMgr.reset(new clang::SourceManager(*mDiagEngine, *mFileMgr));
198 }
199
createPreprocessor()200 void Slang::createPreprocessor() {
201 // Default only search header file in current dir
202 clang::HeaderSearch *HeaderInfo = new clang::HeaderSearch(*mFileMgr,
203 *mDiagEngine,
204 LangOpts,
205 mTarget.get());
206
207 mPP.reset(new clang::Preprocessor(*mDiagEngine,
208 LangOpts,
209 mTarget.get(),
210 *mSourceMgr,
211 *HeaderInfo,
212 *this,
213 NULL,
214 /* OwnsHeaderSearch = */true));
215 // Initialize the preprocessor
216 mPragmas.clear();
217 mPP->AddPragmaHandler(new PragmaRecorder(&mPragmas));
218
219 std::vector<clang::DirectoryLookup> SearchList;
220 for (unsigned i = 0, e = mIncludePaths.size(); i != e; i++) {
221 if (const clang::DirectoryEntry *DE =
222 mFileMgr->getDirectory(mIncludePaths[i])) {
223 SearchList.push_back(clang::DirectoryLookup(DE,
224 clang::SrcMgr::C_System,
225 false,
226 false));
227 }
228 }
229
230 HeaderInfo->SetSearchPaths(SearchList,
231 /* angledDirIdx = */1,
232 /* systemDixIdx = */1,
233 /* noCurDirSearch = */false);
234
235 initPreprocessor();
236 }
237
createASTContext()238 void Slang::createASTContext() {
239 mASTContext.reset(new clang::ASTContext(LangOpts,
240 *mSourceMgr,
241 mTarget.get(),
242 mPP->getIdentifierTable(),
243 mPP->getSelectorTable(),
244 mPP->getBuiltinInfo(),
245 /* size_reserve = */0));
246 initASTContext();
247 }
248
249 clang::ASTConsumer *
createBackend(const clang::CodeGenOptions & CodeGenOpts,llvm::raw_ostream * OS,OutputType OT)250 Slang::createBackend(const clang::CodeGenOptions& CodeGenOpts,
251 llvm::raw_ostream *OS, OutputType OT) {
252 return new Backend(mDiagEngine, CodeGenOpts, mTargetOpts,
253 &mPragmas, OS, OT);
254 }
255
Slang()256 Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
257 GlobalInitialization();
258 }
259
init(const std::string & Triple,const std::string & CPU,const std::vector<std::string> & Features,clang::DiagnosticsEngine * DiagEngine,DiagnosticBuffer * DiagClient)260 void Slang::init(const std::string &Triple, const std::string &CPU,
261 const std::vector<std::string> &Features,
262 clang::DiagnosticsEngine *DiagEngine,
263 DiagnosticBuffer *DiagClient) {
264 if (mInitialized)
265 return;
266
267 mDiagEngine = DiagEngine;
268 mDiagClient = DiagClient;
269 mDiag.reset(new clang::Diagnostic(mDiagEngine));
270 initDiagnostic();
271 llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagEngine);
272
273 createTarget(Triple, CPU, Features);
274 createFileManager();
275 createSourceManager();
276
277 mInitialized = true;
278 }
279
loadModule(clang::SourceLocation ImportLoc,clang::ModuleIdPath Path,clang::Module::NameVisibilityKind Visibility,bool IsInclusionDirective)280 clang::Module *Slang::loadModule(clang::SourceLocation ImportLoc,
281 clang::ModuleIdPath Path,
282 clang::Module::NameVisibilityKind Visibility,
283 bool IsInclusionDirective) {
284 slangAssert(0 && "Not implemented");
285 return NULL;
286 }
287
setInputSource(llvm::StringRef InputFile,const char * Text,size_t TextLength)288 bool Slang::setInputSource(llvm::StringRef InputFile,
289 const char *Text,
290 size_t TextLength) {
291 mInputFileName = InputFile.str();
292
293 // Reset the ID tables if we are reusing the SourceManager
294 mSourceMgr->clearIDTables();
295
296 // Load the source
297 llvm::MemoryBuffer *SB =
298 llvm::MemoryBuffer::getMemBuffer(Text, Text + TextLength);
299 mSourceMgr->createMainFileIDForMemBuffer(SB);
300
301 if (mSourceMgr->getMainFileID().isInvalid()) {
302 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
303 return false;
304 }
305 return true;
306 }
307
setInputSource(llvm::StringRef InputFile)308 bool Slang::setInputSource(llvm::StringRef InputFile) {
309 mInputFileName = InputFile.str();
310
311 mSourceMgr->clearIDTables();
312
313 const clang::FileEntry *File = mFileMgr->getFile(InputFile);
314 if (File)
315 mSourceMgr->createMainFileID(File);
316
317 if (mSourceMgr->getMainFileID().isInvalid()) {
318 mDiagEngine->Report(clang::diag::err_fe_error_reading) << InputFile;
319 return false;
320 }
321
322 return true;
323 }
324
setOutput(const char * OutputFile)325 bool Slang::setOutput(const char *OutputFile) {
326 llvm::sys::Path OutputFilePath(OutputFile);
327 std::string Error;
328 llvm::tool_output_file *OS = NULL;
329
330 switch (mOT) {
331 case OT_Dependency:
332 case OT_Assembly:
333 case OT_LLVMAssembly: {
334 OS = OpenOutputFile(OutputFile, 0, &Error, mDiagEngine);
335 break;
336 }
337 case OT_Nothing: {
338 break;
339 }
340 case OT_Object:
341 case OT_Bitcode: {
342 OS = OpenOutputFile(OutputFile, llvm::raw_fd_ostream::F_Binary,
343 &Error, mDiagEngine);
344 break;
345 }
346 default: {
347 llvm_unreachable("Unknown compiler output type");
348 }
349 }
350
351 if (!Error.empty())
352 return false;
353
354 mOS.reset(OS);
355
356 mOutputFileName = OutputFile;
357
358 return true;
359 }
360
setDepOutput(const char * OutputFile)361 bool Slang::setDepOutput(const char *OutputFile) {
362 llvm::sys::Path OutputFilePath(OutputFile);
363 std::string Error;
364
365 mDOS.reset(OpenOutputFile(OutputFile, 0, &Error, mDiagEngine));
366 if (!Error.empty() || (mDOS.get() == NULL))
367 return false;
368
369 mDepOutputFileName = OutputFile;
370
371 return true;
372 }
373
generateDepFile()374 int Slang::generateDepFile() {
375 if (mDiagEngine->hasErrorOccurred())
376 return 1;
377 if (mDOS.get() == NULL)
378 return 1;
379
380 // Initialize options for generating dependency file
381 clang::DependencyOutputOptions DepOpts;
382 DepOpts.IncludeSystemHeaders = 1;
383 DepOpts.OutputFile = mDepOutputFileName;
384 DepOpts.Targets = mAdditionalDepTargets;
385 DepOpts.Targets.push_back(mDepTargetBCFileName);
386 for (std::vector<std::string>::const_iterator
387 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
388 I != E;
389 I++) {
390 DepOpts.Targets.push_back(*I);
391 }
392 mGeneratedFileNames.clear();
393
394 // Per-compilation needed initialization
395 createPreprocessor();
396 AttachDependencyFileGen(*mPP.get(), DepOpts);
397
398 // Inform the diagnostic client we are processing a source file
399 mDiagClient->BeginSourceFile(LangOpts, mPP.get());
400
401 // Go through the source file (no operations necessary)
402 clang::Token Tok;
403 mPP->EnterMainSourceFile();
404 do {
405 mPP->Lex(Tok);
406 } while (Tok.isNot(clang::tok::eof));
407
408 mPP->EndSourceFile();
409
410 // Declare success if no error
411 if (!mDiagEngine->hasErrorOccurred())
412 mDOS->keep();
413
414 // Clean up after compilation
415 mPP.reset();
416 mDOS.reset();
417
418 return mDiagEngine->hasErrorOccurred() ? 1 : 0;
419 }
420
compile()421 int Slang::compile() {
422 if (mDiagEngine->hasErrorOccurred())
423 return 1;
424 if (mOS.get() == NULL)
425 return 1;
426
427 // Here is per-compilation needed initialization
428 createPreprocessor();
429 createASTContext();
430
431 mBackend.reset(createBackend(CodeGenOpts, &mOS->os(), mOT));
432
433 // Inform the diagnostic client we are processing a source file
434 mDiagClient->BeginSourceFile(LangOpts, mPP.get());
435
436 // The core of the slang compiler
437 ParseAST(*mPP, mBackend.get(), *mASTContext);
438
439 // Inform the diagnostic client we are done with previous source file
440 mDiagClient->EndSourceFile();
441
442 // Declare success if no error
443 if (!mDiagEngine->hasErrorOccurred())
444 mOS->keep();
445
446 // The compilation ended, clear
447 mBackend.reset();
448 mASTContext.reset();
449 mPP.reset();
450 mOS.reset();
451
452 return mDiagEngine->hasErrorOccurred() ? 1 : 0;
453 }
454
setDebugMetadataEmission(bool EmitDebug)455 void Slang::setDebugMetadataEmission(bool EmitDebug) {
456 CodeGenOpts.DebugInfo = EmitDebug;
457 }
458
setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel)459 void Slang::setOptimizationLevel(llvm::CodeGenOpt::Level OptimizationLevel) {
460 CodeGenOpts.OptimizationLevel = OptimizationLevel;
461 }
462
reset()463 void Slang::reset() {
464 llvm::errs() << mDiagClient->str();
465 mDiagEngine->Reset();
466 mDiagClient->reset();
467 }
468
~Slang()469 Slang::~Slang() {
470 llvm::llvm_shutdown();
471 }
472
473 } // namespace slang
474