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
27 namespace {
28
29 enum SpecialParameterKind {
30 SPK_LOCATION, // 'int' or 'unsigned int'
31 SPK_CONTEXT, // rs_kernel_context
32 };
33
34 struct 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.
42 const 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.
lookupSpecialKernelParameter(const llvm::StringRef name)53 int 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
65 namespace slang {
66
67 // Is the specified name the name of an entry in the specialParameterTable?
isSpecialKernelParameter(const llvm::StringRef Name)68 bool 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.
listSpecialKernelParameters(unsigned int api)74 std::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.
processSpecialKernelParameters(slang::RSContext * Context,std::function<std::string ()> DiagnosticDescription,const clang::FunctionDecl * FD,size_t * IndexOfFirstSpecialParameter,unsigned int * SignatureMetadata)96 bool 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