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