slang.cpp revision 9089828a995422a5ed3beeed0890b755c4abe188
1#include "slang.hpp"
2
3#include <stdlib.h>                     /* for getenv */
4
5#include "libslang.h"
6
7#include "llvm/ADT/Twine.h"     /* for class llvm::Twine */
8
9#include "llvm/Target/TargetSelect.h"       /* for function LLVMInitialize[ARM|X86][TargetInfo|Target|AsmPrinter]() */
10
11#include "llvm/Support/MemoryBuffer.h"      /* for class llvm::MemoryBuffer */
12#include "llvm/Support/ErrorHandling.h"     /* for function llvm::install_fatal_error_handler() */
13#include "llvm/Support/ManagedStatic.h"     /* for class llvm::llvm_shutdown */
14
15#include "clang/Basic/TargetInfo.h"     /* for class clang::TargetInfo */
16#include "clang/Basic/LangOptions.h"    /* for class clang::LangOptions */
17#include "clang/Basic/TargetOptions.h"  /* for class clang::TargetOptions */
18
19#include "clang/Frontend/FrontendDiagnostic.h"      /* for clang::diag::* */
20
21#include "clang/Sema/ParseAST.h"        /* for function clang::ParseAST() */
22
23#if defined(__arm__)
24#   define DEFAULT_TARGET_TRIPLE_STRING "armv7-none-linux-gnueabi"
25#elif defined(__x86_64__)
26#   define DEFAULT_TARGET_TRIPLE_STRING "x86_64-unknown-linux"
27#else
28#   define DEFAULT_TARGET_TRIPLE_STRING "i686-unknown-linux"    // let's use x86 as default target
29#endif
30
31namespace slang {
32
33bool Slang::GlobalInitialized = false;
34
35/* Language option (define the language feature for compiler such as C99) */
36LangOptions Slang::LangOpts;
37
38/* Code generation option for the compiler */
39CodeGenOptions Slang::CodeGenOpts;
40
41const std::string Slang::TargetDescription = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n32";
42
43/* The named of metadata node that pragma resides (should be synced with bcc.cpp) */
44const llvm::Twine Slang::PragmaMetadataName = "#pragma";
45
46void Slang::GlobalInitialization() {
47    if(!GlobalInitialized) {
48        /* We only support x86, x64 and ARM target */
49
50        /* For ARM */
51        LLVMInitializeARMTargetInfo();
52        LLVMInitializeARMTarget();
53        LLVMInitializeARMAsmPrinter();
54
55        /* For x86 and x64 */
56        LLVMInitializeX86TargetInfo();
57        LLVMInitializeX86Target();
58        LLVMInitializeX86AsmPrinter();
59
60        /* Please refer to clang/include/clang/Basic/LangOptions.h for setting up the options */
61        LangOpts.RTTI = 0;  /* turn off the RTTI information support */
62        LangOpts.NeXTRuntime = 0;   /* turn off the NeXT runtime uses */
63        LangOpts.Bool = 1;  /* turn on 'bool', 'true', 'false' keywords. */
64
65        CodeGenOpts.OptimizationLevel = 3;  /* -O3 */
66
67        GlobalInitialized = true;
68    }
69
70    return;
71}
72
73void Slang::LLVMErrorHandler(void *UserData, const std::string &Message) {
74    Diagnostic* Diags = static_cast<Diagnostic*>(UserData);
75    Diags->Report(clang::diag::err_fe_error_backend) << Message;
76    exit(1);
77}
78
79void Slang::createTarget(const char* Triple, const char* CPU, const char** Features) {
80    if(Triple != NULL)
81        mTargetOpts.Triple = Triple;
82    else
83        mTargetOpts.Triple = DEFAULT_TARGET_TRIPLE_STRING;
84
85    if(CPU != NULL)
86        mTargetOpts.CPU = CPU;
87
88    mTarget.reset(TargetInfo::CreateTargetInfo(*mDiagnostics, mTargetOpts));
89
90    if(Features != NULL)
91        for(int i=0;Features[i]!=NULL;i++)
92            mTargetOpts.Features.push_back(Features[i]);
93
94    return;
95}
96
97void Slang::createPreprocessor() {
98  HeaderSearch* HS = new HeaderSearch(*mFileMgr); /* Default only search header file in current dir */
99
100  mPP.reset(new Preprocessor( *mDiagnostics,
101                              LangOpts,
102                              *mTarget,
103                              *mSourceMgr,
104                              *HS,
105                              NULL,
106                              true /* OwnsHeaderSearch */));
107  /* Initialize the prepocessor */
108  mPragmas.clear();
109  mPP->AddPragmaHandler(NULL, new PragmaRecorder(mPragmas));
110
111  /* Like ApplyHeaderSearchOptions in InitHeaderSearch.cpp */
112  const char *inclDir = getenv("ANDROID_BUILD_TOP");
113  std::vector<DirectoryLookup> SearchList;
114  if (inclDir) {
115    char *dirPath = new char[strlen(inclDir) + 33];
116    strcpy(dirPath, inclDir);
117    strcpy(dirPath + strlen(inclDir), "/frameworks/base/libs/rs/scriptc");
118
119    if (const DirectoryEntry *DE = mFileMgr->getDirectory(dirPath, dirPath + strlen(dirPath))) {
120      SearchList.push_back(DirectoryLookup(DE, SrcMgr::C_System, false, false));
121    }
122  }
123
124  int siz = 256;
125  char *currDir = new char[siz];
126  while (!getcwd(currDir, siz)) {
127    siz *= 2;
128  }
129
130  if (siz - strlen(currDir) >= 33) {
131    strcpy(currDir + strlen(currDir), "/frameworks/base/libs/rs/scriptc");
132  } else {
133    char *tmp = new char[strlen(currDir) + 33];
134    strcpy(tmp, currDir);
135    strcpy(tmp + strlen(currDir), "/frameworks/base/libs/rs/scriptc");
136    currDir = tmp;
137  }
138
139  if (const DirectoryEntry *DE = mFileMgr->getDirectory(currDir, currDir + strlen(currDir))) {
140    SearchList.push_back(DirectoryLookup(DE, SrcMgr::C_System, false, false));
141  }
142
143  HS->SetSearchPaths(SearchList, 1, false);
144
145  return;
146}
147
148Slang::Slang(const char* Triple, const char* CPU, const char** Features) :
149    mOutputType(SlangCompilerOutput_Default),
150    mAllowRSPrefix(false)
151{
152    GlobalInitialization();
153
154    createDiagnostic();
155    llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.get());
156
157    createTarget(Triple, CPU, Features);
158    createFileManager();
159    createSourceManager();
160
161    return;
162}
163
164bool Slang::setInputSource(llvm::StringRef inputFile, const char* text, size_t textLength) {
165    mInputFileName = inputFile.str();
166
167    /* Reset the ID tables if we are reusing the SourceManager */
168    mSourceMgr->clearIDTables();
169
170    /* Load the source */
171    llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getMemBuffer(text, text + textLength);
172    mSourceMgr->createMainFileIDForMemBuffer(SB);
173
174    if(mSourceMgr->getMainFileID().isInvalid()) {
175        mDiagnostics->Report(clang::diag::err_fe_error_reading) << inputFile;
176        return false;
177    }
178
179    return true;
180}
181
182bool Slang::setInputSource(llvm::StringRef inputFile) {
183    mInputFileName = inputFile.str();
184
185    mSourceMgr->clearIDTables();
186
187    const FileEntry* File = mFileMgr->getFile(inputFile);
188    if(File)
189        mSourceMgr->createMainFileID(File, SourceLocation());
190
191    if(mSourceMgr->getMainFileID().isInvalid()) {
192        mDiagnostics->Report(clang::diag::err_fe_error_reading) << inputFile;
193        return false;
194    }
195
196    return true;
197}
198
199void Slang::setOutputType(SlangCompilerOutputTy outputType) {
200    mOutputType = outputType;
201    if( mOutputType != SlangCompilerOutput_Assembly &&
202        mOutputType != SlangCompilerOutput_LL &&
203        mOutputType != SlangCompilerOutput_Bitcode &&
204        mOutputType != SlangCompilerOutput_Nothing &&
205        mOutputType != SlangCompilerOutput_Obj)
206        mOutputType = SlangCompilerOutput_Default;
207    return;
208}
209
210static void _mkdir_given_a_file(const char *file) {
211    char buf[256];
212    char *tmp, *p = NULL;
213    size_t len = strlen(file);
214
215    if (len + 1 <= sizeof(buf))
216        tmp = buf;
217    else
218        tmp = new char [len + 1];
219
220    strcpy(tmp, file);
221
222    if (tmp[len - 1] == '/')
223        tmp[len - 1] = 0;
224
225    for (p = tmp + 1; *p; p++) {
226        if (*p == '/') {
227            *p = 0;
228            mkdir(tmp, S_IRWXU);
229            *p = '/';
230        }
231    }
232
233    if (tmp != buf)
234        delete[] tmp;
235}
236
237bool Slang::setOutput(const char* outputFile) {
238    std::string Error;
239
240    _mkdir_given_a_file(outputFile);
241
242    switch(mOutputType) {
243        case SlangCompilerOutput_Assembly:
244        case SlangCompilerOutput_LL:
245            mOS.reset( new llvm::raw_fd_ostream(outputFile, Error, 0) );
246        break;
247
248        case SlangCompilerOutput_Nothing:
249            mOS.reset();
250        break;
251
252        case SlangCompilerOutput_Obj:
253        case SlangCompilerOutput_Bitcode:
254        default:
255            mOS.reset( new llvm::raw_fd_ostream(outputFile, Error, llvm::raw_fd_ostream::F_Binary) );
256        break;
257    }
258
259    if(!Error.empty()) {
260        mOS.reset();
261        mDiagnostics->Report(clang::diag::err_fe_error_opening) << outputFile << Error;
262        return false;
263    }
264
265    mOutputFileName = outputFile;
266
267    return true;
268}
269
270int Slang::compile() {
271    if((mDiagnostics->getNumErrors() > 0) || (mOS.get() == NULL))
272        return mDiagnostics->getNumErrors();
273
274    /* Here is per-compilation needed initialization */
275    createPreprocessor();
276    createASTContext();
277    createRSContext();
278    //createBackend();
279    createRSBackend();
280
281    /* Inform the diagnostic client we are processing a source file */
282    mDiagClient->BeginSourceFile(LangOpts, mPP.get());
283
284    /* The core of the slang compiler */
285    ParseAST(*mPP, mBackend.get(), *mASTContext);
286
287    /* The compilation ended, clear up */
288    mBackend.reset();
289    mASTContext.reset();
290    mPP.reset();
291
292    /* Inform the diagnostic client we are done with previous source file */
293    mDiagClient->EndSourceFile();
294
295    return mDiagnostics->getNumErrors();
296}
297
298bool Slang::reflectToJava(const char* outputPackageName) {
299    if(mRSContext.get())
300        return mRSContext->reflectToJava(outputPackageName, mInputFileName, mOutputFileName);
301    else
302        return false;
303}
304
305bool Slang::reflectToJavaPath(const char* outputPathName) {
306    if(mRSContext.get())
307        return mRSContext->reflectToJavaPath(outputPathName);
308    else
309        return false;
310}
311
312void Slang::getPragmas(size_t* actualStringCount, size_t maxStringCount, char** strings) {
313    int stringCount = mPragmas.size() * 2;
314
315    if(actualStringCount)
316        *actualStringCount = stringCount;
317    if(stringCount > maxStringCount)
318        stringCount = maxStringCount;
319    if(strings)
320        for(PragmaList::const_iterator it = mPragmas.begin();
321            stringCount > 0;
322            stringCount-=2, it++)
323        {
324            *strings++ = const_cast<char*>(it->first.c_str());
325            *strings++ = const_cast<char*>(it->second.c_str());
326        }
327
328    return;
329}
330
331Slang::~Slang() {
332    llvm::llvm_shutdown();
333    return;
334}
335
336}   /* namespace slang */
337