235 lines
8.4 KiB
C++
235 lines
8.4 KiB
C++
/*
|
|
* 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<std::string ()> &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
|