blob: eb8dfdc5d9ace9c991d9b86fc4c2e4a797523f5a [file] [edit]
//===- EntityPointerLevel.cpp -----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "SSAFAnalysesCommon.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryExtractor.h"
#include <optional>
using namespace clang;
using namespace ssaf;
namespace clang::ssaf {
// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
// associated with the declared type of the base address of `E`. If the base
// address of `E` is not associated with an entity, the translation result is an
// empty set.
//
// The translation is a process of traversing into the pointer 'E' until its
// base address can be represented by an entity, with the number of dereferences
// tracked by incrementing the pointer level. Naturally, taking address of, as
// the inverse operation of dereference, is tracked by decrementing the pointer
// level.
//
// For example, suppose there are pointers and arrays declared as
// int *ptr, **p1, **p2;
// int arr[10][10];
// , the translation of expressions involving these base addresses will be:
// Translate(ptr + 5) -> {(ptr, 1)}
// Translate(arr[5]) -> {(arr, 2)}
// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
// Translate(&arr[5]) -> {(arr, 1)}
class EntityPointerLevelTranslator
: ConstStmtVisitor<EntityPointerLevelTranslator,
Expected<EntityPointerLevelSet>> {
friend class StmtVisitorBase;
// Fallback method for all unsupported expression kind:
Expected<EntityPointerLevelSet> fallback(const Stmt *S) {
// Report an error/warning (at least in debug mode) for any unsupported kind
// of pointer/array typed expression, because we want to understand every
// pointer/array expression. But for non-pointer/array typed expressions, we
// could silently ignore unsupported kinds. This translator visits
// non-pointer/array typed expressions because of address-of expressions.
if (const Expr *E = dyn_cast<Expr>(S); E && hasPtrOrArrType(E))
return makeErrAtNode(Ctx, E,
"attempt to translate %s to EntityPointerLevels",
E->getStmtClassName());
return EntityPointerLevelSet{};
}
Expected<EntityPointerLevel>
createEntityPointerLevelFor(const NamedDecl *ND) {
std::optional<EntityId> Id = Extractor.addEntity(ND);
if (!Id)
return makeErrAtNode(Ctx, ND, "failed to create EntityId for %s",
ND->getDeclKindName());
return EntityPointerLevel{buildEntityPointerLevel(*Id, 1)};
}
Expected<EntityPointerLevel>
createEntityPointerLevelForReturn(const FunctionDecl *FD) {
std::optional<EntityId> Id = Extractor.addEntityForReturn(FD);
if (!Id) {
return makeErrAtNode(Ctx, FD, "failed to create EntityId for function %s",
cast<NamedDecl>(FD)->getNameAsString().c_str());
}
return EntityPointerLevel{buildEntityPointerLevel(*Id, 1)};
}
// The common helper function for Translate(*base):
// Translate(*base) -> Translate(base) with .pointerLevel + 1
Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
assert(hasPtrOrArrType(Ptr));
Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
if (!SubResult)
return SubResult.takeError();
auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
}
TUSummaryExtractor &Extractor;
ASTContext &Ctx;
public:
EntityPointerLevelTranslator(TUSummaryExtractor &Extractor, ASTContext &Ctx)
: Extractor(Extractor), Ctx(Ctx) {}
Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
if (!IsRet)
return createEntityPointerLevelFor(D);
if (const auto *FD = dyn_cast<FunctionDecl>(D))
return createEntityPointerLevelForReturn(FD);
return makeErrAtNode(Ctx, D, "attempt to get entity for return of %s",
D->getDeclKindName());
}
static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1});
}
static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
assert(E.getPointerLevel() > 0);
return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1});
}
private:
Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
return fallback(E);
}
// Translate(base + x) -> Translate(base)
// Translate(x + base) -> Translate(base)
// Translate(base - x) -> Translate(base)
// Translate(base {+=, -=, =} x) -> Translate(base)
// Translate(x, base) -> Translate(base)
Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
switch (E->getOpcode()) {
case clang::BO_Add:
if (hasPtrOrArrType(E->getLHS()))
return Visit(E->getLHS());
return Visit(E->getRHS());
case clang::BO_Sub:
case clang::BO_AddAssign:
case clang::BO_SubAssign:
case clang::BO_Assign:
return Visit(E->getLHS());
case clang::BO_Comma:
return Visit(E->getRHS());
default:
return fallback(E);
}
}
// Translate({++, --}base) -> Translate(base)
// Translate(base{++, --}) -> Translate(base)
// Translate(*base) -> Translate(base) with .pointerLevel += 1
// Translate(&base) -> {}, if Translate(base) is {}
// -> Translate(base) with .pointerLevel -= 1
// Translate(+base) -> Translate(base)
Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
switch (E->getOpcode()) {
case clang::UO_PostInc:
case clang::UO_PostDec:
case clang::UO_PreInc:
case clang::UO_PreDec:
return Visit(E->getSubExpr());
case clang::UO_AddrOf: {
Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
if (!SubResult)
return SubResult.takeError();
auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
}
case clang::UO_Deref:
return translateDereferencePointer(E->getSubExpr());
case clang::UO_Plus:
return Visit(E->getSubExpr());
default:
return fallback(E);
}
}
// Translate((T*)base) -> Translate(base) if base has pointer type
// -> {} otherwise
Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
if (hasPtrOrArrType(E->getSubExpr()))
return Visit(E->getSubExpr());
return EntityPointerLevelSet{};
}
// Translate(f(...)) -> {} if it is an indirect call
// -> {(f_return, 1)}, otherwise
Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
if (auto *FD = E->getDirectCallee()) {
if (auto ReturnId = Extractor.addEntityForReturn(FD))
return EntityPointerLevelSet{buildEntityPointerLevel(*ReturnId, 1)};
}
return EntityPointerLevelSet{};
}
// Translate(base[x]) -> Translate(*base)
Expected<EntityPointerLevelSet>
VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
return translateDereferencePointer(E->getBase());
}
// Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
Expected<EntityPointerLevelSet>
VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
if (ReT && ReF) {
ReT->insert(ReF->begin(), ReF->end());
return ReT;
}
if (!ReF && !ReT)
return llvm::joinErrors(ReT.takeError(), ReF.takeError());
if (!ReF)
return ReF.takeError();
return ReT.takeError();
}
Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
return Visit(E->getSubExpr());
}
// Translate("string-literal") -> {} // no entity involved
Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
return EntityPointerLevelSet{};
}
// Translate(predefined-expr) -> {} // treated the same as string literals
Expected<EntityPointerLevelSet> VisitPredefinedExpr(const PredefinedExpr *E) {
return EntityPointerLevelSet{};
}
// Translate(integer-literal) -> {} // no entity involved
Expected<EntityPointerLevelSet> VisitIntegerLiteral(const IntegerLiteral *E) {
return EntityPointerLevelSet{};
}
// Translate(DRE) -> {(Decl, 1)}
Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
auto Res = createEntityPointerLevelFor(E->getDecl());
if (!Res)
return Res.takeError();
return EntityPointerLevelSet{*Res};
}
// Translate({., ->}f) -> {(MemberDecl, 1)}
Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
auto Res = createEntityPointerLevelFor(E->getMemberDecl());
if (!Res)
return Res.takeError();
return EntityPointerLevelSet{*Res};
}
// Unwrap CXXDefaultArgExpr
Expected<EntityPointerLevelSet>
VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
return Visit(E->getExpr());
}
// Unwrap OpaqueValueExpr
Expected<EntityPointerLevelSet>
VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
return Visit(S->getSourceExpr());
}
// Unwrap ExprWithCleanups
Expected<EntityPointerLevelSet>
VisitExprWithCleanups(const ExprWithCleanups *S) {
return Visit(S->getSubExpr());
}
// Unwrap MaterializeTemporaryExpr
Expected<EntityPointerLevelSet>
VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *S) {
return Visit(S->getSubExpr());
}
// Unwrap CXXDefaultInitExpr
Expected<EntityPointerLevelSet>
VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) {
return Visit(E->getExpr());
}
// Translate(`nullptr`) -> {}
Expected<EntityPointerLevelSet>
VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *S) {
return EntityPointerLevelSet{};
}
// Translate(`this`) -> {}
Expected<EntityPointerLevelSet> VisitCXXThisExpr(const CXXThisExpr *S) {
return EntityPointerLevelSet{};
}
// Translate(`new`/`new [*]`) -> {}
Expected<EntityPointerLevelSet> VisitCXXNewExpr(const CXXNewExpr *S) {
return EntityPointerLevelSet{};
}
// ImplicitValueInitExpr, for raw pointer type,
// evaluates to a compile-time constant zero (or null). So no EPL in the
// result.
Expected<EntityPointerLevelSet>
VisitImplicitValueInitExpr(const ImplicitValueInitExpr *S) {
return EntityPointerLevelSet{};
}
// The InitListExpr must be an empty or singleton list that
// initializes a pointer scalar. Other cases are unexpected thus an error.
Expected<EntityPointerLevelSet> VisitInitListExpr(const InitListExpr *E) {
if (E->getNumInits() < 1)
return EntityPointerLevelSet{};
if (E->getType()->isPointerType())
return Visit(E->getInit(0));
return llvm::createStringError(
"Cannot translate an InitListExpr to EntityPointerLevels if it is not "
"an empty or singleton list that initializes a pointer scalar");
}
// Clang may default initializes an array with a CXXConstructExpr. Fallback on
// other cases, if they exist.
// When a CXXConstructExpr has an array type, clang is initializing an array
// of class-type objects with default values. In this case, no entity is
// associated with the initializer.
Expected<EntityPointerLevelSet>
VisitCXXConstructExpr(const CXXConstructExpr *E) {
if (E->getType()->isArrayType()) {
return EntityPointerLevelSet{};
}
return fallback(E);
}
// No entity is associated with a CXXScalarValueInitExpr:
Expected<EntityPointerLevelSet>
VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E) {
return EntityPointerLevelSet{};
}
};
} // namespace clang::ssaf
Expected<EntityPointerLevelSet>
clang::ssaf::translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
TUSummaryExtractor &Extractor) {
EntityPointerLevelTranslator Translator(Extractor, Ctx);
return Translator.translate(E);
}
/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
Expected<EntityPointerLevel> clang::ssaf::createEntityPointerLevel(
const NamedDecl *ND, TUSummaryExtractor &Extractor, bool IsFunRet) {
EntityPointerLevelTranslator Translator(Extractor, ND->getASTContext());
return Translator.translate(ND, IsFunRet);
}
EntityPointerLevel
clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) {
return EntityPointerLevelTranslator::incrementPointerLevel(E);
}
EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id,
unsigned PtrLv) {
return EntityPointerLevel({Id, PtrLv});
}