/* * Copyright 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "slang_rs_special_kernel_param.h" #include "clang/AST/ASTContext.h" #include "bcinfo/MetadataExtractor.h" #include "slang_assert.h" #include "slang_rs_context.h" #include "slang_version.h" namespace { enum SpecialParameterKind { SPK_LOCATION, // 'int' or 'unsigned int' SPK_CONTEXT, // rs_kernel_context }; struct SpecialParameter { const char *name; bcinfo::MetadataSignatureBitval bitval; SpecialParameterKind kind; SlangTargetAPI minAPI; }; // Table entries are in the order parameters must occur in a kernel parameter list. const SpecialParameter specialParameterTable[] = { { "context", bcinfo::MD_SIG_Ctxt, SPK_CONTEXT, SLANG_M_TARGET_API }, { "x", bcinfo::MD_SIG_X, SPK_LOCATION, SLANG_MINIMUM_TARGET_API }, { "y", bcinfo::MD_SIG_Y, SPK_LOCATION, SLANG_MINIMUM_TARGET_API }, { "z", bcinfo::MD_SIG_Z, SPK_LOCATION, SLANG_M_TARGET_API }, { nullptr, bcinfo::MD_SIG_None, SPK_LOCATION, SLANG_MINIMUM_TARGET_API }, // marks end of table }; // If the specified name matches the name of an entry in // specialParameterTable, return the corresponding table index. // Return -1 if not found. int lookupSpecialKernelParameter(const llvm::StringRef name) { for (int i = 0; specialParameterTable[i].name != nullptr; ++i) { if (name.equals(specialParameterTable[i].name)) { return i; } } return -1; } } // end anonymous namespace namespace slang { // Is the specified name the name of an entry in the specialParameterTable? bool isSpecialKernelParameter(const llvm::StringRef Name) { return lookupSpecialKernelParameter(Name) >= 0; } // Return a comma-separated list of names in specialParameterTable // that are available at the specified API level. std::string listSpecialKernelParameters(unsigned int api) { std::string ret; bool first = true; for (int i = 0; specialParameterTable[i].name != nullptr; ++i) { if (specialParameterTable[i].minAPI > api) continue; if (first) first = false; else ret += ", "; ret += "'"; ret += specialParameterTable[i].name; ret += "'"; } return ret; } // Process the optional special parameters: // - Sets *IndexOfFirstSpecialParameter to the index of the first special parameter, or // FD->getNumParams() if none are found. // - Add bits to *SignatureMetadata for the found special parameters. // Returns true if no errors. bool processSpecialKernelParameters( slang::RSContext *Context, const std::function &DiagnosticDescription, const clang::FunctionDecl *FD, size_t *IndexOfFirstSpecialParameter, unsigned int *SignatureMetadata) { slangAssert(IndexOfFirstSpecialParameter != nullptr); slangAssert(SignatureMetadata != nullptr); slangAssert(*SignatureMetadata == 0); clang::ASTContext &C = Context->getASTContext(); // Find all special parameters if present. int LastSpecialParameterIdx = -1; // index into specialParameterTable int FirstLocationSpecialParameterIdx = -1; // index into specialParameterTable clang::QualType FirstLocationSpecialParameterType; size_t NumParams = FD->getNumParams(); *IndexOfFirstSpecialParameter = NumParams; bool valid = true; for (size_t i = 0; i < NumParams; i++) { const clang::ParmVarDecl *PVD = FD->getParamDecl(i); const llvm::StringRef ParamName = PVD->getName(); const clang::QualType Type = PVD->getType(); const clang::QualType QT = Type.getCanonicalType(); const clang::QualType UT = QT.getUnqualifiedType(); int SpecialParameterIdx = lookupSpecialKernelParameter(ParamName); static const char KernelContextUnqualifiedTypeName[] = "const struct rs_kernel_context_t *"; static const char KernelContextTypeName[] = "rs_kernel_context"; // If the type is rs_context, it should have been named "context" and classified // as a special parameter. if (SpecialParameterIdx < 0 && UT.getAsString() == KernelContextUnqualifiedTypeName) { Context->ReportError( PVD->getLocation(), "The special parameter of type '%0' must be called " "'context' instead of '%1'.") << KernelContextTypeName << ParamName; SpecialParameterIdx = lookupSpecialKernelParameter("context"); } // If it's not a special parameter, check that it appears before any special // parameter. if (SpecialParameterIdx < 0) { if (*IndexOfFirstSpecialParameter < NumParams) { Context->ReportError(PVD->getLocation(), "In %0, parameter '%1' cannot " "appear after any of the special parameters (%2).") << DiagnosticDescription() << ParamName << listSpecialKernelParameters(Context->getTargetAPI()); valid = false; } continue; } const SpecialParameter &SP = specialParameterTable[SpecialParameterIdx]; // Verify that this special parameter is OK for the current API level. if (Context->getTargetAPI() < SP.minAPI) { Context->ReportError(PVD->getLocation(), "%0 targeting SDK levels " "%1-%2 may not use special parameter '%3'.") << DiagnosticDescription() << SLANG_MINIMUM_TARGET_API << (SP.minAPI - 1) << SP.name; valid = false; } // Check that the order of the special parameters is correct. if (SpecialParameterIdx < LastSpecialParameterIdx) { Context->ReportError( PVD->getLocation(), "In %0, special parameter '%1' must " "be defined before special parameter '%2'.") << DiagnosticDescription() << SP.name << specialParameterTable[LastSpecialParameterIdx].name; valid = false; } // Validate the data type of the special parameter. switch (SP.kind) { case SPK_LOCATION: { // Location special parameters can only be int or uint. if (UT != C.UnsignedIntTy && UT != C.IntTy) { Context->ReportError(PVD->getLocation(), "Special parameter '%0' must be of type 'int' or " "'unsigned int'. It is of type '%1'.") << ParamName << Type.getAsString(); valid = false; } // Ensure that all location special parameters have the same type. if (FirstLocationSpecialParameterIdx >= 0) { if (Type != FirstLocationSpecialParameterType) { Context->ReportError( PVD->getLocation(), "Special parameters '%0' and '%1' must be of the same type. " "'%0' is of type '%2' while '%1' is of type '%3'.") << specialParameterTable[FirstLocationSpecialParameterIdx].name << SP.name << FirstLocationSpecialParameterType.getAsString() << Type.getAsString(); valid = false; } } else { FirstLocationSpecialParameterIdx = SpecialParameterIdx; FirstLocationSpecialParameterType = Type; } } break; case SPK_CONTEXT: { // Check that variables named "context" are of type rs_context. if (UT.getAsString() != KernelContextUnqualifiedTypeName) { Context->ReportError(PVD->getLocation(), "Special parameter '%0' must be of type '%1'. " "It is of type '%2'.") << ParamName << KernelContextTypeName << Type.getAsString(); valid = false; } } break; default: slangAssert(!"Unexpected special parameter type"); } // We should not be invoked if two parameters of the same name are present. slangAssert(!(*SignatureMetadata & SP.bitval)); *SignatureMetadata |= SP.bitval; LastSpecialParameterIdx = SpecialParameterIdx; // If this is the first time we find a special parameter, save it. if (*IndexOfFirstSpecialParameter >= NumParams) { *IndexOfFirstSpecialParameter = i; } } return valid; } } // namespace slang