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
603 // Process "bool haltername(const compType *accum)"
analyzeHalter(StateOfAnalyzeTranslationUnit & S)604 void RSExportReduce::analyzeHalter(StateOfAnalyzeTranslationUnit &S) {
605 if (!S.FnHalter) // halter is always optional
606 return;
607
608 // Must return bool
609 const clang::QualType ReturnTy = S.FnHalter->getReturnType().getCanonicalType();
610 if (!ReturnTy->isBooleanType()) {
611 S.RSC.ReportError(S.FnHalter->getLocation(),
612 "%0 must return bool not '%1'")
613 << S.DiagnosticDescription(KeyHalter, mNameHalter) << ReturnTy.getAsString();
614 S.Ok = false;
615 }
616
617 // Must have exactly one parameter
618 if (S.FnHalter->getNumParams() != 1) {
619 S.RSC.ReportError(S.FnHalter->getLocation(),
620 "%0 must take exactly 1 parameter (found %1)")
621 << S.DiagnosticDescription(KeyHalter, mNameHalter)
622 << S.FnHalter->getNumParams();
623 S.Ok = false;
624 return;
625 }
626
627 // Parameter must not be a special parameter
628 const clang::ParmVarDecl *const FnHalterParam = S.FnHalter->getParamDecl(0);
629 if (isSpecialKernelParameter(FnHalterParam->getName())) {
630 S.RSC.ReportError(S.FnHalter->getLocation(),
631 "%0 cannot take special parameter '%1'")
632 << S.DiagnosticDescription(KeyHalter, mNameHalter)
633 << FnHalterParam->getName();
634 S.Ok = false;
635 return;
636 }
637
638 // Parameter must be same type as first accumulator parameter
639
640 if (S.FnAccumulatorParamFirstTy.isNull() || !S.FnAccumulatorParamFirstTy->isPointerType()) {
641 // We're already in an error situation. We could compare against
642 // the initializer parameter type or the first combiner parameter
643 // type instead of the first accumulator parameter type (we'd have
644 // to check for the availability of a parameter type there, too),
645 // but it does not seem worth the effort.
646 slangAssert(!S.Ok);
647 return;
648 }
649
650 const clang::QualType FnHalterParamTy = FnHalterParam->getType().getCanonicalType();
651 if (!FnHalterParamTy->isPointerType() ||
652 !S.FnHalter->getASTContext().hasSameUnqualifiedType(
653 S.FnAccumulatorParamFirstTy->getPointeeType().getCanonicalType(),
654 FnHalterParamTy->getPointeeType().getCanonicalType())) {
655 // <halter> parameter '<baz>' (type '<tbaz>')
656 // and accumulator <goo>() parameter '<gaz>' (type '<tgaz>') must be pointers to the same type
657 S.RSC.ReportError(S.FnHalter->getLocation(),
658 "%0 parameter '%1' (type '%2') and %3 %4() parameter '%5' (type '%6')"
659 " must be pointers to the same type")
660 << S.DiagnosticDescription(KeyHalter, mNameHalter)
661 << FnHalterParam->getName() << FnHalterParamTy.getAsString()
662 << KeyAccumulator << mNameAccumulator
663 << S.FnAccumulatorParamFirst->getName() << S.FnAccumulatorParamFirstTy.getAsString();
664 S.Ok = false;
665 return;
666 }
667
668 // Parameter must point to const-qualified
669 checkPointeeConstQualified(S, FN_IDENT_HALTER, mNameHalter, FnHalterParam, true);
670 }
671
analyzeResultType(StateOfAnalyzeTranslationUnit & S)672 void RSExportReduce::analyzeResultType(StateOfAnalyzeTranslationUnit &S) {
673 if (!(S.FnAccumulatorOk && S.FnOutConverterOk)) {
674 // No idea what the result type is
675 slangAssert(!S.Ok);
676 return;
677 }
678
679 struct ResultInfoType {
680 const clang::QualType QType;
681 clang::VarDecl *const Decl;
682 const char *FnKey;
683 const std::string &FnName;
684 std::function<std::string ()> UnlessOutConverter;
685 } ResultInfo =
686 S.FnOutConverter
687 ? ResultInfoType({ S.FnOutConverterParamFirstTy, S.FnOutConverterParamFirst,
688 KeyOutConverter, mNameOutConverter,
689 []() { return std::string(""); }})
690 : ResultInfoType({ S.FnAccumulatorParamFirstTy, S.FnAccumulatorParamFirst,
691 KeyAccumulator, mNameAccumulator,
692 []() { return std::string(" unless ") + KeyOutConverter + " is provided"; }});
693 const clang::QualType PointeeQType = ResultInfo.QType->getPointeeType();
694
695 if (PointeeQType->isPointerType()) {
696 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
697 "%0 parameter '%1' (type '%2') must not point to a pointer%3")
698 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
699 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
700 << ResultInfo.UnlessOutConverter();
701 } else if (PointeeQType->isIncompleteType()) {
702 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
703 "%0 parameter '%1' (type '%2') must not point to an incomplete type%3")
704 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
705 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
706 << ResultInfo.UnlessOutConverter();
707 } else if (HasRSObjectType(PointeeQType.getTypePtr())) {
708 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
709 "%0 parameter '%1' (type '%2') must not point to data containing an object type%3")
710 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
711 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
712 << ResultInfo.UnlessOutConverter();
713 } else if (RSExportType::ValidateType(&S.RSC, S.ASTC, PointeeQType,
714 ResultInfo.Decl, ResultInfo.Decl->getLocStart(),
715 S.RSC.getTargetAPI(),
716 false /* IsFilterscript */,
717 true /* IsExtern */)) {
718 // TODO: Better diagnostics on validation or creation failure?
719 if ((mResultType = RSExportType::Create(&S.RSC, PointeeQType.getTypePtr(),
720 NotLegacyKernelArgument, ResultInfo.Decl)) != nullptr) {
721 const RSExportType *CheckType = mResultType;
722 const char *ArrayErrorPhrase = "";
723 if (mResultType->getClass() == RSExportType::ExportClassConstantArray) {
724 CheckType = static_cast<const RSExportConstantArrayType *>(mResultType)->getElementType();
725 ArrayErrorPhrase = "n array of";
726 }
727 switch (CheckType->getClass()) {
728 case RSExportType::ExportClassMatrix:
729 // Not supported for now -- what does a matrix result type mean?
730 S.RSC.ReportError(ResultInfo.Decl->getLocation(),
731 "%0 parameter '%1' (type '%2') must not point to a%3 matrix type%4")
732 << S.DiagnosticDescription(ResultInfo.FnKey, ResultInfo.FnName)
733 << ResultInfo.Decl->getName() << ResultInfo.QType.getAsString()
734 << ArrayErrorPhrase
735 << ResultInfo.UnlessOutConverter();
736 mResultType = nullptr;
737 break;
738 default:
739 // All's well
740 break;
741 }
742 }
743 }
744
745 if (mResultType)
746 S.RSC.insertExportReduceResultType(mResultType);
747 else
748 S.Ok = false;
749 }
750
analyzeTranslationUnit()751 bool RSExportReduce::analyzeTranslationUnit() {
752
753 RSContext &RSC = *getRSContext();
754 clang::Preprocessor &PP = RSC.getPreprocessor();
755
756 StateOfAnalyzeTranslationUnit S(
757 RSC, PP, RSC.getASTContext(),
758 [&PP, this] (const char *Key, const std::string &Name) {
759 std::ostringstream Description;
760 Description
761 << Key << " " << Name << "()"
762 << " for '#pragma rs " << KeyReduce << "(" << mNameReduce << ")'"
763 << " (" << mLocation.printToString(PP.getSourceManager()) << ")";
764 return Description.str();
765 });
766
767 S.FnInitializer = lookupFunction(S, KeyInitializer, mNameInitializer);
768 S.FnAccumulator = lookupFunction(S, KeyAccumulator, mNameAccumulator);
769 S.FnCombiner = lookupFunction(S, KeyCombiner, mNameCombiner);
770 S.FnOutConverter = lookupFunction(S, KeyOutConverter, mNameOutConverter);
771 S.FnHalter = lookupFunction(S, KeyHalter, mNameHalter);
772
773 if (!S.Ok)
774 return false;
775
776 analyzeInitializer(S);
777 analyzeAccumulator(S);
778 analyzeCombiner(S);
779 analyzeOutConverter(S);
780 analyzeHalter(S);
781 analyzeResultType(S);
782
783 return S.Ok;
784 }
785
786 } // namespace slang
787