slang.cpp revision f83d3c396d5d7eacd97cf0a5d3b6a01d75535b67
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  std::string inclFiles("#include \"rs_types.rsh\"");
112  mPP->setPredefines(inclFiles + "\n" + "#include \"rs_math.rsh\"" + "\n");
113
114  /* Like ApplyHeaderSearchOptions in InitHeaderSearch.cpp */
115  const char *inclDir = getenv("ANDROID_BUILD_TOP");
116  std::vector<DirectoryLookup> SearchList;
117  if (inclDir) {
118    char *dirPath = new char[strlen(inclDir) + 33];
119    strcpy(dirPath, inclDir);
120    strcpy(dirPath + strlen(inclDir), "/frameworks/base/libs/rs/scriptc");
121
122    if (const DirectoryEntry *DE = mFileMgr->getDirectory(dirPath, dirPath + strlen(dirPath))) {
123      SearchList.push_back(DirectoryLookup(DE, SrcMgr::C_System, false, false));
124    }
125  }
126
127  int siz = 256;
128  char *currDir = new char[siz];
129  while (!getcwd(currDir, siz)) {
130    siz *= 2;
131  }
132
133  if (siz - strlen(currDir) >= 33) {
134    strcpy(currDir + strlen(currDir), "/frameworks/base/libs/rs/scriptc");
135  } else {
136    char *tmp = new char[strlen(currDir) + 33];
137    strcpy(tmp, currDir);
138    strcpy(tmp + strlen(currDir), "/frameworks/base/libs/rs/scriptc");
139    currDir = tmp;
140  }
141
142  if (const DirectoryEntry *DE = mFileMgr->getDirectory(currDir, currDir + strlen(currDir))) {
143    SearchList.push_back(DirectoryLookup(DE, SrcMgr::C_System, false, false));
144  }
145
146  HS->SetSearchPaths(SearchList, 1, false);
147
148  return;
149}
150
151Slang::Slang(const char* Triple, const char* CPU, const char** Features) :
152    mOutputType(SlangCompilerOutput_Default),
153    mAllowRSPrefix(false)
154{
155    GlobalInitialization();
156
157    createDiagnostic();
158    llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.get());
159
160    createTarget(Triple, CPU, Features);
161    createFileManager();
162    createSourceManager();
163
164    return;
165}
166
167bool Slang::setInputSource(llvm::StringRef inputFile, const char* text, size_t textLength) {
168    mInputFileName = inputFile.str();
169
170    /* Reset the ID tables if we are reusing the SourceManager */
171    mSourceMgr->clearIDTables();
172
173    /* Load the source */
174    llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getMemBuffer(text, text + textLength);
175    mSourceMgr->createMainFileIDForMemBuffer(SB);
176
177    if(mSourceMgr->getMainFileID().isInvalid()) {
178        mDiagnostics->Report(clang::diag::err_fe_error_reading) << inputFile;
179        return false;
180    }
181
182    return true;
183}
184
185bool Slang::setInputSource(llvm::StringRef inputFile) {
186    mInputFileName = inputFile.str();
187
188    mSourceMgr->clearIDTables();
189
190    const FileEntry* File = mFileMgr->getFile(inputFile);
191    if(File)
192        mSourceMgr->createMainFileID(File, SourceLocation());
193
194    if(mSourceMgr->getMainFileID().isInvalid()) {
195        mDiagnostics->Report(clang::diag::err_fe_error_reading) << inputFile;
196        return false;
197    }
198
199    return true;
200}
201
202void Slang::setOutputType(SlangCompilerOutputTy outputType) {
203    mOutputType = outputType;
204    if( mOutputType != SlangCompilerOutput_Assembly &&
205        mOutputType != SlangCompilerOutput_LL &&
206        mOutputType != SlangCompilerOutput_Bitcode &&
207        mOutputType != SlangCompilerOutput_Nothing &&
208        mOutputType != SlangCompilerOutput_Obj)
209        mOutputType = SlangCompilerOutput_Default;
210    return;
211}
212
213static void _mkdir_given_a_file(const char *file) {
214    char buf[256];
215    char *tmp, *p = NULL;
216    size_t len = strlen(file);
217
218    if (len + 1 <= sizeof(buf))
219        tmp = buf;
220    else
221        tmp = new char [len + 1];
222
223    strcpy(tmp, file);
224
225    if (tmp[len - 1] == '/')
226        tmp[len - 1] = 0;
227
228    for (p = tmp + 1; *p; p++) {
229        if (*p == '/') {
230            *p = 0;
231            mkdir(tmp, S_IRWXU);
232            *p = '/';
233        }
234    }
235
236    if (tmp != buf)
237        delete[] tmp;
238}
239
240bool Slang::setOutput(const char* outputFile) {
241    std::string Error;
242
243    _mkdir_given_a_file(outputFile);
244
245    switch(mOutputType) {
246        case SlangCompilerOutput_Assembly:
247        case SlangCompilerOutput_LL:
248            mOS.reset( new llvm::raw_fd_ostream(outputFile, Error, 0) );
249        break;
250
251        case SlangCompilerOutput_Nothing:
252            mOS.reset();
253        break;
254
255        case SlangCompilerOutput_Obj:
256        case SlangCompilerOutput_Bitcode:
257        default:
258            mOS.reset( new llvm::raw_fd_ostream(outputFile, Error, llvm::raw_fd_ostream::F_Binary) );
259        break;
260    }
261
262    if(!Error.empty()) {
263        mOS.reset();
264        mDiagnostics->Report(clang::diag::err_fe_error_opening) << outputFile << Error;
265        return false;
266    }
267
268    mOutputFileName = outputFile;
269
270    return true;
271}
272
273int Slang::compile() {
274    if((mDiagnostics->getNumErrors() > 0) || (mOS.get() == NULL))
275        return mDiagnostics->getNumErrors();
276
277    /* Here is per-compilation needed initialization */
278    createPreprocessor();
279    createASTContext();
280    createRSContext();
281    //createBackend();
282    createRSBackend();
283
284    /* Inform the diagnostic client we are processing a source file */
285    mDiagClient->BeginSourceFile(LangOpts, mPP.get());
286
287    /* The core of the slang compiler */
288    ParseAST(*mPP, mBackend.get(), *mASTContext);
289
290    /* The compilation ended, clear up */
291    mBackend.reset();
292    // Can't reset yet because the reflection later on still needs mRSContext
293    //    mRSContext.reset();
294    mASTContext.reset();
295    mPP.reset();
296
297    /* Inform the diagnostic client we are done with previous source file */
298    mDiagClient->EndSourceFile();
299
300    return mDiagnostics->getNumErrors();
301}
302
303bool Slang::reflectToJava(const char* outputPackageName) {
304    if(mRSContext.get())
305        return mRSContext->reflectToJava(outputPackageName, mInputFileName, mOutputFileName);
306    else
307        return false;
308}
309
310bool Slang::reflectToJavaPath(const char* outputPathName) {
311    if(mRSContext.get())
312        return mRSContext->reflectToJavaPath(outputPathName);
313    else
314        return false;
315}
316
317void Slang::getPragmas(size_t* actualStringCount, size_t maxStringCount, char** strings) {
318    int stringCount = mPragmas.size() * 2;
319
320    if(actualStringCount)
321        *actualStringCount = stringCount;
322    if(stringCount > maxStringCount)
323        stringCount = maxStringCount;
324    if(strings)
325        for(PragmaList::const_iterator it = mPragmas.begin();
326            stringCount > 0;
327            stringCount-=2, it++)
328        {
329            *strings++ = const_cast<char*>(it->first.c_str());
330            *strings++ = const_cast<char*>(it->second.c_str());
331        }
332
333    return;
334}
335
336Slang::~Slang() {
337    llvm::llvm_shutdown();
338    return;
339}
340
341}   /* namespace slang */
342