slang_rs.cpp revision 0e4ee65a2ba6b32fcd16c5d4a57fc7542d4032b4
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 << PP.getPredefines();
214  RSH << "#define RS_VERSION " << mTargetAPI << "\n";
215  RSH << "#include \"rs_core." RS_HEADER_SUFFIX "\"\n";
216  PP.setPredefines(RSH.str());
217}
218
219void SlangRS::initASTContext() {
220  mRSContext = new RSContext(getPreprocessor(),
221                             getASTContext(),
222                             getTargetInfo(),
223                             &mPragmas,
224                             mTargetAPI);
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, JavaReflectionPathBase, getInputFileName(),
347                            getOutputFileName());
348          if (!R.reflect()) {
349            return false;
350          }
351      } else {
352        if (!RSPackageName.empty()) {
353          mRSContext->setRSPackageName(RSPackageName);
354        }
355
356        RSReflectionJava R(mRSContext, &mGeneratedFileNames,
357                           JavaReflectionPathBase, getInputFileName(),
358                           getOutputFileName(),
359                           BitcodeStorage == BCST_JAVA_CODE);
360        if (!R.reflect()) {
361          // TODO Is this needed or will the error message have been printed
362          // already? and why not for the C++ case?
363          fprintf(stderr, "RSContext::reflectToJava : failed to do reflection "
364                          "(%s)\n",
365                  R.getLastError());
366          return false;
367        }
368
369        for (std::vector<std::string>::const_iterator
370                 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
371             I != E;
372             I++) {
373          std::string ReflectedName = RSSlangReflectUtils::ComputePackagedPath(
374              JavaReflectionPathBase.c_str(),
375              (RealPackageName + OS_PATH_SEPARATOR_STR + *I).c_str());
376          appendGeneratedFileName(ReflectedName + ".java");
377        }
378
379        if ((OutputType == Slang::OT_Bitcode) &&
380            (BitcodeStorage == BCST_JAVA_CODE) &&
381            !generateJavaBitcodeAccessor(JavaReflectionPathBase,
382                                         RealPackageName.c_str(),
383                                         mRSContext->getLicenseNote())) {
384          return false;
385        }
386      }
387    }
388
389    if (OutputDep) {
390      BCOutputFile = DepFileIter->first;
391      DepOutputFile = DepFileIter->second;
392
393      setDepTargetBC(BCOutputFile);
394
395      if (!setDepOutput(DepOutputFile))
396        return false;
397
398      if (SuppressAllWarnings) {
399        getDiagnostics().setSuppressAllDiagnostics(true);
400      }
401      if (generateDepFile() > 0)
402        return false;
403      if (SuppressAllWarnings) {
404        getDiagnostics().setSuppressAllDiagnostics(false);
405      }
406
407      DepFileIter++;
408    }
409
410    if (!checkODR(InputFile))
411      return false;
412
413    IOFileIter++;
414  }
415
416  return true;
417}
418
419void SlangRS::reset() {
420  delete mRSContext;
421  mRSContext = NULL;
422  mGeneratedFileNames.clear();
423  Slang::reset();
424}
425
426SlangRS::~SlangRS() {
427  delete mRSContext;
428  for (ReflectedDefinitionListTy::iterator I = ReflectedDefinitions.begin(),
429          E = ReflectedDefinitions.end();
430       I != E;
431       I++) {
432    delete I->getValue().first;
433  }
434}
435
436}  // namespace slang
437