slang_rs_check_ast.cpp revision 48d893dc7794b3cfb74f35955ca763ee4170f9ad
1/*
2 * Copyright 2012, 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_check_ast.h"
18
19#include "slang_assert.h"
20#include "slang_rs.h"
21#include "slang_rs_export_foreach.h"
22#include "slang_rs_export_type.h"
23
24namespace slang {
25
26void RSCheckAST::VisitStmt(clang::Stmt *S) {
27  // This function does the actual iteration through all sub-Stmt's within
28  // a given Stmt. Note that this function is skipped by all of the other
29  // Visit* functions if we have already found a higher-level match.
30  for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
31       I != E;
32       I++) {
33    if (clang::Stmt *Child = *I) {
34      Visit(Child);
35    }
36  }
37}
38
39void RSCheckAST::WarnOnSetElementAt(clang::CallExpr *E) {
40  clang::FunctionDecl *Decl;
41  Decl = clang::dyn_cast_or_null<clang::FunctionDecl>(E->getCalleeDecl());
42
43  if (!Decl || Decl->getNameAsString() != std::string("rsSetElementAt")) {
44    return;
45  }
46
47  clang::Expr *Expr;
48  clang::ImplicitCastExpr *ImplCast;
49  Expr = E->getArg(1);
50  ImplCast = clang::dyn_cast_or_null<clang::ImplicitCastExpr>(Expr);
51
52  if (!ImplCast) {
53    return;
54  }
55
56  const clang::Type *Ty;
57  const clang::VectorType *VectorTy;
58  const clang::BuiltinType *ElementTy;
59  Ty = ImplCast->getSubExpr()->getType()->getPointeeType()
60    ->getUnqualifiedDesugaredType();
61  VectorTy = clang::dyn_cast_or_null<clang::VectorType>(Ty);
62
63  if (VectorTy) {
64    ElementTy = clang::dyn_cast_or_null<clang::BuiltinType>(
65      VectorTy->getElementType()->getUnqualifiedDesugaredType());
66  } else {
67    ElementTy = clang::dyn_cast_or_null<clang::BuiltinType>(
68      Ty->getUnqualifiedDesugaredType());
69  }
70
71  if (!ElementTy) {
72    return;
73  }
74
75  // We only support vectors with 2, 3 or 4 elements.
76  if (VectorTy) {
77    switch (VectorTy->getNumElements()) {
78    default:
79      return;
80    case 2:
81    case 3:
82    case 4:
83      break;
84    }
85  }
86
87  const char *Name;
88
89  switch (ElementTy->getKind()) {
90    case clang::BuiltinType::Float:
91      Name = "float";
92      break;
93    case clang::BuiltinType::Double:
94      Name = "double";
95      break;
96    case clang::BuiltinType::Char_S:
97      Name = "char";
98      break;
99    case clang::BuiltinType::Short:
100      Name = "short";
101      break;
102    case clang::BuiltinType::Int:
103      Name = "int";
104      break;
105    case clang::BuiltinType::Long:
106      Name = "long";
107      break;
108    case clang::BuiltinType::UChar:
109      Name = "uchar";
110      break;
111    case clang::BuiltinType::UShort:
112      Name = "ushort";
113      break;
114    case clang::BuiltinType::UInt:
115      Name = "uint";
116      break;
117    case clang::BuiltinType::ULong:
118      Name = "ulong";
119      break;
120    default:
121      return;
122  }
123
124  clang::DiagnosticBuilder DiagBuilder =  mDiagEngine.Report(
125    clang::FullSourceLoc(E->getLocStart(), mSM),
126    mDiagEngine.getCustomDiagID( clang::DiagnosticsEngine::Warning,
127    "untyped rsSetElementAt() can reduce performance. "
128    "Use rsSetElementAt_%0%1() instead."));
129  DiagBuilder << Name;
130
131  if (VectorTy) {
132    DiagBuilder << VectorTy->getNumElements();
133  } else {
134    DiagBuilder << "";
135  }
136
137  return;
138}
139
140void RSCheckAST::VisitCallExpr(clang::CallExpr *E) {
141  WarnOnSetElementAt(E);
142
143  for (clang::CallExpr::arg_iterator AI = E->arg_begin(), AE = E->arg_end();
144       AI != AE; ++AI) {
145    Visit(*AI);
146  }
147}
148
149void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
150  if (!FD) {
151    return;
152  }
153
154  if (mIsFilterscript) {
155    // Validate parameters for Filterscript.
156    size_t numParams = FD->getNumParams();
157
158    clang::QualType resultType = FD->getResultType().getCanonicalType();
159
160    // We use FD as our NamedDecl in the case of a bad return type.
161    if (!RSExportType::ValidateType(C, resultType, FD,
162                                    FD->getLocStart(), mTargetAPI,
163                                    mIsFilterscript)) {
164      mValid = false;
165    }
166
167    for (size_t i = 0; i < numParams; i++) {
168      clang::ParmVarDecl *PVD = FD->getParamDecl(i);
169      clang::QualType QT = PVD->getType().getCanonicalType();
170      if (!RSExportType::ValidateType(C, QT, PVD, PVD->getLocStart(),
171                                      mTargetAPI, mIsFilterscript)) {
172        mValid = false;
173      }
174    }
175  }
176
177  bool saveKernel = mInKernel;
178  mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, &mDiagEngine, FD);
179
180  if (clang::Stmt *Body = FD->getBody()) {
181    Visit(Body);
182  }
183
184  mInKernel = saveKernel;
185}
186
187
188void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
189  if (!VD) {
190    return;
191  }
192
193  clang::QualType QT = VD->getType();
194
195  if (VD->getFormalLinkage() == clang::ExternalLinkage) {
196    llvm::StringRef TypeName;
197    const clang::Type *T = QT.getTypePtr();
198    if (!RSExportType::NormalizeType(T, TypeName, Context, VD)) {
199      mValid = false;
200    }
201  }
202
203  // We don't allow static (non-const) variables within kernels.
204  if (mInKernel && VD->isStaticLocal()) {
205    if (!QT.isConstQualified()) {
206      mDiagEngine.Report(
207        clang::FullSourceLoc(VD->getLocation(), mSM),
208        mDiagEngine.getCustomDiagID(
209          clang::DiagnosticsEngine::Error,
210          "Non-const static variables are not allowed in kernels: '%0'"))
211          << VD->getName();
212      mValid = false;
213    }
214  }
215
216  if (!RSExportType::ValidateVarDecl(VD, mTargetAPI, mIsFilterscript)) {
217    mValid = false;
218  } else if (clang::Expr *Init = VD->getInit()) {
219    // Only check the initializer if the decl is already ok.
220    Visit(Init);
221  }
222}
223
224
225void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
226  if (!SlangRS::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
227    for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
228                                        E = DS->decl_end();
229         I != E;
230         ++I) {
231      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
232        ValidateVarDecl(VD);
233      } else if (clang::FunctionDecl *FD =
234            llvm::dyn_cast<clang::FunctionDecl>(*I)) {
235        ValidateFunctionDecl(FD);
236      }
237    }
238  }
239}
240
241
242void RSCheckAST::VisitCastExpr(clang::CastExpr *CE) {
243  if (CE->getCastKind() == clang::CK_BitCast) {
244    clang::QualType QT = CE->getType();
245    const clang::Type *T = QT.getTypePtr();
246    if (T->isVectorType()) {
247      if (llvm::isa<clang::ImplicitCastExpr>(CE)) {
248        mDiagEngine.Report(
249          clang::FullSourceLoc(CE->getExprLoc(),
250                               mDiagEngine.getSourceManager()),
251          mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
252                                     "invalid implicit vector cast"));
253      } else {
254        mDiagEngine.Report(
255          clang::FullSourceLoc(CE->getExprLoc(),
256                               mDiagEngine.getSourceManager()),
257          mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
258                                      "invalid vector cast"));
259      }
260      mValid = false;
261    }
262  }
263  Visit(CE->getSubExpr());
264}
265
266
267void RSCheckAST::VisitExpr(clang::Expr *E) {
268  // This is where FS checks for code using pointer and/or 64-bit expressions
269  // (i.e. things like casts).
270
271  // First we skip implicit casts (things like function calls and explicit
272  // array accesses rely heavily on them and they are valid.
273  E = E->IgnoreImpCasts();
274  if (mIsFilterscript &&
275      !SlangRS::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
276      !RSExportType::ValidateType(C, E->getType(), NULL, E->getExprLoc(),
277                                  mTargetAPI, mIsFilterscript)) {
278    mValid = false;
279  } else {
280    // Only visit sub-expressions if we haven't already seen a violation.
281    VisitStmt(E);
282  }
283}
284
285
286bool RSCheckAST::Validate() {
287  clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
288  for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
289          DE = TUDecl->decls_end();
290       DI != DE;
291       DI++) {
292    if (!SlangRS::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
293      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
294        ValidateVarDecl(VD);
295      } else if (clang::FunctionDecl *FD =
296            llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
297        ValidateFunctionDecl(FD);
298      } else if (clang::Stmt *Body = (*DI)->getBody()) {
299        Visit(Body);
300      }
301    }
302  }
303
304  return mValid;
305}
306
307}  // namespace slang
308