You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
8.8 KiB
315 lines
8.8 KiB
/*
|
|
* Copyright 2012, 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 "clang/AST/Attr.h"
|
|
|
|
#include "slang_rs_check_ast.h"
|
|
|
|
#include "slang_assert.h"
|
|
#include "slang.h"
|
|
#include "slang_rs_export_foreach.h"
|
|
#include "slang_rs_export_reduce.h"
|
|
#include "slang_rs_export_type.h"
|
|
|
|
namespace slang {
|
|
|
|
void RSCheckAST::VisitStmt(clang::Stmt *S) {
|
|
// This function does the actual iteration through all sub-Stmt's within
|
|
// a given Stmt. Note that this function is skipped by all of the other
|
|
// Visit* functions if we have already found a higher-level match.
|
|
for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
|
|
I != E;
|
|
I++) {
|
|
if (clang::Stmt *Child = *I) {
|
|
Visit(Child);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RSCheckAST::WarnOnSetElementAt(clang::CallExpr *E) {
|
|
clang::FunctionDecl *Decl;
|
|
Decl = clang::dyn_cast_or_null<clang::FunctionDecl>(E->getCalleeDecl());
|
|
|
|
if (!Decl || Decl->getNameAsString() != std::string("rsSetElementAt")) {
|
|
return;
|
|
}
|
|
|
|
clang::Expr *Expr;
|
|
clang::ImplicitCastExpr *ImplCast;
|
|
Expr = E->getArg(1);
|
|
ImplCast = clang::dyn_cast_or_null<clang::ImplicitCastExpr>(Expr);
|
|
|
|
if (!ImplCast) {
|
|
return;
|
|
}
|
|
|
|
const clang::Type *Ty;
|
|
const clang::VectorType *VectorTy;
|
|
const clang::BuiltinType *ElementTy;
|
|
Ty = ImplCast->getSubExpr()->getType()->getPointeeType()
|
|
->getUnqualifiedDesugaredType();
|
|
VectorTy = clang::dyn_cast<clang::VectorType>(Ty);
|
|
|
|
if (VectorTy) {
|
|
ElementTy = clang::dyn_cast_or_null<clang::BuiltinType>(
|
|
VectorTy->getElementType()->getUnqualifiedDesugaredType());
|
|
} else {
|
|
ElementTy = clang::dyn_cast_or_null<clang::BuiltinType>(
|
|
Ty->getUnqualifiedDesugaredType());
|
|
}
|
|
|
|
if (!ElementTy) {
|
|
return;
|
|
}
|
|
|
|
// We only support vectors with 2, 3 or 4 elements.
|
|
if (VectorTy) {
|
|
switch (VectorTy->getNumElements()) {
|
|
default:
|
|
return;
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char *Name;
|
|
|
|
switch (ElementTy->getKind()) {
|
|
case clang::BuiltinType::Float:
|
|
Name = "float";
|
|
break;
|
|
case clang::BuiltinType::Double:
|
|
Name = "double";
|
|
break;
|
|
case clang::BuiltinType::Char_S:
|
|
Name = "char";
|
|
break;
|
|
case clang::BuiltinType::Short:
|
|
Name = "short";
|
|
break;
|
|
case clang::BuiltinType::Int:
|
|
Name = "int";
|
|
break;
|
|
case clang::BuiltinType::Long:
|
|
Name = "long";
|
|
break;
|
|
case clang::BuiltinType::UChar:
|
|
Name = "uchar";
|
|
break;
|
|
case clang::BuiltinType::UShort:
|
|
Name = "ushort";
|
|
break;
|
|
case clang::BuiltinType::UInt:
|
|
Name = "uint";
|
|
break;
|
|
case clang::BuiltinType::ULong:
|
|
Name = "ulong";
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
clang::DiagnosticBuilder DiagBuilder =
|
|
Context->ReportWarning(E->getLocStart(),
|
|
"untyped rsSetElementAt() can reduce performance. "
|
|
"Use rsSetElementAt_%0%1() instead.");
|
|
DiagBuilder << Name;
|
|
|
|
if (VectorTy) {
|
|
DiagBuilder << VectorTy->getNumElements();
|
|
} else {
|
|
DiagBuilder << "";
|
|
}
|
|
}
|
|
|
|
void RSCheckAST::VisitCallExpr(clang::CallExpr *E) {
|
|
WarnOnSetElementAt(E);
|
|
|
|
for (clang::CallExpr::arg_iterator AI = E->arg_begin(), AE = E->arg_end();
|
|
AI != AE; ++AI) {
|
|
Visit(*AI);
|
|
}
|
|
}
|
|
|
|
void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
|
|
if (!FD) {
|
|
return;
|
|
}
|
|
|
|
// Validate that the kernel attribute is not used with static.
|
|
if (FD->hasAttr<clang::RenderScriptKernelAttr>() &&
|
|
FD->getStorageClass() == clang::SC_Static) {
|
|
Context->ReportError(FD->getLocation(),
|
|
"Invalid use of attribute kernel with "
|
|
"static function declaration: %0")
|
|
<< FD->getName();
|
|
mValid = false;
|
|
}
|
|
|
|
clang::QualType resultType = FD->getReturnType().getCanonicalType();
|
|
bool isExtern = (FD->getFormalLinkage() == clang::ExternalLinkage);
|
|
|
|
// We use FD as our NamedDecl in the case of a bad return type.
|
|
if (!RSExportType::ValidateType(Context, C, resultType, FD,
|
|
FD->getLocStart(), mTargetAPI,
|
|
mIsFilterscript, isExtern)) {
|
|
mValid = false;
|
|
}
|
|
|
|
size_t numParams = FD->getNumParams();
|
|
for (size_t i = 0; i < numParams; i++) {
|
|
clang::ParmVarDecl *PVD = FD->getParamDecl(i);
|
|
clang::QualType QT = PVD->getType().getCanonicalType();
|
|
if (!RSExportType::ValidateType(Context, C, QT, PVD, PVD->getLocStart(),
|
|
mTargetAPI, mIsFilterscript, isExtern)) {
|
|
mValid = false;
|
|
}
|
|
}
|
|
|
|
bool saveKernel = mInKernel;
|
|
mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, FD);
|
|
|
|
if (clang::Stmt *Body = FD->getBody()) {
|
|
Visit(Body);
|
|
}
|
|
|
|
mInKernel = saveKernel;
|
|
}
|
|
|
|
|
|
void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
|
|
if (!VD || RSContext::isSyntheticName(VD->getName())) {
|
|
return;
|
|
}
|
|
|
|
clang::QualType QT = VD->getType();
|
|
|
|
if (VD->getFormalLinkage() == clang::ExternalLinkage) {
|
|
llvm::StringRef TypeName;
|
|
const clang::Type *T = QT.getTypePtr();
|
|
if (!RSExportType::NormalizeType(T, TypeName, Context, VD,
|
|
NotLegacyKernelArgument)) {
|
|
mValid = false;
|
|
}
|
|
}
|
|
|
|
// We don't allow static (non-const) variables within kernels.
|
|
if (mInKernel && VD->isStaticLocal()) {
|
|
if (!QT.isConstQualified()) {
|
|
Context->ReportError(
|
|
VD->getLocation(),
|
|
"Non-const static variables are not allowed in kernels: '%0'")
|
|
<< VD->getName();
|
|
mValid = false;
|
|
}
|
|
}
|
|
|
|
if (!RSExportType::ValidateVarDecl(Context, VD, mTargetAPI, mIsFilterscript)) {
|
|
mValid = false;
|
|
} else if (clang::Expr *Init = VD->getInit()) {
|
|
// Only check the initializer if the decl is already ok.
|
|
Visit(Init);
|
|
}
|
|
}
|
|
|
|
|
|
void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
|
|
if (!Slang::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
|
|
for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
|
|
E = DS->decl_end();
|
|
I != E;
|
|
++I) {
|
|
if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
|
|
ValidateVarDecl(VD);
|
|
} else if (clang::FunctionDecl *FD =
|
|
llvm::dyn_cast<clang::FunctionDecl>(*I)) {
|
|
ValidateFunctionDecl(FD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RSCheckAST::VisitCastExpr(clang::CastExpr *CE) {
|
|
if (CE->getCastKind() == clang::CK_BitCast) {
|
|
clang::QualType QT = CE->getType();
|
|
const clang::Type *T = QT.getTypePtr();
|
|
if (T->isVectorType()) {
|
|
if (llvm::isa<clang::ImplicitCastExpr>(CE)) {
|
|
Context->ReportError(CE->getExprLoc(), "invalid implicit vector cast");
|
|
} else {
|
|
Context->ReportError(CE->getExprLoc(), "invalid vector cast");
|
|
}
|
|
mValid = false;
|
|
}
|
|
}
|
|
Visit(CE->getSubExpr());
|
|
}
|
|
|
|
|
|
void RSCheckAST::VisitExpr(clang::Expr *E) {
|
|
// This is where FS checks for code using pointer and/or 64-bit expressions
|
|
// (i.e. things like casts).
|
|
|
|
// First we skip implicit casts (things like function calls and explicit
|
|
// array accesses rely heavily on them and they are valid.
|
|
E = E->IgnoreImpCasts();
|
|
|
|
// Expressions at this point in the checker are not externally visible.
|
|
static const bool kIsExtern = false;
|
|
|
|
if (mIsFilterscript &&
|
|
!Slang::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
|
|
!RSExportType::ValidateType(Context, C, E->getType(), nullptr, E->getExprLoc(),
|
|
mTargetAPI, mIsFilterscript, kIsExtern)) {
|
|
mValid = false;
|
|
} else {
|
|
// Only visit sub-expressions if we haven't already seen a violation.
|
|
VisitStmt(E);
|
|
}
|
|
}
|
|
|
|
|
|
bool RSCheckAST::Validate() {
|
|
clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
|
|
for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
|
|
DE = TUDecl->decls_end();
|
|
DI != DE;
|
|
DI++) {
|
|
|
|
// Following tests are not applicable to implicitly defined types
|
|
if (DI->isImplicit())
|
|
continue;
|
|
|
|
if (!Slang::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
|
|
if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
|
|
ValidateVarDecl(VD);
|
|
} else if (clang::FunctionDecl *FD =
|
|
llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
|
|
ValidateFunctionDecl(FD);
|
|
} else if (clang::Stmt *Body = (*DI)->getBody()) {
|
|
Visit(Body);
|
|
}
|
|
}
|
|
}
|
|
|
|
return mValid;
|
|
}
|
|
|
|
} // namespace slang
|