/* * 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(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(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(Ty); if (VectorTy) { ElementTy = clang::dyn_cast_or_null( VectorTy->getElementType()->getUnqualifiedDesugaredType()); } else { ElementTy = clang::dyn_cast_or_null( 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() && 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(*I)) { ValidateVarDecl(VD); } else if (clang::FunctionDecl *FD = llvm::dyn_cast(*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(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(*DI)) { ValidateVarDecl(VD); } else if (clang::FunctionDecl *FD = llvm::dyn_cast(*DI)) { ValidateFunctionDecl(FD); } else if (clang::Stmt *Body = (*DI)->getBody()) { Visit(Body); } } } return mValid; } } // namespace slang