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
34namespace slang {
35
36const char RSExportReduce::KeyReduce[] = "reduce";
37const char RSExportReduce::KeyInitializer[] = "initializer";
38const char RSExportReduce::KeyAccumulator[] = "accumulator";
39const char RSExportReduce::KeyCombiner[] = "combiner";
40const char RSExportReduce::KeyOutConverter[] = "outconverter";
41const char RSExportReduce::KeyHalter[] = "halter";
42
43bool 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
52RSExportReduce *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
73const 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.
94struct RSExportReduce::StateOfAnalyzeTranslationUnit {
95
96  typedef std::function<std::string (const char *Key, const std::string &Name)> DiagnosticDescriptionType;
97
98  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
161clang::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
210void 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
220void 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
233void 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)"
250void 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])"
294void 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)"
433void 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)"
525void 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)"
604void 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
672void 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
751bool RSExportReduce::analyzeTranslationUnit() {
752
753  RSContext &RSC = *getRSContext();
754  clang::Preprocessor &PP = RSC.getPreprocessor();
755
756  StateOfAnalyzeTranslationUnit S(
757      RSC, PP, RSC.getASTContext(),
758      [&RSC, &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