slang_rs.cpp revision 11274a7324b478ec13e1d10a1b81350b34a65ab1
1/*
2 * Copyright 2010-2012, 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_rs.h"
18
19#include <cstring>
20#include <list>
21#include <sstream>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "clang/Basic/SourceLocation.h"
27
28#include "clang/Frontend/FrontendDiagnostic.h"
29
30#include "clang/Sema/SemaDiagnostic.h"
31
32#include "llvm/Support/Path.h"
33
34#include "os_sep.h"
35#include "slang_rs_backend.h"
36#include "slang_rs_context.h"
37#include "slang_rs_export_type.h"
38
39#include "slang_rs_reflection_cpp.h"
40
41namespace slang {
42
43#define FS_SUFFIX  "fs"
44
45#define RS_HEADER_SUFFIX  "rsh"
46
47/* RS_HEADER_ENTRY(name) */
48#define ENUM_RS_HEADER()  \
49  RS_HEADER_ENTRY(rs_allocation) \
50  RS_HEADER_ENTRY(rs_atomic) \
51  RS_HEADER_ENTRY(rs_cl) \
52  RS_HEADER_ENTRY(rs_core) \
53  RS_HEADER_ENTRY(rs_debug) \
54  RS_HEADER_ENTRY(rs_element) \
55  RS_HEADER_ENTRY(rs_graphics) \
56  RS_HEADER_ENTRY(rs_math) \
57  RS_HEADER_ENTRY(rs_mesh) \
58  RS_HEADER_ENTRY(rs_matrix) \
59  RS_HEADER_ENTRY(rs_object) \
60  RS_HEADER_ENTRY(rs_program) \
61  RS_HEADER_ENTRY(rs_quaternion) \
62  RS_HEADER_ENTRY(rs_sampler) \
63  RS_HEADER_ENTRY(rs_time) \
64  RS_HEADER_ENTRY(rs_types) \
65
66// Returns true if \p Filename ends in ".fs".
67bool SlangRS::isFilterscript(const char *Filename) {
68  const char *c = strrchr(Filename, '.');
69  if (c && !strncmp(FS_SUFFIX, c + 1, strlen(FS_SUFFIX) + 1)) {
70    return true;
71  } else {
72    return false;
73  }
74}
75
76bool SlangRS::reflectToJava(const std::string &OutputPathBase,
77                            const std::string &OutputPackageName,
78                            const std::string &RSPackageName,
79                            std::string *RealPackageName) {
80  return mRSContext->reflectToJava(OutputPathBase,
81                                   OutputPackageName,
82                                   RSPackageName,
83                                   getInputFileName(),
84                                   getOutputFileName(),
85                                   RealPackageName);
86}
87
88bool SlangRS::generateBitcodeAccessor(const std::string &OutputPathBase,
89                                      const std::string &PackageName) {
90  RSSlangReflectUtils::BitCodeAccessorContext BCAccessorContext;
91
92  BCAccessorContext.rsFileName = getInputFileName().c_str();
93  BCAccessorContext.bcFileName = getOutputFileName().c_str();
94  BCAccessorContext.reflectPath = OutputPathBase.c_str();
95  BCAccessorContext.packageName = PackageName.c_str();
96  BCAccessorContext.bcStorage = BCST_JAVA_CODE;   // Must be BCST_JAVA_CODE
97
98  return RSSlangReflectUtils::GenerateBitCodeAccessor(BCAccessorContext);
99}
100
101bool SlangRS::checkODR(const char *CurInputFile) {
102  for (RSContext::ExportableList::iterator I = mRSContext->exportable_begin(),
103          E = mRSContext->exportable_end();
104       I != E;
105       I++) {
106    RSExportable *RSE = *I;
107    if (RSE->getKind() != RSExportable::EX_TYPE)
108      continue;
109
110    RSExportType *ET = static_cast<RSExportType *>(RSE);
111    if (ET->getClass() != RSExportType::ExportClassRecord)
112      continue;
113
114    RSExportRecordType *ERT = static_cast<RSExportRecordType *>(ET);
115
116    // Artificial record types (create by us not by user in the source) always
117    // conforms the ODR.
118    if (ERT->isArtificial())
119      continue;
120
121    // Key to lookup ERT in ReflectedDefinitions
122    llvm::StringRef RDKey(ERT->getName());
123    ReflectedDefinitionListTy::const_iterator RD =
124        ReflectedDefinitions.find(RDKey);
125
126    if (RD != ReflectedDefinitions.end()) {
127      const RSExportRecordType *Reflected = RD->getValue().first;
128      // There's a record (struct) with the same name reflected before. Enforce
129      // ODR checking - the Reflected must hold *exactly* the same "definition"
130      // as the one defined previously. We say two record types A and B have the
131      // same definition iff:
132      //
133      //  struct A {              struct B {
134      //    Type(a1) a1,            Type(b1) b1,
135      //    Type(a2) a2,            Type(b1) b2,
136      //    ...                     ...
137      //    Type(aN) aN             Type(b3) b3,
138      //  };                      }
139      //  Cond. #1. They have same number of fields, i.e., N = M;
140      //  Cond. #2. for (i := 1 to N)
141      //              Type(ai) = Type(bi) must hold;
142      //  Cond. #3. for (i := 1 to N)
143      //              Name(ai) = Name(bi) must hold;
144      //
145      // where,
146      //  Type(F) = the type of field F and
147      //  Name(F) = the field name.
148
149      bool PassODR = false;
150      // Cond. #1 and Cond. #2
151      if (Reflected->equals(ERT)) {
152        // Cond #3.
153        RSExportRecordType::const_field_iterator AI = Reflected->fields_begin(),
154                                                 BI = ERT->fields_begin();
155
156        for (unsigned i = 0, e = Reflected->getFields().size(); i != e; i++) {
157          if ((*AI)->getName() != (*BI)->getName())
158            break;
159          AI++;
160          BI++;
161        }
162        PassODR = (AI == (Reflected->fields_end()));
163      }
164
165      if (!PassODR) {
166        getDiagnostics().Report(mDiagErrorODR) << Reflected->getName()
167                                               << getInputFileName()
168                                               << RD->getValue().second;
169        return false;
170      }
171    } else {
172      llvm::StringMapEntry<ReflectedDefinitionTy> *ME =
173          llvm::StringMapEntry<ReflectedDefinitionTy>::Create(RDKey.begin(),
174                                                              RDKey.end());
175      ME->setValue(std::make_pair(ERT, CurInputFile));
176
177      if (!ReflectedDefinitions.insert(ME))
178        delete ME;
179
180      // Take the ownership of ERT such that it won't be freed in ~RSContext().
181      ERT->keep();
182    }
183  }
184  return true;
185}
186
187void SlangRS::initDiagnostic() {
188  clang::DiagnosticsEngine &DiagEngine = getDiagnostics();
189
190  if (DiagEngine.setDiagnosticGroupMapping("implicit-function-declaration",
191                                           clang::diag::MAP_ERROR))
192    DiagEngine.Report(clang::diag::warn_unknown_warning_option)
193      << "implicit-function-declaration";
194
195  DiagEngine.setDiagnosticMapping(
196    clang::diag::ext_typecheck_convert_discards_qualifiers,
197    clang::diag::MAP_ERROR,
198    clang::SourceLocation());
199
200  mDiagErrorInvalidOutputDepParameter =
201    DiagEngine.getCustomDiagID(
202      clang::DiagnosticsEngine::Error,
203      "invalid parameter for output dependencies files.");
204
205  mDiagErrorODR =
206    DiagEngine.getCustomDiagID(
207      clang::DiagnosticsEngine::Error,
208      "type '%0' in different translation unit (%1 v.s. %2) "
209      "has incompatible type definition");
210
211  mDiagErrorTargetAPIRange =
212    DiagEngine.getCustomDiagID(
213      clang::DiagnosticsEngine::Error,
214      "target API level '%0' is out of range ('%1' - '%2')");
215}
216
217void SlangRS::initPreprocessor() {
218  clang::Preprocessor &PP = getPreprocessor();
219
220  std::stringstream RSH;
221  RSH << "#define RS_VERSION " << mTargetAPI << std::endl;
222  RSH << "#include \"rs_core." RS_HEADER_SUFFIX "\"" << std::endl;
223  PP.setPredefines(RSH.str());
224}
225
226void SlangRS::initASTContext() {
227  mRSContext = new RSContext(getPreprocessor(),
228                             getASTContext(),
229                             getTargetInfo(),
230                             &mPragmas,
231                             mTargetAPI,
232                             &mGeneratedFileNames);
233}
234
235clang::ASTConsumer
236*SlangRS::createBackend(const clang::CodeGenOptions& CodeGenOpts,
237                        llvm::raw_ostream *OS,
238                        Slang::OutputType OT) {
239    return new RSBackend(mRSContext,
240                         &getDiagnostics(),
241                         CodeGenOpts,
242                         getTargetOptions(),
243                         &mPragmas,
244                         OS,
245                         OT,
246                         getSourceManager(),
247                         mAllowRSPrefix,
248                         mIsFilterscript);
249}
250
251bool SlangRS::IsRSHeaderFile(const char *File) {
252#define RS_HEADER_ENTRY(name)  \
253  if (::strcmp(File, #name "."RS_HEADER_SUFFIX) == 0)  \
254    return true;
255ENUM_RS_HEADER()
256#undef RS_HEADER_ENTRY
257  return false;
258}
259
260bool SlangRS::IsLocInRSHeaderFile(const clang::SourceLocation &Loc,
261                                  const clang::SourceManager &SourceMgr) {
262  clang::FullSourceLoc FSL(Loc, SourceMgr);
263  clang::PresumedLoc PLoc = SourceMgr.getPresumedLoc(FSL);
264
265  const char *Filename = PLoc.getFilename();
266  if (!Filename) {
267    return false;
268  } else {
269    return IsRSHeaderFile(llvm::sys::path::filename(Filename).data());
270  }
271}
272
273SlangRS::SlangRS()
274  : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0),
275    mIsFilterscript(false) {
276}
277
278bool SlangRS::compile(
279    const std::list<std::pair<const char*, const char*> > &IOFiles,
280    const std::list<std::pair<const char*, const char*> > &DepFiles,
281    const std::vector<std::string> &IncludePaths,
282    const std::vector<std::string> &AdditionalDepTargets,
283    Slang::OutputType OutputType, BitCodeStorageType BitcodeStorage,
284    bool AllowRSPrefix, bool OutputDep,
285    unsigned int TargetAPI, bool EmitDebug,
286    llvm::CodeGenOpt::Level OptimizationLevel,
287    const std::string &JavaReflectionPathBase,
288    const std::string &JavaReflectionPackageName,
289    const std::string &RSPackageName) {
290  if (IOFiles.empty())
291    return true;
292
293  if (OutputDep && (DepFiles.size() != IOFiles.size())) {
294    getDiagnostics().Report(mDiagErrorInvalidOutputDepParameter);
295    return false;
296  }
297
298  std::string RealPackageName;
299
300  const char *InputFile, *OutputFile, *BCOutputFile, *DepOutputFile;
301  std::list<std::pair<const char*, const char*> >::const_iterator
302      IOFileIter = IOFiles.begin(), DepFileIter = DepFiles.begin();
303
304  setIncludePaths(IncludePaths);
305  setOutputType(OutputType);
306  if (OutputDep) {
307    setAdditionalDepTargets(AdditionalDepTargets);
308  }
309
310  setDebugMetadataEmission(EmitDebug);
311
312  setOptimizationLevel(OptimizationLevel);
313
314  mAllowRSPrefix = AllowRSPrefix;
315
316  mTargetAPI = TargetAPI;
317  if (mTargetAPI < SLANG_MINIMUM_TARGET_API ||
318      mTargetAPI > SLANG_MAXIMUM_TARGET_API) {
319    getDiagnostics().Report(mDiagErrorTargetAPIRange) << mTargetAPI
320        << SLANG_MINIMUM_TARGET_API << SLANG_MAXIMUM_TARGET_API;
321    return false;
322  }
323
324  // Skip generation of warnings a second time if we are doing more than just
325  // a single pass over the input file.
326  bool SuppressAllWarnings = (OutputType != Slang::OT_Dependency);
327
328  for (unsigned i = 0, e = IOFiles.size(); i != e; i++) {
329    InputFile = IOFileIter->first;
330    OutputFile = IOFileIter->second;
331
332    reset();
333
334    if (!setInputSource(InputFile))
335      return false;
336
337    if (!setOutput(OutputFile))
338      return false;
339
340    if (!JavaReflectionPackageName.empty()) {
341      mRSContext->setReflectJavaPackageName(
342          JavaReflectionPackageName);
343    }
344
345    mIsFilterscript = isFilterscript(InputFile);
346
347    if (Slang::compile() > 0)
348      return false;
349
350    if (OutputType != Slang::OT_Dependency) {
351
352      if (BitcodeStorage == BCST_CPP_CODE) {
353          RSReflectionCpp R(mRSContext);
354          bool ret = R.reflect(JavaReflectionPathBase, getInputFileName(), getOutputFileName());
355          if (!ret) {
356            return false;
357          }
358      } else {
359
360        if (!reflectToJava(JavaReflectionPathBase,
361                           JavaReflectionPackageName,
362                           RSPackageName,
363                           &RealPackageName)) {
364          return false;
365        }
366
367        for (std::vector<std::string>::const_iterator
368                 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
369             I != E;
370             I++) {
371          std::string ReflectedName = RSSlangReflectUtils::ComputePackagedPath(
372              JavaReflectionPathBase.c_str(),
373              (RealPackageName + OS_PATH_SEPARATOR_STR + *I).c_str());
374          appendGeneratedFileName(ReflectedName + ".java");
375        }
376
377        if ((OutputType == Slang::OT_Bitcode) &&
378            (BitcodeStorage == BCST_JAVA_CODE) &&
379            !generateBitcodeAccessor(JavaReflectionPathBase,
380                                     RealPackageName.c_str())) {
381          return false;
382        }
383      }
384    }
385
386    if (OutputDep) {
387      BCOutputFile = DepFileIter->first;
388      DepOutputFile = DepFileIter->second;
389
390      setDepTargetBC(BCOutputFile);
391
392      if (!setDepOutput(DepOutputFile))
393        return false;
394
395      if (SuppressAllWarnings) {
396        getDiagnostics().setSuppressAllDiagnostics(true);
397      }
398      if (generateDepFile() > 0)
399        return false;
400      if (SuppressAllWarnings) {
401        getDiagnostics().setSuppressAllDiagnostics(false);
402      }
403
404      DepFileIter++;
405    }
406
407    if (!checkODR(InputFile))
408      return false;
409
410    IOFileIter++;
411  }
412
413  return true;
414}
415
416void SlangRS::reset() {
417  delete mRSContext;
418  mRSContext = NULL;
419  mGeneratedFileNames.clear();
420  Slang::reset();
421  return;
422}
423
424SlangRS::~SlangRS() {
425  delete mRSContext;
426  for (ReflectedDefinitionListTy::iterator I = ReflectedDefinitions.begin(),
427          E = ReflectedDefinitions.end();
428       I != E;
429       I++) {
430    delete I->getValue().first;
431  }
432  return;
433}
434
435}  // namespace slang
436