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::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::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