• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_context.h"
18 
19 #include <string>
20 
21 #include "clang/AST/ASTContext.h"
22 #include "clang/AST/Attr.h"
23 #include "clang/AST/Decl.h"
24 #include "clang/AST/DeclBase.h"
25 #include "clang/AST/Mangle.h"
26 #include "clang/AST/Type.h"
27 
28 #include "clang/Basic/Linkage.h"
29 #include "clang/Basic/TargetInfo.h"
30 
31 #include "llvm/IR/LLVMContext.h"
32 #include "llvm/IR/DataLayout.h"
33 
34 #include "slang.h"
35 #include "slang_assert.h"
36 #include "slang_backend.h"
37 #include "slang_rs_export_foreach.h"
38 #include "slang_rs_export_func.h"
39 #include "slang_rs_export_reduce.h"
40 #include "slang_rs_export_type.h"
41 #include "slang_rs_export_var.h"
42 #include "slang_rs_exportable.h"
43 #include "slang_rs_pragma_handler.h"
44 #include "slang_rs_reflection.h"
45 #include "slang_rs_special_func.h"
46 
47 namespace slang {
48 
RSContext(clang::Preprocessor & PP,clang::ASTContext & Ctx,const clang::TargetInfo & Target,PragmaList * Pragmas,unsigned int TargetAPI,bool Verbose)49 RSContext::RSContext(clang::Preprocessor &PP,
50                      clang::ASTContext &Ctx,
51                      const clang::TargetInfo &Target,
52                      PragmaList *Pragmas,
53                      unsigned int TargetAPI,
54                      bool Verbose)
55     : mPP(PP),
56       mCtx(Ctx),
57       mPragmas(Pragmas),
58       mTargetAPI(TargetAPI),
59       mVerbose(Verbose),
60       mDataLayout(Target.getDataLayout()),
61       mLLVMContext(slang::getGlobalLLVMContext()),
62       mLicenseNote(nullptr),
63       mRSPackageName("android.renderscript"),
64       version(0),
65       mMangleCtx(Ctx.createMangleContext()),
66       mIs64Bit(Target.getPointerWidth(0) == 64),
67       mNextSlot(1),
68       mNextForEachOrdinal(0) {
69 
70   AddPragmaHandlers(PP, this);
71 
72   // Prepare target data
73   // mDataLayout = Target.getDataLayout();
74 
75   // Reserve slot 0 for the root kernel.
76   mExportForEach.push_back(nullptr);
77   mFirstOldStyleKernel = mExportForEach.end();
78 }
79 
processExportVar(const clang::VarDecl * VD)80 bool RSContext::processExportVar(const clang::VarDecl *VD) {
81   slangAssert(!VD->getName().empty() && "Variable name should not be empty");
82 
83   RSExportType *ET = RSExportType::CreateFromDecl(this, VD);
84   if (!ET)
85     return false;
86 
87   RSExportVar *EV = new RSExportVar(this, VD, ET);
88   if (EV == nullptr)
89     return false;
90   else
91     mExportVars.push_back(EV);
92 
93   return true;
94 }
95 
getForEachSlotNumber(const clang::FunctionDecl * FD)96 int RSContext::getForEachSlotNumber(const clang::FunctionDecl* FD) {
97   const clang::StringRef& funcName = FD->getName();
98   return getForEachSlotNumber(funcName);
99 }
100 
getForEachSlotNumber(const clang::StringRef & funcName)101 int RSContext::getForEachSlotNumber(const clang::StringRef& funcName) {
102   auto it = mExportForEachMap.find(funcName);
103   if (it == mExportForEachMap.end()) {
104     return -1;
105   }
106   return it->second;
107 }
108 
processExportFunc(const clang::FunctionDecl * FD)109 bool RSContext::processExportFunc(const clang::FunctionDecl *FD) {
110   slangAssert(!FD->getName().empty() && "Function name should not be empty");
111 
112   if (!FD->isThisDeclarationADefinition()) {
113     return true;
114   }
115 
116   slangAssert(FD->getStorageClass() == clang::SC_None);
117 
118   // Specialized function
119   if (RSSpecialFunc::isSpecialRSFunc(mTargetAPI, FD)) {
120     // Do not reflect specialized functions like init, dtor, or graphics root.
121     return RSSpecialFunc::validateSpecialFuncDecl(mTargetAPI, this, FD);
122   }
123 
124   // Foreach kernel
125   if (RSExportForEach::isRSForEachFunc(mTargetAPI, FD)) {
126     RSExportForEach *EFE = RSExportForEach::Create(this, FD);
127     if (EFE == nullptr) {
128       return false;
129     }
130 
131     // The root function should be at index 0 in the list
132     if (FD->getName().equals("root")) {
133       mExportForEach[0] = EFE;
134       return true;
135     }
136 
137     // New-style kernels with attribute "kernel" should come first in the list
138     if (FD->hasAttr<clang::RenderScriptKernelAttr>()) {
139       mFirstOldStyleKernel = mExportForEach.insert(mFirstOldStyleKernel, EFE) + 1;
140       slangAssert((mTargetAPI < SLANG_FEATURE_SINGLE_SOURCE_API ||
141                    getForEachSlotNumber(FD->getName()) ==
142                    mFirstOldStyleKernel - mExportForEach.begin() - 1) &&
143                   "Inconsistent slot number assignment");
144       return true;
145     }
146 
147     // Old-style kernels should appear in the end of the list
148     mFirstOldStyleKernel = mExportForEach.insert(mFirstOldStyleKernel, EFE);
149     return true;
150   }
151 
152   // Invokable
153   if (auto *EF = RSExportFunc::Create(this, FD)) {
154     mExportFuncs.push_back(EF);
155     return true;
156   }
157 
158   return false;
159 }
160 
addForEach(const clang::FunctionDecl * FD)161 bool RSContext::addForEach(const clang::FunctionDecl* FD) {
162   const llvm::StringRef& funcName = FD->getName();
163 
164   if (funcName.equals("root")) {
165     // The root kernel should always be in slot 0.
166     mExportForEachMap.insert(std::make_pair(funcName, 0));
167   } else {
168     mExportForEachMap.insert(std::make_pair(funcName, mNextSlot++));
169   }
170 
171   return true;
172 }
173 
processExportType(const llvm::StringRef & Name)174 bool RSContext::processExportType(const llvm::StringRef &Name) {
175   clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl();
176 
177   slangAssert(TUDecl != nullptr && "Translation unit declaration (top-level "
178                                    "declaration) is null object");
179 
180   const clang::IdentifierInfo *II = mPP.getIdentifierInfo(Name);
181   if (II == nullptr)
182     // TODO(zonr): alert identifier @Name mark as an exportable type cannot be
183     //             found
184     return false;
185 
186   clang::DeclContext::lookup_result R = TUDecl->lookup(II);
187   RSExportType *ET = nullptr;
188 
189   for (clang::DeclContext::lookup_iterator I = R.begin(), E = R.end();
190        I != E;
191        I++) {
192     clang::NamedDecl *const ND = *I;
193     const clang::Type *T = nullptr;
194 
195     switch (ND->getKind()) {
196       case clang::Decl::Typedef: {
197         T = static_cast<const clang::TypedefDecl*>(
198             ND)->getCanonicalDecl()->getUnderlyingType().getTypePtr();
199         break;
200       }
201       case clang::Decl::Record: {
202         T = static_cast<const clang::RecordDecl*>(ND)->getTypeForDecl();
203         break;
204       }
205       default: {
206         // unsupported, skip
207         break;
208       }
209     }
210 
211     if (T != nullptr)
212       ET = RSExportType::Create(this, T, NotLegacyKernelArgument);
213   }
214 
215   return (ET != nullptr);
216 }
217 
setAllocationType(const clang::TypeDecl * TD)218 void RSContext::setAllocationType(const clang::TypeDecl* TD) {
219   mAllocationType = mCtx.getTypeDeclType(TD);
220 }
221 
setScriptCallType(const clang::TypeDecl * TD)222 void RSContext::setScriptCallType(const clang::TypeDecl* TD) {
223   mScriptCallType = mCtx.getTypeDeclType(TD);
224 }
225 
processExports()226 bool RSContext::processExports() {
227   bool valid = true;
228 
229   if (getDiagnostics()->hasErrorOccurred()) {
230     return false;
231   }
232 
233   clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl();
234   for (auto I = TUDecl->decls_begin(), E = TUDecl->decls_end(); I != E; I++) {
235     clang::Decl* D = *I;
236     switch (D->getKind()) {
237     case clang::Decl::Var: {
238       clang::VarDecl* VD = llvm::dyn_cast<clang::VarDecl>(D);
239       bool ShouldExportVariable = true;
240       if (VD->getFormalLinkage() == clang::ExternalLinkage) {
241         clang::QualType QT = VD->getTypeSourceInfo()->getType();
242         if (QT.isConstQualified() && !VD->hasInit()) {
243           if (Slang::IsLocInRSHeaderFile(VD->getLocation(),
244                                          *getSourceManager())) {
245             // We don't export variables internal to the runtime's
246             // implementation.
247             ShouldExportVariable = false;
248           } else {
249             clang::DiagnosticsEngine *DiagEngine = getDiagnostics();
250             DiagEngine->Report(VD->getLocation(), DiagEngine->getCustomDiagID(
251                 clang::DiagnosticsEngine::Error,
252                 "invalid declaration of uninitialized constant variable '%0'"))
253               << VD->getName();
254             valid = false;
255           }
256         }
257         if (valid && ShouldExportVariable && isSyntheticName(VD->getName()))
258           ShouldExportVariable = false;
259         if (valid && ShouldExportVariable && !processExportVar(VD)) {
260           valid = false;
261         }
262       }
263       break;
264     }
265     case clang::Decl::Function: {
266       clang::FunctionDecl* FD = llvm::dyn_cast<clang::FunctionDecl>(D);
267       if (FD->getFormalLinkage() == clang::ExternalLinkage) {
268         if (!processExportFunc(FD)) {
269           valid = false;
270         }
271       }
272       break;
273     }
274     default:
275       break;
276     }
277   }
278 
279   // Create a dummy root in slot 0 if a root kernel is not seen
280   // and there exists a non-root kernel.
281   if (valid && mExportForEach[0] == nullptr) {
282     const size_t numExportedForEach = mExportForEach.size();
283     if (numExportedForEach > 1) {
284       mExportForEach[0] = RSExportForEach::CreateDummyRoot(this);
285     } else {
286       slangAssert(numExportedForEach == 1);
287       mExportForEach.pop_back();
288     }
289   }
290 
291   // Finally, export type forcely set to be exported by user
292   for (NeedExportTypeSet::const_iterator EI = mNeedExportTypes.begin(),
293            EE = mNeedExportTypes.end();
294        EI != EE;
295        EI++) {
296     if (!processExportType(EI->getKey())) {
297       valid = false;
298     }
299   }
300 
301   return valid;
302 }
303 
processReducePragmas(Backend * BE)304 bool RSContext::processReducePragmas(Backend *BE) {
305   // This is needed to ensure that the dummy variable is emitted into
306   // the bitcode -- which in turn forces the function to be emitted
307   // into the bitcode.  We couldn't do this at
308   // markUsedByReducePragma() time because we had to wait until the
309   // Backend is available.
310   for (auto DummyVar : mUsedByReducePragmaDummyVars)
311     BE->HandleTopLevelDecl(clang::DeclGroupRef(DummyVar));
312 
313   bool valid = true;
314   for (auto I = export_reduce_begin(), E = export_reduce_end(); I != E; ++I) {
315     if (! (*I)->analyzeTranslationUnit())
316       valid = false;
317   }
318   return valid;
319 }
320 
markUsedByReducePragma(clang::FunctionDecl * FD,CheckName Check)321 void RSContext::markUsedByReducePragma(clang::FunctionDecl *FD, CheckName Check) {
322   if (mUsedByReducePragmaFns.find(FD) != mUsedByReducePragmaFns.end())
323     return;  // already marked used
324 
325   if (Check == CheckNameYes) {
326     // This is an inefficient linear search.  If this turns out to be a
327     // problem in practice, then processReducePragmas() could build a
328     // set or hash table or something similar containing all function
329     // names mentioned in a reduce pragma and searchable in O(c) or
330     // O(log(n)) time rather than the currently-implemented O(n) search.
331     auto NameMatches = [this, FD]() {
332       for (auto I = export_reduce_begin(), E = export_reduce_end(); I != E; ++I) {
333         if ((*I)->matchName(FD->getName()))
334           return true;
335       }
336       return false;
337     };
338     if (!NameMatches())
339       return;
340   }
341 
342   mUsedByReducePragmaFns.insert(FD);
343 
344   // This is needed to prevent clang from warning that the function is
345   // unused (in the case where it is only referenced by #pragma rs
346   // reduce).
347   FD->setIsUsed();
348 
349   // Each constituent function "f" of a reduction kernel gets a dummy variable generated for it:
350   //   void *.rs.reduce_fn.f = (void*)&f;
351   // This is a trick to ensure that clang will not delete "f" as unused.
352 
353   // `-VarDecl 0x87cb558 <line:3:1, col:30> col:7 var 'void *' cinit
354   //     `-CStyleCastExpr 0x87cb630 <col:19, col:26> 'void *' <BitCast>
355   //       `-ImplicitCastExpr 0x87cb618 <col:26> 'void (*)(int *, float, double)' <FunctionToPointerDecay>
356   //         `-DeclRefExpr 0x87cb5b8 <col:26> 'void (int *, float, double)' Function 0x8784e10 'foo' 'void (int *, float, double)
357 
358   const clang::QualType VoidPtrType = mCtx.getPointerType(mCtx.VoidTy);
359 
360   clang::DeclContext *const DC = FD->getDeclContext();
361   const clang::SourceLocation Loc = FD->getLocation();
362 
363   clang::VarDecl *const VD = clang::VarDecl::Create(
364       mCtx, DC, Loc, Loc,
365       &mCtx.Idents.get(std::string(".rs.reduce_fn.") + FD->getNameAsString()),
366       VoidPtrType,
367       mCtx.getTrivialTypeSourceInfo(VoidPtrType),
368       clang::SC_None);
369   VD->setLexicalDeclContext(DC);
370   DC->addDecl(VD);
371 
372   clang::DeclRefExpr *const DRE = clang::DeclRefExpr::Create(mCtx,
373                                                              clang::NestedNameSpecifierLoc(),
374                                                              Loc,
375                                                              FD, false, Loc, FD->getType(),
376                                                              clang::VK_RValue);
377   clang::ImplicitCastExpr *const ICE = clang::ImplicitCastExpr::Create(mCtx, mCtx.getPointerType(FD->getType()),
378                                                                        clang::CK_FunctionToPointerDecay, DRE,
379                                                                        nullptr, clang::VK_RValue);
380   clang::CStyleCastExpr *const CSCE = clang::CStyleCastExpr::Create(mCtx, VoidPtrType, clang::VK_RValue, clang::CK_BitCast,
381                                                                     ICE, nullptr, nullptr,
382                                                                     Loc, Loc);
383   VD->setInit(CSCE);
384 
385   mUsedByReducePragmaDummyVars.push_back(VD);
386 }
387 
insertExportType(const llvm::StringRef & TypeName,RSExportType * ET)388 bool RSContext::insertExportType(const llvm::StringRef &TypeName,
389                                  RSExportType *ET) {
390   ExportTypeMap::value_type *NewItem =
391       ExportTypeMap::value_type::Create(TypeName,
392                                         mExportTypes.getAllocator(),
393                                         ET);
394 
395   if (mExportTypes.insert(NewItem)) {
396     return true;
397   } else {
398     NewItem->Destroy(mExportTypes.getAllocator());
399     return false;
400   }
401 }
402 
~RSContext()403 RSContext::~RSContext() {
404   delete mLicenseNote;
405   for (ExportableList::iterator I = mExportables.begin(),
406           E = mExportables.end();
407        I != E;
408        I++) {
409     if (!(*I)->isKeep())
410       delete *I;
411   }
412 }
413 
414 }  // namespace slang
415