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,const std::function<std::string ()> & DiagnosticDescription,const clang::FunctionDecl * FD,size_t * IndexOfFirstSpecialParameter,unsigned int * SignatureMetadata)96 bool processSpecialKernelParameters(
97   slang::RSContext *Context,
98   const 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