1 /*
2 * Copyright 2015, 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_reduce.h"
18
19 #include <algorithm>
20 #include <sstream>
21 #include <string>
22
23 #include "clang/AST/ASTContext.h"
24
25 #include "slang_assert.h"
26 #include "slang_rs_context.h"
27 #include "slang_rs_export_type.h"
28 #include "slang_rs_object_ref_count.h"
29 #include "slang_rs_special_kernel_param.h"
30 #include "slang_version.h"
31
32 #include "bcinfo/MetadataExtractor.h"
33
34 namespace slang {
35
36 const char RSExportReduce::KeyReduce[] = "reduce";
37 const char RSExportReduce::KeyInitializer[] = "initializer";
38 const char RSExportReduce::KeyAccumulator[] = "accumulator";
39 const char RSExportReduce::KeyCombiner[] = "combiner";
40 const char RSExportReduce::KeyOutConverter[] = "outconverter";
41 const char RSExportReduce::KeyHalter[] = "halter";
42
matchName(const llvm::StringRef & Candidate) const43 bool RSExportReduce::matchName(const llvm::StringRef &Candidate) const {
44 return
45 Candidate.equals(mNameInitializer) ||
46 Candidate.equals(mNameAccumulator) ||
47 Candidate.equals(mNameCombiner) ||
48 Candidate.equals(mNameOutConverter) ||
49 Candidate.equals(mNameHalter);
50 }
51
Create(RSContext * Context,const clang::SourceLocation Location,const llvm::StringRef & NameReduce,const llvm::StringRef & NameInitializer,const llvm::StringRef & NameAccumulator,const llvm::StringRef & NameCombiner,const llvm::StringRef & NameOutConverter,const llvm::StringRef & NameHalter)52 RSExportReduce *RSExportReduce::Create(RSContext *Context,
53 const clang::SourceLocation Location,
54 const llvm::StringRef &NameReduce,
55 const llvm::StringRef &NameInitializer,
56 const llvm::StringRef &NameAccumulator,
57 const llvm::StringRef &NameCombiner,
58 const llvm::StringRef &NameOutConverter,
59 const llvm::StringRef &NameHalter) {
60 slangAssert(Context);
61 RSExportReduce *RNE = new RSExportReduce(Context,
62 Location,
63 NameReduce,
64 NameInitializer,
65 NameAccumulator,
66 NameCombiner,
67 NameOutConverter,
68 NameHalter);
69
70 return RNE;
71 }
72
getKey(FnIdent Kind)73 const char *RSExportReduce::getKey(FnIdent Kind) {
74 switch (Kind) {
75 default:
76 slangAssert(!"Unknown FnIdent");
77 // and fall through
78 case FN_IDENT_INITIALIZER:
79 return KeyInitializer;
80 case FN_IDENT_ACCUMULATOR:
81 return KeyAccumulator;
82 case FN_IDENT_COMBINER:
83 return KeyCombiner;
84 case FN_IDENT_OUT_CONVERTER:
85 return KeyOutConverter;
86 case FN_IDENT_HALTER:
87 return KeyHalter;
88 }
89 }
90
91 // This data is needed during analyzeTranslationUnit() but not afterwards.
92 // Breaking it out into a struct makes it easy for analyzeTranslationUnit()
93 // to call a number of helper functions that all need access to this data.
94 struct RSExportReduce::StateOfAnalyzeTranslationUnit {
95
96 typedef std::function<std::string (const char *Key, const std::string &Name)> DiagnosticDescriptionType;
97
StateOfAnalyzeTranslationUnitslang::RSExportReduce::StateOfAnalyzeTranslationUnit98 StateOfAnalyzeTranslationUnit(
99 RSContext &anRSContext,
100 clang::Preprocessor &aPP,
101 clang::ASTContext &anASTContext,
102 const DiagnosticDescriptionType &aDiagnosticDescription) :
103
104 RSC(anRSContext),
105 PP(aPP),
106 ASTC(anASTContext),
107 DiagnosticDescription(aDiagnosticDescription),
108
109 Ok(true),
110
111 FnInitializer(nullptr),
112 FnAccumulator(nullptr),
113 FnCombiner(nullptr),
114 FnOutConverter(nullptr),
115 FnHalter(nullptr),
116
117 FnInitializerParam(nullptr),
118 FnInitializerParamTy(),
119
120 FnAccumulatorOk(true),
121 FnAccumulatorParamFirst(nullptr),
122 FnAccumulatorParamFirstTy(),
123 FnAccumulatorIndexOfFirstSpecialParameter(0),
124
125 FnOutConverterOk(true),
126 FnOutConverterParamFirst(nullptr),
127 FnOutConverterParamFirstTy()
128 { }
129
130 /*-- Convenience ------------------------------------------*/
131
132 RSContext &RSC;
133 clang::Preprocessor &PP;
134 clang::ASTContext &ASTC;
135 const DiagnosticDescriptionType DiagnosticDescription;
136
137 /*-- Actual state -----------------------------------------*/
138
139 bool Ok;
140
141 clang::FunctionDecl *FnInitializer;
142 clang::FunctionDecl *FnAccumulator;
143 clang::FunctionDecl *FnCombiner;
144 clang::FunctionDecl *FnOutConverter;
145 clang::FunctionDecl *FnHalter;
146
147 clang::ParmVarDecl *FnInitializerParam;
148 clang::QualType FnInitializerParamTy;
149
150 bool FnAccumulatorOk;
151 clang::ParmVarDecl *FnAccumulatorParamFirst;
152 clang::QualType FnAccumulatorParamFirstTy;
153 size_t FnAccumulatorIndexOfFirstSpecialParameter;
154
155 bool FnOutConverterOk; // also true if no outconverter
156 clang::ParmVarDecl *FnOutConverterParamFirst;
157 clang::QualType FnOutConverterParamFirstTy;
158 };
159
160 // does update S.Ok
lookupFunction(StateOfAnalyzeTranslationUnit & S,const char * Kind,const llvm::StringRef & Name)161 clang::FunctionDecl *RSExportReduce::lookupFunction(StateOfAnalyzeTranslationUnit &S,
162 const char *Kind, const llvm::StringRef &Name) {
163 if (Name.empty())
164 return nullptr;
165
166 clang::TranslationUnitDecl *TUDecl = getRSContext()->getASTContext().getTranslationUnitDecl();
167 slangAssert(TUDecl);
168
169 clang::FunctionDecl *Ret = nullptr;
170 const clang::IdentifierInfo *II = S.PP.getIdentifierInfo(Name);
171 if (II) {
172 for (auto Decl : TUDecl->lookup(II)) {
173 clang::FunctionDecl *FDecl = Decl->getAsFunction();
174 if (!FDecl || !FDecl->isThisDeclarationADefinition())
175 continue;
176 if (Ret) {
177 S.RSC.ReportError(mLocation,
178 "duplicate function definition for '%0(%1)' for '#pragma rs %2(%3)' (%4, %5)")
179 << Kind << Name << KeyReduce << mNameReduce
180 << Ret->getLocation().printToString(S.PP.getSourceManager())
181 << FDecl->getLocation().printToString(S.PP.getSourceManager());
182 S.Ok = false;
183 return nullptr;
184 }
185 Ret = FDecl;
186 }
187 }
188 if (!Ret) {
189 // Either the identifier lookup failed, or we never found the function definition.
190 S.RSC.ReportError(mLocation,
191 "could not find function definition for '%0(%1)' for '#pragma rs %2(%3)'")
192 << Kind << Name << KeyReduce << mNameReduce;
193 S.Ok = false;
194 return nullptr;
195 }
196 if (Ret) {
197 // Must have internal linkage
198 if (Ret->getFormalLinkage() != clang::InternalLinkage) {
199 S.RSC.ReportError(Ret->getLocation(), "%0 must be static")
200 << S.DiagnosticDescription(Kind, Name);
201 S.Ok = false;
202 }
203 }
204 if (Ret == nullptr)
205 S.Ok = false;
206 return Ret;
207 }
208
209 // updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk
notOk(StateOfAnalyzeTranslationUnit & S,FnIdent Kind)210 void RSExportReduce::notOk(StateOfAnalyzeTranslationUnit &S, FnIdent Kind) {
211 S.Ok = false;
212 if (Kind == FN_IDENT_ACCUMULATOR) {
213 S.FnAccumulatorOk = false;
214 } else if (Kind == FN_IDENT_OUT_CONVERTER) {
215 S.FnOutConverterOk = false;
216 }
217 }
218
219 // updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk
checkVoidReturn(StateOfAnalyzeTranslationUnit & S,FnIdent Kind,clang::FunctionDecl * Fn)220 void RSExportReduce::checkVoidReturn(StateOfAnalyzeTranslationUnit &S,
221 FnIdent Kind, clang::FunctionDecl *Fn) {
222 slangAssert(Fn);
223 const clang::QualType ReturnTy = Fn->getReturnType().getCanonicalType();
224 if (!ReturnTy->isVoidType()) {
225 S.RSC.ReportError(Fn->getLocation(),
226 "%0 must return void not '%1'")
227 << S.DiagnosticDescription(getKey(Kind), Fn->getName()) << ReturnTy.getAsString();
228 notOk(S, Kind);
229 }
230 }
231
232 // updates S.Ok; and, depending on Kind, possibly S.FnAccumulatorOk or S.FnOutConverterOk
checkPointeeConstQualified(StateOfAnalyzeTranslationUnit & S,FnIdent Kind,const llvm::StringRef & Name,const clang::ParmVarDecl * Param,bool ExpectedQualification)233 void RSExportReduce::checkPointeeConstQualified(StateOfAnalyzeTranslationUnit &S,
234 FnIdent Kind, const llvm::StringRef &Name,
235 const clang::ParmVarDecl *Param, bool ExpectedQualification) {
236 const clang::QualType ParamQType = Param->getType();
237 slangAssert(ParamQType->isPointerType());
238 const clang::QualType PointeeQType = ParamQType->getPointeeType();
239 if (PointeeQType.isConstQualified() != ExpectedQualification) {
240 S.RSC.ReportError(Param->getLocation(),
241 "%0 parameter '%1' (type '%2') must%3 point to const-qualified type")
242 << S.DiagnosticDescription(getKey(Kind), Name)
243 << Param->getName() << ParamQType.getAsString()
244 << (ExpectedQualification ? "" : " not");
245 notOk(S, Kind);
246 }
247 }
248
249 // Process "void mNameInitializer(compType *accum)"
analyzeInitializer(StateOfAnalyzeTranslationUnit & S)250 void RSExportReduce::analyzeInitializer(StateOfAnalyzeTranslationUnit &S) {
251 if (!S.FnInitializer) // initializer is always optional
252 return;
253
254 // Must return void
255 checkVoidReturn(S, FN_IDENT_INITIALIZER, S.FnInitializer);
256
257 // Must have exactly one parameter
258 if (S.FnInitializer->getNumParams() != 1) {
259 S.RSC.ReportError(S.FnInitializer->getLocation(),
260 "%0 must take exactly 1 parameter (found %1)")
261 << S.DiagnosticDescription(KeyInitializer, mNameInitializer)
262 << S.FnInitializer->getNumParams();
263 S.Ok = false;
264 return;
265 }
266
267 // Parameter must not be a special parameter
268 S.FnInitializerParam = S.FnInitializer->getParamDecl(0);
269 if (isSpecialKernelParameter(S.FnInitializerParam->getName())) {
270 S.RSC.ReportError(S.FnInitializer->getLocation(),
271 "%0 cannot take special parameter '%1'")
272 << S.DiagnosticDescription(KeyInitializer, mNameInitializer)
273 << S.FnInitializerParam->getName();
274 S.Ok = false;
275 return;
276 }
277
278 // Parameter must be of pointer type
279 S.FnInitializerParamTy = S.FnInitializerParam->getType().getCanonicalType();
280 if (!S.FnInitializerParamTy->isPointerType()) {
281 S.RSC.ReportError(S.FnInitializer->getLocation(),
282 "%0 parameter '%1' must be of pointer type not '%2'")
283 << S.DiagnosticDescription(KeyInitializer, mNameInitializer)
284 << S.FnInitializerParam->getName() << S.FnInitializerParamTy.getAsString();
285 S.Ok = false;
286 return;
287 }
288
289 // Parameter must not point to const-qualified
290 checkPointeeConstQualified(S, FN_IDENT_INITIALIZER, mNameInitializer, S.FnInitializerParam, false);
291 }
292
293 // Process "void mNameAccumulator(compType *accum, in1Type in1, …, inNType inN[, specialarguments])"
analyzeAccumulator(StateOfAnalyzeTranslationUnit & S)294 void RSExportReduce::analyzeAccumulator(StateOfAnalyzeTranslationUnit &S) {
295 slangAssert(S.FnAccumulator);
296
297 // Must return void
298 checkVoidReturn(S, FN_IDENT_ACCUMULATOR, S.FnAccumulator);
299
300 // Must have initial parameter of same type as initializer parameter
301 // (if there is an initializer), followed by at least 1 input
302
303 if (S.FnAccumulator->getNumParams() < 2) {
304 S.RSC.ReportError(S.FnAccumulator->getLocation(),
305 "%0 must take at least 2 parameters")
306 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator);
307 S.Ok = S.FnAccumulatorOk = false;
308 return;
309 }
310
311 S.FnAccumulatorParamFirst = S.FnAccumulator->getParamDecl(0);
312 S.FnAccumulatorParamFirstTy = S.FnAccumulatorParamFirst->getType().getCanonicalType();
313
314 // First parameter must be of pointer type
315 if (!S.FnAccumulatorParamFirstTy->isPointerType()) {
316 S.RSC.ReportError(S.FnAccumulator->getLocation(),
317 "%0 parameter '%1' must be of pointer type not '%2'")
318 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
319 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
320 S.Ok = S.FnAccumulatorOk = false;
321 return;
322 }
323
324 // If there is an initializer with a pointer-typed parameter (as
325 // opposed to an initializer with a bad parameter list), then
326 // accumulator first parameter must be of same type as initializer
327 // parameter
328 if (S.FnInitializer &&
329 !S.FnInitializerParamTy.isNull() &&
330 S.FnInitializerParamTy->isPointerType() &&
331 !S.FnAccumulator->getASTContext().hasSameUnqualifiedType(
332 S.FnInitializerParamTy->getPointeeType().getCanonicalType(),
333 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType())) {
334 // <accumulator> parameter '<baz>' (type '<tbaz>') and initializer <goo>() parameter '<gaz>' (type '<tgaz>')
335 // must be pointers to the same type
336 S.RSC.ReportError(S.FnAccumulator->getLocation(),
337 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
338 " must be pointers to the same type")
339 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
340 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString()
341 << KeyInitializer << mNameInitializer
342 << S.FnInitializerParam->getName() << S.FnInitializerParamTy.getAsString();
343 S.Ok = S.FnAccumulatorOk = false;
344 }
345
346 if (S.FnAccumulatorOk && S.FnAccumulatorParamFirstTy->getPointeeType()->isFunctionType()) {
347 S.RSC.ReportError(S.FnAccumulator->getLocation(),
348 "%0 parameter '%1' (type '%2') must not be pointer to function type")
349 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
350 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
351 S.Ok = S.FnAccumulatorOk = false;
352 }
353
354 if (S.FnAccumulatorOk && S.FnAccumulatorParamFirstTy->getPointeeType()->isIncompleteType()) {
355 S.RSC.ReportError(S.FnAccumulator->getLocation(),
356 "%0 parameter '%1' (type '%2') must not be pointer to incomplete type")
357 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
358 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
359 S.Ok = S.FnAccumulatorOk = false;
360 }
361
362 if (S.FnAccumulatorOk &&
363 HasRSObjectType(S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType().getTypePtr())) {
364 S.RSC.ReportError(S.FnAccumulator->getLocation(),
365 "%0 parameter '%1' (type '%2') must not be pointer to data containing an object type")
366 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
367 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
368 S.Ok = S.FnAccumulatorOk = false;
369 }
370
371 // Parameter must not point to const-qualified
372 checkPointeeConstQualified(S, FN_IDENT_ACCUMULATOR, mNameAccumulator, S.FnAccumulatorParamFirst, false);
373
374 // Analyze special parameters
375 S.Ok &= (S.FnAccumulatorOk &= processSpecialKernelParameters(
376 &S.RSC,
377 std::bind(S.DiagnosticDescription, KeyAccumulator, mNameAccumulator),
378 S.FnAccumulator,
379 &S.FnAccumulatorIndexOfFirstSpecialParameter,
380 &mAccumulatorSignatureMetadata));
381
382 // Must have at least an accumulator and an input.
383 // If we get here we know there are at least 2 arguments; so the only problem case is
384 // where we have an accumulator followed immediately by a special parameter.
385 if (S.FnAccumulatorIndexOfFirstSpecialParameter < 2) {
386 slangAssert(S.FnAccumulatorIndexOfFirstSpecialParameter < S.FnAccumulator->getNumParams());
387 S.RSC.ReportError(S.FnAccumulator->getLocation(),
388 "%0 must have at least 1 input ('%1' is a special parameter)")
389 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
390 << S.FnAccumulator->getParamDecl(S.FnAccumulatorIndexOfFirstSpecialParameter)->getName();
391 S.Ok = S.FnAccumulatorOk = false;
392 return;
393 }
394
395 if (S.FnAccumulatorOk) {
396 mAccumulatorSignatureMetadata |= bcinfo::MD_SIG_In;
397 mAccumulatorTypeSize = S.ASTC.getTypeSizeInChars(S.FnAccumulatorParamFirstTy->getPointeeType()).getQuantity();
398 for (size_t ParamIdx = 1; ParamIdx < S.FnAccumulatorIndexOfFirstSpecialParameter; ++ParamIdx) {
399 const clang::ParmVarDecl *const Param = S.FnAccumulator->getParamDecl(ParamIdx);
400 mAccumulatorIns.push_back(Param);
401 const clang::QualType ParamQType = Param->getType().getCanonicalType();
402 const clang::Type *ParamType = ParamQType.getTypePtr();
403
404 RSExportType *ParamEType = nullptr;
405 if (ParamQType->isPointerType()) {
406 S.RSC.ReportError(Param->getLocation(),
407 "%0 parameter '%1' (type '%2') must not be a pointer")
408 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
409 << Param->getName() << ParamQType.getAsString();
410 S.Ok = false;
411 } else if (HasRSObjectType(ParamType)) {
412 S.RSC.ReportError(Param->getLocation(),
413 "%0 parameter '%1' (type '%2') must not contain an object type")
414 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
415 << Param->getName() << ParamQType.getAsString();
416 S.Ok = false;
417 } else if (RSExportType::ValidateType(&S.RSC, S.ASTC, ParamQType, Param, Param->getLocStart(),
418 S.RSC.getTargetAPI(),
419 false /* IsFilterscript */,
420 true /* IsExtern */)) {
421 // TODO: Better diagnostics on validation or creation failure?
422 ParamEType = RSExportType::Create(&S.RSC, ParamType, NotLegacyKernelArgument);
423 S.Ok &= (ParamEType != nullptr);
424 } else {
425 S.Ok = false;
426 }
427 mAccumulatorInTypes.push_back(ParamEType); // possibly nullptr
428 }
429 }
430 }
431
432 // Process "void combinename(compType *accum, const compType *val)"
analyzeCombiner(StateOfAnalyzeTranslationUnit & S)433 void RSExportReduce::analyzeCombiner(StateOfAnalyzeTranslationUnit &S) {
434 if (S.FnCombiner) {
435 // Must return void
436 checkVoidReturn(S, FN_IDENT_COMBINER, S.FnCombiner);
437
438 // Must have exactly two parameters, of same type as first accumulator parameter
439
440 if (S.FnCombiner->getNumParams() != 2) {
441 S.RSC.ReportError(S.FnCombiner->getLocation(),
442 "%0 must take exactly 2 parameters (found %1)")
443 << S.DiagnosticDescription(KeyCombiner, mNameCombiner)
444 << S.FnCombiner->getNumParams();
445 S.Ok = false;
446 return;
447 }
448
449 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
450 // We're already in an error situation. We could compare
451 // against the initializer parameter type instead of the first
452 // accumulator parameter type (we'd have to check for the
453 // availability of a parameter type there, too), but it does not
454 // seem worth the effort.
455 //
456 // Likewise, we could compare the two combiner parameter types
457 // against each other.
458 slangAssert(!S.Ok);
459 return;
460 }
461
462 for (int ParamIdx = 0; ParamIdx < 2; ++ParamIdx) {
463 const clang::ParmVarDecl *const FnCombinerParam = S.FnCombiner->getParamDecl(ParamIdx);
464 const clang::QualType FnCombinerParamTy = FnCombinerParam->getType().getCanonicalType();
465 if (!FnCombinerParamTy->isPointerType() ||
466 !S.FnCombiner->getASTContext().hasSameUnqualifiedType(
467 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
468 FnCombinerParamTy->getPointeeType().getCanonicalType())) {
469 // <combiner> parameter '<baz>' (type '<tbaz>')
470 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
471 S.RSC.ReportError(S.FnCombiner->getLocation(),
472 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
473 " must be pointers to the same type")
474 << S.DiagnosticDescription(KeyCombiner, mNameCombiner)
475 << FnCombinerParam->getName() << FnCombinerParamTy.getAsString()
476 << KeyAccumulator << mNameAccumulator
477 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
478 S.Ok = false;
479 } else {
480 // Check const-qualification
481 checkPointeeConstQualified(S, FN_IDENT_COMBINER, mNameCombiner, FnCombinerParam, ParamIdx==1);
482 }
483 }
484
485 return;
486 }
487
488 // Ensure accumulator properties permit omission of combiner.
489
490 if (!S.FnAccumulatorOk) {
491 // Couldn't fully analyze accumulator, so cannot see whether it permits omission of combiner.
492 return;
493 }
494
495 if (mAccumulatorIns.size() != 1 ||
496 S.FnAccumulatorIndexOfFirstSpecialParameter != S.FnAccumulator->getNumParams())
497 {
498 S.RSC.ReportError(S.FnAccumulator->getLocation(),
499 "%0 must have exactly 1 input"
500 " and no special parameters in order for the %1 to be omitted")
501 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
502 << KeyCombiner;
503 S.Ok = false;
504 return;
505 }
506
507 const clang::ParmVarDecl *const FnAccumulatorParamInput = S.FnAccumulator->getParamDecl(1);
508 const clang::QualType FnAccumulatorParamInputTy = FnAccumulatorParamInput->getType().getCanonicalType();
509 if (!S.FnAccumulator->getASTContext().hasSameUnqualifiedType(
510 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
511 FnAccumulatorParamInputTy.getCanonicalType())) {
512 S.RSC.ReportError(S.FnAccumulator->getLocation(),
513 "%0 parameter '%1' (type '%2')"
514 " must be pointer to the type of parameter '%3' (type '%4')"
515 " in order for the %5 to be omitted")
516 << S.DiagnosticDescription(KeyAccumulator, mNameAccumulator)
517 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString()
518 << FnAccumulatorParamInput->getName() << FnAccumulatorParamInputTy.getAsString()
519 << KeyCombiner;
520 S.Ok = false;
521 }
522 }
523
524 // Process "void outconvertname(resultType *result, const compType *accum)"
analyzeOutConverter(StateOfAnalyzeTranslationUnit & S)525 void RSExportReduce::analyzeOutConverter(StateOfAnalyzeTranslationUnit &S) {
526 if (!S.FnOutConverter) // outconverter is always optional
527 return;
528
529 // Must return void
530 checkVoidReturn(S, FN_IDENT_OUT_CONVERTER, S.FnOutConverter);
531
532 // Must have exactly two parameters
533 if (S.FnOutConverter->getNumParams() != 2) {
534 S.RSC.ReportError(S.FnOutConverter->getLocation(),
535 "%0 must take exactly 2 parameters (found %1)")
536 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
537 << S.FnOutConverter->getNumParams();
538 S.Ok = S.FnOutConverterOk = false;
539 return;
540 }
541
542 // Parameters must not be special and must be of pointer type;
543 // and second parameter must match first accumulator parameter
544 for (int ParamIdx = 0; ParamIdx < 2; ++ParamIdx) {
545 clang::ParmVarDecl *const FnOutConverterParam = S.FnOutConverter->getParamDecl(ParamIdx);
546
547 if (isSpecialKernelParameter(FnOutConverterParam->getName())) {
548 S.RSC.ReportError(S.FnOutConverter->getLocation(),
549 "%0 cannot take special parameter '%1'")
550 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
551 << FnOutConverterParam->getName();
552 S.Ok = S.FnOutConverterOk = false;
553 continue;
554 }
555
556 const clang::QualType FnOutConverterParamTy = FnOutConverterParam->getType().getCanonicalType();
557
558 if (!FnOutConverterParamTy->isPointerType()) {
559 S.RSC.ReportError(S.FnOutConverter->getLocation(),
560 "%0 parameter '%1' must be of pointer type not '%2'")
561 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
562 << FnOutConverterParam->getName() << FnOutConverterParamTy.getAsString();
563 S.Ok = S.FnOutConverterOk = false;
564 continue;
565 }
566
567 // Check const-qualification
568 checkPointeeConstQualified(S, FN_IDENT_OUT_CONVERTER, mNameOutConverter, FnOutConverterParam, ParamIdx==1);
569
570 if (ParamIdx == 0) {
571 S.FnOutConverterParamFirst = FnOutConverterParam;
572 S.FnOutConverterParamFirstTy = FnOutConverterParamTy;
573 continue;
574 }
575
576 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
577 // We're already in an error situation. We could compare
578 // against the initializer parameter type instead of the first
579 // accumulator parameter type (we'd have to check for the
580 // availability of a parameter type there, too), but it does not
581 // seem worth the effort.
582 slangAssert(!S.Ok);
583 continue;
584 }
585
586 if (!S.FnOutConverter->getASTContext().hasSameUnqualifiedType(
587 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
588 FnOutConverterParamTy->getPointeeType().getCanonicalType())) {
589 // <outconverter> parameter '<baz>' (type '<tbaz>')
590 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
591 S.RSC.ReportError(S.FnOutConverter->getLocation(),
592 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
593 " must be pointers to the same type")
594 << S.DiagnosticDescription(KeyOutConverter, mNameOutConverter)
595 << FnOutConverterParam->getName() << FnOutConverterParamTy.getAsString()
596 << KeyAccumulator << mNameAccumulator
597 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
598 S.Ok = S.FnOutConverterOk = false;
599 }
600 }
601 }
602
analyzeHalter(StateOfAnalyzeTranslationUnit & S)603 void RSExportReduce::analyzeHalter(StateOfAnalyzeTranslationUnit &S) {
604 if (!S.FnHalter) // halter is always optional
605 return;
606
607 // Must return bool
608 const clang::QualType ReturnTy = S.FnHalter->getReturnType().getCanonicalType();
609 if (!ReturnTy->isBooleanType()) {
610 S.RSC.ReportError(S.FnHalter->getLocation(),
611 "%0 must return bool not '%1'")
612 << S.DiagnosticDescription(KeyHalter, mNameHalter) << ReturnTy.getAsString();
613 S.Ok = false;
614 }
615
616 // Must have exactly one parameter
617 if (S.FnHalter->getNumParams() != 1) {
618 S.RSC.ReportError(S.FnHalter->getLocation(),
619 "%0 must take exactly 1 parameter (found %1)")
620 << S.DiagnosticDescription(KeyHalter, mNameHalter)
621 << S.FnHalter->getNumParams();
622 S.Ok = false;
623 return;
624 }
625
626 // Parameter must not be a special parameter
627 const clang::ParmVarDecl *const FnHalterParam = S.FnHalter->getParamDecl(0);
628 if (isSpecialKernelParameter(FnHalterParam->getName())) {
629 S.RSC.ReportError(S.FnHalter->getLocation(),
630 "%0 cannot take special parameter '%1'")
631 << S.DiagnosticDescription(KeyHalter, mNameHalter)
632 << FnHalterParam->getName();
633 S.Ok = false;
634 return;
635 }
636
637 // Parameter must be same type as first accumulator parameter
638
639 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
640 // We're already in an error situation. We could compare against
641 // the initializer parameter type or the first combiner parameter
642 // type instead of the first accumulator parameter type (we'd have
643 // to check for the availability of a parameter type there, too),
644 // but it does not seem worth the effort.
645 slangAssert(!S.Ok);
646 return;
647 }
648
649 const clang::QualType FnHalterParamTy = FnHalterParam->getType().getCanonicalType();
650 if (!FnHalterParamTy->isPointerType() ||
651 !S.FnHalter->getASTContext().hasSameUnqualifiedType(
652 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
653 FnHalterParamTy->getPointeeType().getCanonicalType())) {
654 // <halter> parameter '<baz>' (type '<tbaz>')
655 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
656 S.RSC.ReportError(S.FnHalter->getLocation(),
657 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
658 " must be pointers to the same type")
659 << S.DiagnosticDescription(KeyHalter, mNameHalter)
660 << FnHalterParam->getName() << FnHalterParamTy.getAsString()
661 << KeyAccumulator << mNameAccumulator
662 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
663 S.Ok = false;
664 return;
665 }
666
667 // Parameter must point to const-qualified
668 checkPointeeConstQualified(S, FN_IDENT_HALTER, mNameHalter, FnHalterParam, true);
669 }
670
analyzeResultType(StateOfAnalyzeTranslationUnit & S)671 void RSExportReduce::analyzeResultType(StateOfAnalyzeTranslationUnit &S) {
672 if (!(S.FnAccumulatorOk && S.FnOutConverterOk)) {
673 // No idea what the result type is
674 slangAssert(!S.Ok);
675 return;
676 }
677
678 struct ResultInfoType {
679 const clang::QualType QType;
680 clang::VarDecl *const Decl;
681 const char *FnKey;
682 const std::string &FnName;
683 std::function<std::string ()> UnlessOutConverter;
684 } ResultInfo =
685 S.FnOutConverter
686 ? ResultInfoType({ S.FnOutConverterParamFirstTy, S.FnOutConverterParamFirst,
687 KeyOutConverter, mNameOutConverter,
688 []() { return std::string(""); }})
689 : ResultInfoType({ S.FnAccumulatorParamFirstTy, S.FnAccumulatorParamFirst,
690 KeyAccumulator, mNameAccumulator,
691 []() { return std::string(" unless ") + KeyOutConverter + " is provided"; }});
692 const clang::QualType PointeeQType = ResultInfo.QType->getPointeeType();
693
694 if (PointeeQType->isPointerType()) {
695 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
696 "%0 parameter '%1' (type '%2') must not point to a pointer%3")
697 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
698 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
699 << ResultInfo.UnlessOutConverter();
700 } else if (PointeeQType->isIncompleteType()) {
701 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
702 "%0 parameter '%1' (type '%2') must not point to an incomplete type%3")
703 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
704 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
705 << ResultInfo.UnlessOutConverter();
706 } else if (HasRSObjectType(PointeeQType.getTypePtr())) {
707 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
708 "%0 parameter '%1' (type '%2') must not point to data containing an object type%3")
709 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
710 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
711 << ResultInfo.UnlessOutConverter();
712 } else if (RSExportType::ValidateType(&S.RSC, S.ASTC, PointeeQType,
713 ResultInfo.Decl, ResultInfo.Decl->getLocStart(),
714 S.RSC.getTargetAPI(),
715 false /* IsFilterscript */,
716 true /* IsExtern */)) {
717 // TODO: Better diagnostics on validation or creation failure?
718 if ((mResultType = RSExportType::Create(&S.RSC, PointeeQType.getTypePtr(),
719 NotLegacyKernelArgument, ResultInfo.Decl)) != nullptr) {
720 const RSExportType *CheckType = mResultType;
721 const char *ArrayErrorPhrase = "";
722 if (mResultType->getClass() == RSExportType::ExportClassConstantArray) {
723 CheckType = static_cast<const RSExportConstantArrayType *>(mResultType)->getElementType();
724 ArrayErrorPhrase = "n array of";
725 }
726 switch (CheckType->getClass()) {
727 case RSExportType::ExportClassMatrix:
728 // Not supported for now -- what does a matrix result type mean?
729 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
730 "%0 parameter '%1' (type '%2') must not point to a%3 matrix type%4")
731 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
732 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
733 << ArrayErrorPhrase
734 << ResultInfo.UnlessOutConverter();
735 mResultType = nullptr;
736 break;
737 default:
738 // All's well
739 break;
740 }
741 }
742 }
743
744 if (mResultType)
745 S.RSC.insertExportReduceResultType(mResultType);
746 else
747 S.Ok = false;
748 }
749
analyzeTranslationUnit()750 bool RSExportReduce::analyzeTranslationUnit() {
751
752 RSContext &RSC = *getRSContext();
753 clang::Preprocessor &PP = RSC.getPreprocessor();
754
755 StateOfAnalyzeTranslationUnit S(
756 RSC, PP, RSC.getASTContext(),
757 [&RSC, &PP, this] (const char *Key, const std::string &Name) {
758 std::ostringstream Description;
759 Description
760 << Key << " " << Name << "()"
761 << " for '#pragma rs " << KeyReduce << "(" << mNameReduce << ")'"
762 << " (" << mLocation.printToString(PP.getSourceManager()) << ")";
763 return Description.str();
764 });
765
766 S.FnInitializer = lookupFunction(S, KeyInitializer, mNameInitializer);
767 S.FnAccumulator = lookupFunction(S, KeyAccumulator, mNameAccumulator);
768 S.FnCombiner = lookupFunction(S, KeyCombiner, mNameCombiner);
769 S.FnOutConverter = lookupFunction(S, KeyOutConverter, mNameOutConverter);
770 S.FnHalter = lookupFunction(S, KeyHalter, mNameHalter);
771
772 if (!S.Ok)
773 return false;
774
775 analyzeInitializer(S);
776 analyzeAccumulator(S);
777 analyzeCombiner(S);
778 analyzeOutConverter(S);
779 analyzeHalter(S);
780 analyzeResultType(S);
781
782 return S.Ok;
783 }
784
785 } // namespace slang
786