slang_rs_export_foreach.cpp revision ab94bccca64c9b126cbd1b732aa5e681d8639b99
1/*
2 * Copyright 2011-2012, 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_foreach.h"
18
19#include <string>
20
21#include "clang/AST/ASTContext.h"
22#include "clang/AST/Attr.h"
23#include "clang/AST/Decl.h"
24#include "clang/AST/TypeLoc.h"
25
26#include "llvm/IR/DerivedTypes.h"
27
28#include "bcinfo/MetadataExtractor.h"
29
30#include "slang_assert.h"
31#include "slang_rs_context.h"
32#include "slang_rs_export_type.h"
33#include "slang_version.h"
34
35namespace {
36
37const size_t RS_KERNEL_INPUT_LIMIT = 8; // see frameworks/base/libs/rs/cpu_ref/rsCpuCoreRuntime.h
38
39enum SpecialParameterKind {
40  SPK_INT,  // 'int' or 'unsigned int'
41  SPK_CTXT, // rs_kernel_context
42};
43
44struct SpecialParameter {
45  const char *name;
46  bcinfo::MetadataSignatureBitval bitval;
47  SpecialParameterKind kind;
48  SlangTargetAPI minAPI;
49};
50
51// Table entries are in the order parameters must occur in a kernel parameter list.
52const SpecialParameter specialParameterTable[] = {
53  { "ctxt", bcinfo::MD_SIG_Ctxt, SPK_CTXT, SLANG_23_TARGET_API },
54  { "x", bcinfo::MD_SIG_X, SPK_INT, SLANG_MINIMUM_TARGET_API },
55  { "y", bcinfo::MD_SIG_Y, SPK_INT, SLANG_MINIMUM_TARGET_API },
56  { "z", bcinfo::MD_SIG_Z, SPK_INT, SLANG_23_TARGET_API },
57  { nullptr, bcinfo::MD_SIG_None, SPK_INT, SLANG_MINIMUM_TARGET_API }, // marks end of table
58};
59
60// If the specified name matches the name of an entry in
61// specialParameterTable, return the corresponding table index;
62// otherwise return -1.
63int lookupSpecialParameter(const llvm::StringRef name) {
64  for (int i = 0; specialParameterTable[i].name != nullptr; ++i)
65    if (name.equals(specialParameterTable[i].name))
66      return i;
67  return -1;
68}
69
70// Return a comma-separated list of names in specialParameterTable
71// that are available at the specified API level.
72std::string listSpecialParameters(unsigned int api) {
73  std::string ret;
74  bool first = true;
75  for (int i = 0; specialParameterTable[i].name != nullptr; ++i) {
76    if (specialParameterTable[i].minAPI > api)
77      continue;
78    if (first)
79      first = false;
80    else
81      ret += ", ";
82    ret += "'";
83    ret += specialParameterTable[i].name;
84    ret += "'";
85  }
86  return ret;
87}
88
89}
90
91namespace slang {
92
93// This function takes care of additional validation and construction of
94// parameters related to forEach_* reflection.
95bool RSExportForEach::validateAndConstructParams(
96    RSContext *Context, const clang::FunctionDecl *FD) {
97  slangAssert(Context && FD);
98  bool valid = true;
99
100  numParams = FD->getNumParams();
101
102  if (Context->getTargetAPI() < SLANG_JB_TARGET_API) {
103    // Before JellyBean, we allowed only one kernel per file.  It must be called "root".
104    if (!isRootRSFunc(FD)) {
105      Context->ReportError(FD->getLocation(),
106                           "Non-root compute kernel %0() is "
107                           "not supported in SDK levels %1-%2")
108          << FD->getName() << SLANG_MINIMUM_TARGET_API
109          << (SLANG_JB_TARGET_API - 1);
110      return false;
111    }
112  }
113
114  mResultType = FD->getReturnType().getCanonicalType();
115  // Compute kernel functions are defined differently when the
116  // "__attribute__((kernel))" is set.
117  if (FD->hasAttr<clang::KernelAttr>()) {
118    valid |= validateAndConstructKernelParams(Context, FD);
119  } else {
120    valid |= validateAndConstructOldStyleParams(Context, FD);
121  }
122
123  valid |= setSignatureMetadata(Context, FD);
124  return valid;
125}
126
127bool RSExportForEach::validateAndConstructOldStyleParams(
128    RSContext *Context, const clang::FunctionDecl *FD) {
129  slangAssert(Context && FD);
130  // If numParams is 0, we already marked this as a graphics root().
131  slangAssert(numParams > 0);
132
133  bool valid = true;
134
135  // Compute kernel functions of this style are required to return a void type.
136  clang::ASTContext &C = Context->getASTContext();
137  if (mResultType != C.VoidTy) {
138    Context->ReportError(FD->getLocation(),
139                         "Compute kernel %0() is required to return a "
140                         "void type")
141        << FD->getName();
142    valid = false;
143  }
144
145  // Validate remaining parameter types
146
147  size_t IndexOfFirstSpecialParameter = numParams;
148  valid |= validateSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter);
149
150  // Validate the non-special parameters, which should all be found before the
151  // first special parameter.
152  for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) {
153    const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
154    clang::QualType QT = PVD->getType().getCanonicalType();
155
156    if (!QT->isPointerType()) {
157      Context->ReportError(PVD->getLocation(),
158                           "Compute kernel %0() cannot have non-pointer "
159                           "parameters besides (%1). Parameter '%2' is "
160                           "of type: '%3'")
161          << FD->getName() << listSpecialParameters(Context->getTargetAPI())
162          << PVD->getName() << PVD->getType().getAsString();
163      valid = false;
164      continue;
165    }
166
167    // The only non-const pointer should be out.
168    if (!QT->getPointeeType().isConstQualified()) {
169      if (mOut == nullptr) {
170        mOut = PVD;
171      } else {
172        Context->ReportError(PVD->getLocation(),
173                             "Compute kernel %0() can only have one non-const "
174                             "pointer parameter. Parameters '%1' and '%2' are "
175                             "both non-const.")
176            << FD->getName() << mOut->getName() << PVD->getName();
177        valid = false;
178      }
179    } else {
180      if (mIns.empty() && mOut == nullptr) {
181        mIns.push_back(PVD);
182      } else if (mUsrData == nullptr) {
183        mUsrData = PVD;
184      } else {
185        Context->ReportError(
186            PVD->getLocation(),
187            "Unexpected parameter '%0' for compute kernel %1()")
188            << PVD->getName() << FD->getName();
189        valid = false;
190      }
191    }
192  }
193
194  if (mIns.empty() && !mOut) {
195    Context->ReportError(FD->getLocation(),
196                         "Compute kernel %0() must have at least one "
197                         "parameter for in or out")
198        << FD->getName();
199    valid = false;
200  }
201
202  return valid;
203}
204
205bool RSExportForEach::validateAndConstructKernelParams(
206    RSContext *Context, const clang::FunctionDecl *FD) {
207  slangAssert(Context && FD);
208  bool valid = true;
209  clang::ASTContext &C = Context->getASTContext();
210
211  if (Context->getTargetAPI() < SLANG_JB_MR1_TARGET_API) {
212    Context->ReportError(FD->getLocation(),
213                         "Compute kernel %0() targeting SDK levels "
214                         "%1-%2 may not use pass-by-value with "
215                         "__attribute__((kernel))")
216        << FD->getName() << SLANG_MINIMUM_TARGET_API
217        << (SLANG_JB_MR1_TARGET_API - 1);
218    return false;
219  }
220
221  // Denote that we are indeed a pass-by-value kernel.
222  mIsKernelStyle = true;
223  mHasReturnType = (mResultType != C.VoidTy);
224
225  if (mResultType->isPointerType()) {
226    Context->ReportError(
227        FD->getTypeSpecStartLoc(),
228        "Compute kernel %0() cannot return a pointer type: '%1'")
229        << FD->getName() << mResultType.getAsString();
230    valid = false;
231  }
232
233  // Validate remaining parameter types
234
235  size_t IndexOfFirstSpecialParameter = numParams;
236  valid |= validateSpecialParameters(Context, FD, &IndexOfFirstSpecialParameter);
237
238  // Validate the non-special parameters, which should all be found before the
239  // first special.
240  for (size_t i = 0; i < IndexOfFirstSpecialParameter; i++) {
241    const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
242
243    /*
244     * FIXME: Change this to a test against an actual API version when the
245     *        multi-input feature is officially supported.
246     */
247    if (Context->getTargetAPI() == SLANG_DEVELOPMENT_TARGET_API || i == 0) {
248      if (i >= RS_KERNEL_INPUT_LIMIT) {
249        Context->ReportError(PVD->getLocation(),
250                             "Invalid parameter '%0' for compute kernel %1(). "
251                             "Kernels targeting SDK levels %2-%3 may not use "
252                             "more than %4 input parameters.") << PVD->getName() <<
253                             FD->getName() << SLANG_MINIMUM_TARGET_API <<
254                             SLANG_MAXIMUM_TARGET_API << int(RS_KERNEL_INPUT_LIMIT);
255
256      } else {
257        mIns.push_back(PVD);
258      }
259    } else {
260      Context->ReportError(PVD->getLocation(),
261                           "Invalid parameter '%0' for compute kernel %1(). "
262                           "Kernels targeting SDK levels %2-%3 may not use "
263                           "multiple input parameters.") << PVD->getName() <<
264                           FD->getName() << SLANG_MINIMUM_TARGET_API <<
265                           SLANG_MAXIMUM_TARGET_API;
266      valid = false;
267    }
268    clang::QualType QT = PVD->getType().getCanonicalType();
269    if (QT->isPointerType()) {
270      Context->ReportError(PVD->getLocation(),
271                           "Compute kernel %0() cannot have "
272                           "parameter '%1' of pointer type: '%2'")
273          << FD->getName() << PVD->getName() << PVD->getType().getAsString();
274      valid = false;
275    }
276  }
277
278  // Check that we have at least one allocation to use for dimensions.
279  if (valid && mIns.empty() && !mHasReturnType && Context->getTargetAPI() < SLANG_23_TARGET_API) {
280    Context->ReportError(FD->getLocation(),
281                         "Compute kernel %0() targeting SDK levels "
282                         "%1-%2 must have at least one "
283                         "input parameter or a non-void return "
284                         "type")
285        << FD->getName() << SLANG_MINIMUM_TARGET_API
286        << (SLANG_23_TARGET_API - 1);
287    valid = false;
288  }
289
290  return valid;
291}
292
293// Search for the optional special parameters.  Returns true if valid.   Also
294// sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or
295// FD->getNumParams() if none are found.
296bool RSExportForEach::validateSpecialParameters(
297    RSContext *Context, const clang::FunctionDecl *FD,
298    size_t *IndexOfFirstSpecialParameter) {
299  slangAssert(IndexOfFirstSpecialParameter != nullptr);
300  slangAssert(mSpecialParameterSignatureMetadata == 0);
301  clang::ASTContext &C = Context->getASTContext();
302
303  // Find all special parameters if present.
304  int LastSpecialParameterIdx = -1;     // index into specialParameterTable
305  int FirstIntSpecialParameterIdx = -1; // index into specialParameterTable
306  clang::QualType FirstIntSpecialParameterType;
307  size_t NumParams = FD->getNumParams();
308  *IndexOfFirstSpecialParameter = NumParams;
309  bool valid = true;
310  for (size_t i = 0; i < NumParams; i++) {
311    const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
312    llvm::StringRef ParamName = PVD->getName();
313    int SpecialParameterIdx = lookupSpecialParameter(ParamName);
314    if (SpecialParameterIdx >= 0) {
315      const SpecialParameter &SP = specialParameterTable[SpecialParameterIdx];
316      // We won't be invoked if two parameters of the same name are present.
317      slangAssert(!(mSpecialParameterSignatureMetadata & SP.bitval));
318
319      if (Context->getTargetAPI() < SP.minAPI) {
320        Context->ReportError(PVD->getLocation(),
321                             "Compute kernel %0() targeting SDK levels "
322                             "%1-%2 may not use parameter '%3'.")
323            << FD->getName()
324            << SLANG_MINIMUM_TARGET_API
325            << (SP.minAPI - 1)
326            << SP.name;
327        valid = false;
328      }
329
330      mSpecialParameterSignatureMetadata |= SP.bitval;
331      if (SpecialParameterIdx < LastSpecialParameterIdx) {
332        Context->ReportError(PVD->getLocation(),
333                             "In compute kernel %0(), parameter '%1' must "
334                             "be defined before parameter '%2'.")
335            << FD->getName()
336            << SP.name
337            << specialParameterTable[LastSpecialParameterIdx].name;
338        valid = false;
339      }
340      LastSpecialParameterIdx = SpecialParameterIdx;
341
342      // Ensure that all SPK_INT special parameters have the same type.
343      if (SP.kind == SPK_INT) {
344        clang::QualType SpecialParameterType = PVD->getType();
345        if (FirstIntSpecialParameterIdx >= 0) {
346          if (SpecialParameterType != FirstIntSpecialParameterType) {
347            Context->ReportError(PVD->getLocation(),
348                                 "Parameters '%0' and '%1' must be of the same type. "
349                                 "'%0' is of type '%2' while '%1' is of type '%3'.")
350                << specialParameterTable[FirstIntSpecialParameterIdx].name
351                << SP.name
352                << FirstIntSpecialParameterType.getAsString()
353                << SpecialParameterType.getAsString();
354            valid = false;
355          }
356        } else {
357          FirstIntSpecialParameterIdx = SpecialParameterIdx;
358          FirstIntSpecialParameterType = SpecialParameterType;
359        }
360      }
361    } else {
362      // It's not a special parameter.
363      if (*IndexOfFirstSpecialParameter < NumParams) {
364        Context->ReportError(PVD->getLocation(),
365                             "In compute kernel %0(), parameter '%1' cannot "
366                             "appear after any of the (%2) parameters.")
367            << FD->getName() << ParamName << listSpecialParameters(Context->getTargetAPI());
368        valid = false;
369      }
370      continue;
371    }
372    // Validate the data type of the special parameter.
373    switch (specialParameterTable[SpecialParameterIdx].kind) {
374      case SPK_INT: {
375        clang::QualType QT = PVD->getType().getCanonicalType();
376        clang::QualType UT = QT.getUnqualifiedType();
377        if (UT != C.UnsignedIntTy && UT != C.IntTy) {
378          Context->ReportError(PVD->getLocation(),
379                               "Parameter '%0' must be of type 'int' or "
380                               "'unsigned int'. It is of type '%1'.")
381              << ParamName << PVD->getType().getAsString();
382          valid = false;
383        }
384        break;
385      }
386      case SPK_CTXT: {
387        static const char ExpectedTypeNameMatch[] = "const struct rs_kernel_context_t *";
388        static const char ExpectedTypeNamePrint[] = "rs_kernel_context";
389        clang::QualType QT = PVD->getType().getCanonicalType();
390        clang::QualType UT = QT.getUnqualifiedType();
391        if (UT.getAsString() != ExpectedTypeNameMatch) {
392          Context->ReportError(PVD->getLocation(),
393                               "Parameter '%0' must be of type '%1'. "
394                               "It is of type '%2'.")
395              << ParamName << ExpectedTypeNamePrint << PVD->getType().getAsString();
396          valid = false;
397        }
398        break;
399      }
400      default:
401        slangAssert(!"Unexpected special parameter type");
402    }
403    // If this is the first time we find a special parameter, save it.
404    if (*IndexOfFirstSpecialParameter >= NumParams) {
405      *IndexOfFirstSpecialParameter = i;
406    }
407  }
408  return valid;
409}
410
411bool RSExportForEach::setSignatureMetadata(RSContext *Context,
412                                           const clang::FunctionDecl *FD) {
413  mSignatureMetadata = 0;
414  bool valid = true;
415
416  if (mIsKernelStyle) {
417    slangAssert(mOut == nullptr);
418    slangAssert(mUsrData == nullptr);
419  } else {
420    slangAssert(!mHasReturnType);
421  }
422
423  // Set up the bitwise metadata encoding for runtime argument passing.
424  const bool HasOut = mOut || mHasReturnType;
425  mSignatureMetadata |= (hasIns() ?       bcinfo::MD_SIG_In     : 0);
426  mSignatureMetadata |= (HasOut ?         bcinfo::MD_SIG_Out    : 0);
427  mSignatureMetadata |= (mUsrData ?       bcinfo::MD_SIG_Usr    : 0);
428  mSignatureMetadata |= (mIsKernelStyle ? bcinfo::MD_SIG_Kernel : 0);  // pass-by-value
429  mSignatureMetadata |= mSpecialParameterSignatureMetadata;
430
431  if (Context->getTargetAPI() < SLANG_ICS_TARGET_API) {
432    // APIs before ICS cannot skip between parameters. It is ok, however, for
433    // them to omit further parameters (i.e. skipping X is ok if you skip Y).
434    if (mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr |
435                               bcinfo::MD_SIG_X | bcinfo::MD_SIG_Y) &&
436        mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr |
437                               bcinfo::MD_SIG_X) &&
438        mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out | bcinfo::MD_SIG_Usr) &&
439        mSignatureMetadata != (bcinfo::MD_SIG_In | bcinfo::MD_SIG_Out) &&
440        mSignatureMetadata != (bcinfo::MD_SIG_In)) {
441      Context->ReportError(FD->getLocation(),
442                           "Compute kernel %0() targeting SDK levels "
443                           "%1-%2 may not skip parameters")
444          << FD->getName() << SLANG_MINIMUM_TARGET_API
445          << (SLANG_ICS_TARGET_API - 1);
446      valid = false;
447    }
448  }
449  return valid;
450}
451
452RSExportForEach *RSExportForEach::Create(RSContext *Context,
453                                         const clang::FunctionDecl *FD) {
454  slangAssert(Context && FD);
455  llvm::StringRef Name = FD->getName();
456  RSExportForEach *FE;
457
458  slangAssert(!Name.empty() && "Function must have a name");
459
460  FE = new RSExportForEach(Context, Name);
461
462  if (!FE->validateAndConstructParams(Context, FD)) {
463    return nullptr;
464  }
465
466  clang::ASTContext &Ctx = Context->getASTContext();
467
468  std::string Id = CreateDummyName("helper_foreach_param", FE->getName());
469
470  // Extract the usrData parameter (if we have one)
471  if (FE->mUsrData) {
472    const clang::ParmVarDecl *PVD = FE->mUsrData;
473    clang::QualType QT = PVD->getType().getCanonicalType();
474    slangAssert(QT->isPointerType() &&
475                QT->getPointeeType().isConstQualified());
476
477    const clang::ASTContext &C = Context->getASTContext();
478    if (QT->getPointeeType().getCanonicalType().getUnqualifiedType() ==
479        C.VoidTy) {
480      // In the case of using const void*, we can't reflect an appopriate
481      // Java type, so we fall back to just reflecting the ain/aout parameters
482      FE->mUsrData = nullptr;
483    } else {
484      clang::RecordDecl *RD =
485          clang::RecordDecl::Create(Ctx, clang::TTK_Struct,
486                                    Ctx.getTranslationUnitDecl(),
487                                    clang::SourceLocation(),
488                                    clang::SourceLocation(),
489                                    &Ctx.Idents.get(Id));
490
491      clang::FieldDecl *FD =
492          clang::FieldDecl::Create(Ctx,
493                                   RD,
494                                   clang::SourceLocation(),
495                                   clang::SourceLocation(),
496                                   PVD->getIdentifier(),
497                                   QT->getPointeeType(),
498                                   nullptr,
499                                   /* BitWidth = */ nullptr,
500                                   /* Mutable = */ false,
501                                   /* HasInit = */ clang::ICIS_NoInit);
502      RD->addDecl(FD);
503      RD->completeDefinition();
504
505      // Create an export type iff we have a valid usrData type
506      clang::QualType T = Ctx.getTagDeclType(RD);
507      slangAssert(!T.isNull());
508
509      RSExportType *ET = RSExportType::Create(Context, T.getTypePtr());
510
511      slangAssert(ET && "Failed to export a kernel");
512
513      slangAssert((ET->getClass() == RSExportType::ExportClassRecord) &&
514                  "Parameter packet must be a record");
515
516      FE->mParamPacketType = static_cast<RSExportRecordType *>(ET);
517    }
518  }
519
520  if (FE->hasIns()) {
521
522    for (InIter BI = FE->mIns.begin(), EI = FE->mIns.end(); BI != EI; BI++) {
523      const clang::Type *T = (*BI)->getType().getCanonicalType().getTypePtr();
524      RSExportType *InExportType = RSExportType::Create(Context, T);
525
526      if (FE->mIsKernelStyle) {
527        slangAssert(InExportType != nullptr);
528      }
529
530      FE->mInTypes.push_back(InExportType);
531    }
532  }
533
534  if (FE->mIsKernelStyle && FE->mHasReturnType) {
535    const clang::Type *T = FE->mResultType.getTypePtr();
536    FE->mOutType = RSExportType::Create(Context, T);
537    slangAssert(FE->mOutType);
538  } else if (FE->mOut) {
539    const clang::Type *T = FE->mOut->getType().getCanonicalType().getTypePtr();
540    FE->mOutType = RSExportType::Create(Context, T);
541  }
542
543  return FE;
544}
545
546RSExportForEach *RSExportForEach::CreateDummyRoot(RSContext *Context) {
547  slangAssert(Context);
548  llvm::StringRef Name = "root";
549  RSExportForEach *FE = new RSExportForEach(Context, Name);
550  FE->mDummyRoot = true;
551  return FE;
552}
553
554bool RSExportForEach::isGraphicsRootRSFunc(unsigned int targetAPI,
555                                           const clang::FunctionDecl *FD) {
556  if (FD->hasAttr<clang::KernelAttr>()) {
557    return false;
558  }
559
560  if (!isRootRSFunc(FD)) {
561    return false;
562  }
563
564  if (FD->getNumParams() == 0) {
565    // Graphics root function
566    return true;
567  }
568
569  // Check for legacy graphics root function (with single parameter).
570  if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
571    const clang::QualType &IntType = FD->getASTContext().IntTy;
572    if (FD->getReturnType().getCanonicalType() == IntType) {
573      return true;
574    }
575  }
576
577  return false;
578}
579
580bool RSExportForEach::isRSForEachFunc(unsigned int targetAPI,
581                                      slang::RSContext* Context,
582                                      const clang::FunctionDecl *FD) {
583  slangAssert(Context && FD);
584  bool hasKernelAttr = FD->hasAttr<clang::KernelAttr>();
585
586  if (FD->getStorageClass() == clang::SC_Static) {
587    if (hasKernelAttr) {
588      Context->ReportError(FD->getLocation(),
589                           "Invalid use of attribute kernel with "
590                           "static function declaration: %0")
591          << FD->getName();
592    }
593    return false;
594  }
595
596  // Anything tagged as a kernel is definitely used with ForEach.
597  if (hasKernelAttr) {
598    return true;
599  }
600
601  if (isGraphicsRootRSFunc(targetAPI, FD)) {
602    return false;
603  }
604
605  // Check if first parameter is a pointer (which is required for ForEach).
606  unsigned int numParams = FD->getNumParams();
607
608  if (numParams > 0) {
609    const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
610    clang::QualType QT = PVD->getType().getCanonicalType();
611
612    if (QT->isPointerType()) {
613      return true;
614    }
615
616    // Any non-graphics root() is automatically a ForEach candidate.
617    // At this point, however, we know that it is not going to be a valid
618    // compute root() function (due to not having a pointer parameter). We
619    // still want to return true here, so that we can issue appropriate
620    // diagnostics.
621    if (isRootRSFunc(FD)) {
622      return true;
623    }
624  }
625
626  return false;
627}
628
629bool
630RSExportForEach::validateSpecialFuncDecl(unsigned int targetAPI,
631                                         slang::RSContext *Context,
632                                         clang::FunctionDecl const *FD) {
633  slangAssert(Context && FD);
634  bool valid = true;
635  const clang::ASTContext &C = FD->getASTContext();
636  const clang::QualType &IntType = FD->getASTContext().IntTy;
637
638  if (isGraphicsRootRSFunc(targetAPI, FD)) {
639    if ((targetAPI < SLANG_ICS_TARGET_API) && (FD->getNumParams() == 1)) {
640      // Legacy graphics root function
641      const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
642      clang::QualType QT = PVD->getType().getCanonicalType();
643      if (QT != IntType) {
644        Context->ReportError(PVD->getLocation(),
645                             "invalid parameter type for legacy "
646                             "graphics root() function: %0")
647            << PVD->getType();
648        valid = false;
649      }
650    }
651
652    // Graphics root function, so verify that it returns an int
653    if (FD->getReturnType().getCanonicalType() != IntType) {
654      Context->ReportError(FD->getLocation(),
655                           "root() is required to return "
656                           "an int for graphics usage");
657      valid = false;
658    }
659  } else if (isInitRSFunc(FD) || isDtorRSFunc(FD)) {
660    if (FD->getNumParams() != 0) {
661      Context->ReportError(FD->getLocation(),
662                           "%0(void) is required to have no "
663                           "parameters")
664          << FD->getName();
665      valid = false;
666    }
667
668    if (FD->getReturnType().getCanonicalType() != C.VoidTy) {
669      Context->ReportError(FD->getLocation(),
670                           "%0(void) is required to have a void "
671                           "return type")
672          << FD->getName();
673      valid = false;
674    }
675  } else {
676    slangAssert(false && "must be called on root, init or .rs.dtor function!");
677  }
678
679  return valid;
680}
681
682}  // namespace slang
683