1 /*
2 * Copyright 2011, 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_export_foreach.h"
18
19 #include <string>
20
21 #include "clang/AST/ASTContext.h"
22 #include "clang/AST/Decl.h"
23 #include "clang/AST/TypeLoc.h"
24
25 #include "llvm/DerivedTypes.h"
26 #include "llvm/Target/TargetData.h"
27
28 #include "slang_assert.h"
29 #include "slang_rs_context.h"
30 #include "slang_rs_export_type.h"
31 #include "slang_version.h"
32
33 namespace slang {
34
35 namespace {
36
ReportNameError(clang::Diagnostic * Diags,const clang::ParmVarDecl * PVD)37 static void ReportNameError(clang::Diagnostic *Diags,
38 const clang::ParmVarDecl *PVD) {
39 slangAssert(Diags && PVD);
40 const clang::SourceManager &SM = Diags->getSourceManager();
41
42 Diags->Report(clang::FullSourceLoc(PVD->getLocation(), SM),
43 Diags->getCustomDiagID(clang::Diagnostic::Error,
44 "Duplicate parameter entry (by position/name): '%0'"))
45 << PVD->getName();
46 return;
47 }
48
49 } // namespace
50
51 // This function takes care of additional validation and construction of
52 // parameters related to forEach_* reflection.
validateAndConstructParams(RSContext * Context,const clang::FunctionDecl * FD)53 bool RSExportForEach::validateAndConstructParams(
54 RSContext *Context, const clang::FunctionDecl *FD) {
55 slangAssert(Context && FD);
56 bool valid = true;
57 clang::ASTContext &C = Context->getASTContext();
58 clang::Diagnostic *Diags = Context->getDiagnostics();
59
60 if (!isRootRSFunc(FD)) {
61 slangAssert(false && "must be called on compute root function!");
62 }
63
64 numParams = FD->getNumParams();
65 slangAssert(numParams > 0);
66
67 // Compute root functions are required to return a void type for now
68 if (FD->getResultType().getCanonicalType() != C.VoidTy) {
69 Diags->Report(
70 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()),
71 Diags->getCustomDiagID(clang::Diagnostic::Error,
72 "compute root() is required to return a "
73 "void type"));
74 valid = false;
75 }
76
77 // Validate remaining parameter types
78 // TODO(all): Add support for LOD/face when we have them
79
80 size_t i = 0;
81 const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
82 clang::QualType QT = PVD->getType().getCanonicalType();
83
84 // Check for const T1 *in
85 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
86 mIn = PVD;
87 i++; // advance parameter pointer
88 }
89
90 // Check for T2 *out
91 if (i < numParams) {
92 PVD = FD->getParamDecl(i);
93 QT = PVD->getType().getCanonicalType();
94 if (QT->isPointerType() && !QT->getPointeeType().isConstQualified()) {
95 mOut = PVD;
96 i++; // advance parameter pointer
97 }
98 }
99
100 if (!mIn && !mOut) {
101 Diags->Report(
102 clang::FullSourceLoc(FD->getLocation(),
103 Diags->getSourceManager()),
104 Diags->getCustomDiagID(clang::Diagnostic::Error,
105 "Compute root() must have at least one "
106 "parameter for in or out"));
107 valid = false;
108 }
109
110 // Check for T3 *usrData
111 if (i < numParams) {
112 PVD = FD->getParamDecl(i);
113 QT = PVD->getType().getCanonicalType();
114 if (QT->isPointerType() && QT->getPointeeType().isConstQualified()) {
115 mUsrData = PVD;
116 i++; // advance parameter pointer
117 }
118 }
119
120 while (i < numParams) {
121 PVD = FD->getParamDecl(i);
122 QT = PVD->getType().getCanonicalType();
123
124 if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
125 Diags->Report(
126 clang::FullSourceLoc(PVD->getLocation(),
127 Diags->getSourceManager()),
128 Diags->getCustomDiagID(clang::Diagnostic::Error,
129 "Unexpected root() parameter '%0' "
130 "of type '%1'"))
131 << PVD->getName() << PVD->getType().getAsString();
132 valid = false;
133 } else {
134 llvm::StringRef ParamName = PVD->getName();
135 if (ParamName.equals("x")) {
136 if (mX) {
137 ReportNameError(Diags, PVD);
138 valid = false;
139 } else if (mY) {
140 // Can't go back to X after skipping Y
141 ReportNameError(Diags, PVD);
142 valid = false;
143 } else {
144 mX = PVD;
145 }
146 } else if (ParamName.equals("y")) {
147 if (mY) {
148 ReportNameError(Diags, PVD);
149 valid = false;
150 } else {
151 mY = PVD;
152 }
153 } else {
154 if (!mX && !mY) {
155 mX = PVD;
156 } else if (!mY) {
157 mY = PVD;
158 } else {
159 Diags->Report(
160 clang::FullSourceLoc(PVD->getLocation(),
161 Diags->getSourceManager()),
162 Diags->getCustomDiagID(clang::Diagnostic::Error,
163 "Unexpected root() parameter '%0' "
164 "of type '%1'"))
165 << PVD->getName() << PVD->getType().getAsString();
166 valid = false;
167 }
168 }
169 }
170
171 i++;
172 }
173
174 mMetadataEncoding = 0;
175 if (valid) {
176 // Set up the bitwise metadata encoding for runtime argument passing.
177 mMetadataEncoding |= (mIn ? 0x01 : 0);
178 mMetadataEncoding |= (mOut ? 0x02 : 0);
179 mMetadataEncoding |= (mUsrData ? 0x04 : 0);
180 mMetadataEncoding |= (mX ? 0x08 : 0);
181 mMetadataEncoding |= (mY ? 0x10 : 0);
182 }
183
184 if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
185 // APIs before ICS cannot skip between parameters. It is ok, however, for
186 // them to omit further parameters (i.e. skipping X is ok if you skip Y).
187 if (mMetadataEncoding != 0x1f && // In, Out, UsrData, X, Y
188 mMetadataEncoding != 0x0f && // In, Out, UsrData, X
189 mMetadataEncoding != 0x07 && // In, Out, UsrData
190 mMetadataEncoding != 0x03 && // In, Out
191 mMetadataEncoding != 0x01) { // In
192 Diags->Report(
193 clang::FullSourceLoc(FD->getLocation(),
194 Diags->getSourceManager()),
195 Diags->getCustomDiagID(clang::Diagnostic::Error,
196 "Compute root() targeting SDK levels %0-%1 "
197 "may not skip parameters"))
198 << SLANG_MINIMUM_TARGET_API << (SLANG_ICS_TARGET_API-1);
199 valid = false;
200 }
201 }
202
203
204 return valid;
205 }
206
Create(RSContext * Context,const clang::FunctionDecl * FD)207 RSExportForEach *RSExportForEach::Create(RSContext *Context,
208 const clang::FunctionDecl *FD) {
209 slangAssert(Context && FD);
210 llvm::StringRef Name = FD->getName();
211 RSExportForEach *FE;
212
213 slangAssert(!Name.empty() && "Function must have a name");
214
215 FE = new RSExportForEach(Context, Name, FD);
216
217 if (!FE->validateAndConstructParams(Context, FD)) {
218 return NULL;
219 }
220
221 clang::ASTContext &Ctx = Context->getASTContext();
222
223 std::string Id(DUMMY_RS_TYPE_NAME_PREFIX"helper_foreach_param:");
224 Id.append(FE->getName()).append(DUMMY_RS_TYPE_NAME_POSTFIX);
225
226 // Extract the usrData parameter (if we have one)
227 if (FE->mUsrData) {
228 const clang::ParmVarDecl *PVD = FE->mUsrData;
229 clang::QualType QT = PVD->getType().getCanonicalType();
230 slangAssert(QT->isPointerType() &&
231 QT->getPointeeType().isConstQualified());
232
233 const clang::ASTContext &C = Context->getASTContext();
234 if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
235 C.VoidTy) {
236 // In the case of using const void*, we can't reflect an appopriate
237 // Java type, so we fall back to just reflecting the ain/aout parameters
238 FE->mUsrData = NULL;
239 } else {
240 clang::RecordDecl *RD =
241 clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
242 Ctx.getTranslationUnitDecl(),
243 clang::SourceLocation(),
244 clang::SourceLocation(),
245 &Ctx.Idents.get(Id));
246
247 llvm::StringRef ParamName = PVD->getName();
248 clang::FieldDecl *FD =
249 clang::FieldDecl::Create(Ctx,
250 RD,
251 clang::SourceLocation(),
252 clang::SourceLocation(),
253 PVD->getIdentifier(),
254 QT->getPointeeType(),
255 NULL,
256 /* BitWidth = */ NULL,
257 /* Mutable = */ false,
258 /* HasInit = */ false);
259 RD->addDecl(FD);
260 RD->completeDefinition();
261
262 // Create an export type iff we have a valid usrData type
263 clang::QualType T = Ctx.getTagDeclType(RD);
264 slangAssert(!T.isNull());
265
266 RSExportType *ET = RSExportType::Create(Context, T.getTypePtr());
267
268 if (ET == NULL) {
269 fprintf(stderr, "Failed to export the function %s. There's at least "
270 "one parameter whose type is not supported by the "
271 "reflection\n", FE->getName().c_str());
272 return NULL;
273 }
274
275 slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
276 "Parameter packet must be a record");
277
278 FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
279 }
280 }
281
282 if (FE->mIn) {
283 const clang::Type *T = FE->mIn->getType().getCanonicalType().getTypePtr();
284 FE->mInType = RSExportType::Create(Context, T);
285 }
286
287 if (FE->mOut) {
288 const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr();
289 FE->mOutType = RSExportType::Create(Context, T);
290 }
291
292 return FE;
293 }
294
isRSForEachFunc(int targetAPI,const clang::FunctionDecl * FD)295 bool RSExportForEach::isRSForEachFunc(int targetAPI,
296 const clang::FunctionDecl *FD) {
297 // We currently support only compute root() being exported via forEach
298 if (!isRootRSFunc(FD)) {
299 return false;
300 }
301
302 if (FD->getNumParams() == 0) {
303 // Graphics compute function
304 return false;
305 }
306
307 // Handle legacy graphics root functions.
308 if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
309 const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
310 clang::QualType QT = PVD->getType().getCanonicalType();
311 const clang::QualType &IntType = FD->getASTContext().IntTy;
312 if ((FD->getResultType().getCanonicalType() == IntType) &&
313 (QT == IntType)) {
314 return false;
315 }
316 }
317
318 return true;
319 }
320
validateSpecialFuncDecl(int targetAPI,clang::Diagnostic * Diags,const clang::FunctionDecl * FD)321 bool RSExportForEach::validateSpecialFuncDecl(int targetAPI,
322 clang::Diagnostic *Diags,
323 const clang::FunctionDecl *FD) {
324 slangAssert(Diags && FD);
325 bool valid = true;
326 const clang::ASTContext &C = FD->getASTContext();
327
328 if (isRootRSFunc(FD)) {
329 unsigned int numParams = FD->getNumParams();
330 if (numParams == 0) {
331 // Graphics root function, so verify that it returns an int
332 if (FD->getResultType().getCanonicalType() != C.IntTy) {
333 Diags->Report(
334 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()),
335 Diags->getCustomDiagID(clang::Diagnostic::Error,
336 "root(void) is required to return "
337 "an int for graphics usage"));
338 valid = false;
339 }
340 } else if ((targetAPI < SLANG_ICS_TARGET_API) && (numParams == 1)) {
341 // Legacy graphics root function
342 // This has already been validated in isRSForEachFunc().
343 } else {
344 slangAssert(false &&
345 "Should not call validateSpecialFuncDecl() on compute root()");
346 }
347 } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) {
348 if (FD->getNumParams() != 0) {
349 Diags->Report(
350 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()),
351 Diags->getCustomDiagID(clang::Diagnostic::Error,
352 "%0(void) is required to have no "
353 "parameters")) << FD->getName();
354 valid = false;
355 }
356
357 if (FD->getResultType().getCanonicalType() != C.VoidTy) {
358 Diags->Report(
359 clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()),
360 Diags->getCustomDiagID(clang::Diagnostic::Error,
361 "%0(void) is required to have a void "
362 "return type")) << FD->getName();
363 valid = false;
364 }
365 } else {
366 slangAssert(false && "must be called on root, init or .rs.dtor function!");
367 }
368
369 return valid;
370 }
371
372 } // namespace slang
373