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