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