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