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_special_kernel_param.h"
18
19#include "clang/AST/ASTContext.h"
20
21#include "bcinfo/MetadataExtractor.h"
22
23#include "slang_assert.h"
24#include "slang_rs_context.h"
25#include "slang_version.h"
26
27namespace {
28
29enum SpecialParameterKind {
30  SPK_LOCATION, // 'int' or 'unsigned int'
31  SPK_CONTEXT,  // rs_kernel_context
32};
33
34struct SpecialParameter {
35  const char *name;
36  bcinfo::MetadataSignatureBitval bitval;
37  SpecialParameterKind kind;
38  SlangTargetAPI minAPI;
39};
40
41// Table entries are in the order parameters must occur in a kernel parameter list.
42const SpecialParameter specialParameterTable[] = {
43  { "context", bcinfo::MD_SIG_Ctxt, SPK_CONTEXT, SLANG_M_TARGET_API },
44  { "x", bcinfo::MD_SIG_X, SPK_LOCATION, SLANG_MINIMUM_TARGET_API },
45  { "y", bcinfo::MD_SIG_Y, SPK_LOCATION, SLANG_MINIMUM_TARGET_API },
46  { "z", bcinfo::MD_SIG_Z, SPK_LOCATION, SLANG_M_TARGET_API },
47  { nullptr, bcinfo::MD_SIG_None, SPK_LOCATION, SLANG_MINIMUM_TARGET_API }, // marks end of table
48};
49
50// If the specified name matches the name of an entry in
51// specialParameterTable, return the corresponding table index.
52// Return -1 if not found.
53int lookupSpecialKernelParameter(const llvm::StringRef name) {
54  for (int i = 0; specialParameterTable[i].name != nullptr; ++i) {
55    if (name.equals(specialParameterTable[i].name)) {
56      return i;
57    }
58  }
59
60  return -1;
61}
62
63} // end anonymous namespace
64
65namespace slang {
66
67// Is the specified name the name of an entry in the specialParameterTable?
68bool isSpecialKernelParameter(const llvm::StringRef Name) {
69  return lookupSpecialKernelParameter(Name) >= 0;
70}
71
72// Return a comma-separated list of names in specialParameterTable
73// that are available at the specified API level.
74std::string listSpecialKernelParameters(unsigned int api) {
75  std::string ret;
76  bool first = true;
77  for (int i = 0; specialParameterTable[i].name != nullptr; ++i) {
78    if (specialParameterTable[i].minAPI > api)
79      continue;
80    if (first)
81      first = false;
82    else
83      ret += ", ";
84    ret += "'";
85    ret += specialParameterTable[i].name;
86    ret += "'";
87  }
88  return ret;
89}
90
91// Process the optional special parameters:
92// - Sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or
93//     FD->getNumParams() if none are found.
94// - Add bits to *SignatureMetadata for the found special parameters.
95// Returns true if no errors.
96bool processSpecialKernelParameters(
97  slang::RSContext *Context,
98  std::function<std::string ()> DiagnosticDescription,
99  const clang::FunctionDecl *FD,
100  size_t *IndexOfFirstSpecialParameter,
101  unsigned int *SignatureMetadata) {
102  slangAssert(IndexOfFirstSpecialParameter != nullptr);
103  slangAssert(SignatureMetadata != nullptr);
104  slangAssert(*SignatureMetadata == 0);
105  clang::ASTContext &C = Context->getASTContext();
106
107  // Find all special parameters if present.
108  int LastSpecialParameterIdx = -1; // index into specialParameterTable
109  int FirstLocationSpecialParameterIdx = -1; // index into specialParameterTable
110  clang::QualType FirstLocationSpecialParameterType;
111  size_t NumParams = FD->getNumParams();
112  *IndexOfFirstSpecialParameter = NumParams;
113  bool valid = true;
114  for (size_t i = 0; i < NumParams; i++) {
115    const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
116    const llvm::StringRef ParamName = PVD->getName();
117    const clang::QualType Type = PVD->getType();
118    const clang::QualType QT = Type.getCanonicalType();
119    const clang::QualType UT = QT.getUnqualifiedType();
120    int SpecialParameterIdx = lookupSpecialKernelParameter(ParamName);
121
122    static const char KernelContextUnqualifiedTypeName[] =
123        "const struct rs_kernel_context_t *";
124    static const char KernelContextTypeName[] = "rs_kernel_context";
125
126    // If the type is rs_context, it should have been named "context" and classified
127    // as a special parameter.
128    if (SpecialParameterIdx < 0 && UT.getAsString() == KernelContextUnqualifiedTypeName) {
129      Context->ReportError(
130          PVD->getLocation(),
131          "The special parameter of type '%0' must be called "
132          "'context' instead of '%1'.")
133          << KernelContextTypeName << ParamName;
134      SpecialParameterIdx = lookupSpecialKernelParameter("context");
135    }
136
137    // If it's not a special parameter, check that it appears before any special
138    // parameter.
139    if (SpecialParameterIdx < 0) {
140      if (*IndexOfFirstSpecialParameter < NumParams) {
141        Context->ReportError(PVD->getLocation(),
142                             "In %0, parameter '%1' cannot "
143                             "appear after any of the special parameters (%2).")
144            << DiagnosticDescription()
145            << ParamName
146            << listSpecialKernelParameters(Context->getTargetAPI());
147        valid = false;
148      }
149      continue;
150    }
151
152    const SpecialParameter &SP = specialParameterTable[SpecialParameterIdx];
153
154    // Verify that this special parameter is OK for the current API level.
155    if (Context->getTargetAPI() < SP.minAPI) {
156      Context->ReportError(PVD->getLocation(),
157                           "%0 targeting SDK levels "
158                           "%1-%2 may not use special parameter '%3'.")
159          << DiagnosticDescription()
160          << SLANG_MINIMUM_TARGET_API << (SP.minAPI - 1)
161          << SP.name;
162      valid = false;
163    }
164
165    // Check that the order of the special parameters is correct.
166    if (SpecialParameterIdx < LastSpecialParameterIdx) {
167      Context->ReportError(
168          PVD->getLocation(),
169          "In %0, special parameter '%1' must "
170          "be defined before special parameter '%2'.")
171          << DiagnosticDescription()
172          << SP.name
173          << specialParameterTable[LastSpecialParameterIdx].name;
174      valid = false;
175    }
176
177    // Validate the data type of the special parameter.
178    switch (SP.kind) {
179    case SPK_LOCATION: {
180      // Location special parameters can only be int or uint.
181      if (UT != C.UnsignedIntTy && UT != C.IntTy) {
182        Context->ReportError(PVD->getLocation(),
183                             "Special parameter '%0' must be of type 'int' or "
184                             "'unsigned int'. It is of type '%1'.")
185            << ParamName << Type.getAsString();
186        valid = false;
187      }
188
189      // Ensure that all location special parameters have the same type.
190      if (FirstLocationSpecialParameterIdx >= 0) {
191        if (Type != FirstLocationSpecialParameterType) {
192          Context->ReportError(
193              PVD->getLocation(),
194              "Special parameters '%0' and '%1' must be of the same type. "
195              "'%0' is of type '%2' while '%1' is of type '%3'.")
196              << specialParameterTable[FirstLocationSpecialParameterIdx].name
197              << SP.name << FirstLocationSpecialParameterType.getAsString()
198              << Type.getAsString();
199          valid = false;
200        }
201      } else {
202        FirstLocationSpecialParameterIdx = SpecialParameterIdx;
203        FirstLocationSpecialParameterType = Type;
204      }
205    } break;
206    case SPK_CONTEXT: {
207      // Check that variables named "context" are of type rs_context.
208      if (UT.getAsString() != KernelContextUnqualifiedTypeName) {
209        Context->ReportError(PVD->getLocation(),
210                             "Special parameter '%0' must be of type '%1'. "
211                             "It is of type '%2'.")
212            << ParamName << KernelContextTypeName
213            << Type.getAsString();
214        valid = false;
215      }
216    } break;
217    default:
218      slangAssert(!"Unexpected special parameter type");
219    }
220
221    // We should not be invoked if two parameters of the same name are present.
222    slangAssert(!(*SignatureMetadata & SP.bitval));
223    *SignatureMetadata |= SP.bitval;
224
225    LastSpecialParameterIdx = SpecialParameterIdx;
226    // If this is the first time we find a special parameter, save it.
227    if (*IndexOfFirstSpecialParameter >= NumParams) {
228      *IndexOfFirstSpecialParameter = i;
229    }
230  }
231  return valid;
232}
233
234} // namespace slang
235