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