Skip to content

Commit 5f4341b

Browse files
committed
Support for branchs
1 parent 3103be9 commit 5f4341b

3 files changed

Lines changed: 463 additions & 18 deletions

File tree

include/CppInterOp/CppInterOpTypes.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,10 @@ enum class AllocType : unsigned char {
412412
NewArr,
413413
Malloc,
414414
Unknown,
415-
CustomAlloc
415+
CustomAlloc,
416+
Null,
417+
OperatorNew,
418+
OperatorNewArr
416419
};
417420

418421
inline QualKind operator|(QualKind a, QualKind b) {

lib/CppInterOp/CppInterOp.cpp

Lines changed: 165 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,9 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16501650
// Result var keeps the combination all possible values of previous return
16511651
// statements
16521652
std::optional<AllocType> result;
1653+
// A map every branch writes the previous value of overwriten variables
1654+
std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>* undoLog =
1655+
nullptr;
16531656

16541657
AllocationTraverser(std::unordered_map<const clang::FunctionDecl*,
16551658
std::optional<AllocType>>& cache)
@@ -1665,6 +1668,109 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16651668
return RecursiveASTVisitor::TraverseDecl(D);
16661669
}
16671670

1671+
std::optional<AllocType> join(std::optional<AllocType> a,
1672+
std::optional<AllocType> b) {
1673+
if (a == AllocType::Null)
1674+
return b;
1675+
1676+
if (b == AllocType::Null)
1677+
return a;
1678+
1679+
if (a == b)
1680+
return a;
1681+
return AllocType::Unknown;
1682+
}
1683+
void undoOnVarMap() {
1684+
for (auto& [VD, oldVal] : *undoLog) {
1685+
auto it = varMap.find(VD);
1686+
if (it == varMap.end())
1687+
continue;
1688+
varMap[VD] = oldVal;
1689+
}
1690+
}
1691+
1692+
bool TraverseIfStmt(clang::IfStmt* IS) {
1693+
TraverseStmt(IS->getConditionVariableDeclStmt());
1694+
TraverseStmt(IS->getCond());
1695+
auto* undoLogCopy = undoLog;
1696+
std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>
1697+
undoLogThen;
1698+
undoLog = &undoLogThen;
1699+
TraverseStmt(IS->getThen());
1700+
auto* elseBranch = IS->getElse();
1701+
// There is no else
1702+
if (!elseBranch) {
1703+
for (auto& [VD, val] : undoLogThen) {
1704+
auto it = varMap.find(VD);
1705+
if (it == varMap.end())
1706+
continue;
1707+
// Overwrite varMap with join of overwriten and previous value
1708+
it->second = join(val, it->second);
1709+
// If branch is nested, inform upper branch about your changes
1710+
if (undoLogCopy)
1711+
undoLogCopy->try_emplace(VD, val);
1712+
}
1713+
undoLog = undoLogCopy;
1714+
return true;
1715+
}
1716+
1717+
// Save overwriten values in if branch to join
1718+
std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>
1719+
valuesInIfBranch;
1720+
for (auto& [VD, val] : undoLogThen) {
1721+
auto it = varMap.find(VD);
1722+
if (it == varMap.end())
1723+
continue;
1724+
valuesInIfBranch[VD] = it->second;
1725+
}
1726+
1727+
// Take changes on varMap back before going to else branch
1728+
undoOnVarMap();
1729+
1730+
std::unordered_map<const clang::VarDecl*, std::optional<AllocType>>
1731+
undoLogElse;
1732+
undoLog = &undoLogElse;
1733+
TraverseStmt(elseBranch);
1734+
1735+
for (auto& [VD, val] : undoLogElse) {
1736+
auto it = varMap.find(VD);
1737+
if (it == varMap.end())
1738+
continue;
1739+
auto it2 = valuesInIfBranch.find(VD);
1740+
// If var is just changed in else branch
1741+
if (it2 == valuesInIfBranch.end()) {
1742+
it->second = join(val, it->second);
1743+
continue;
1744+
}
1745+
// If var is changed in both
1746+
it->second = join(it->second, it2->second);
1747+
}
1748+
1749+
for (auto& [VD, val] : valuesInIfBranch) {
1750+
auto it = varMap.find(VD);
1751+
if (it == varMap.end())
1752+
continue;
1753+
// If var is just changed in if branch
1754+
if (undoLogElse.find(VD) == undoLogElse.end()) {
1755+
it->second = join(val, it->second);
1756+
continue;
1757+
}
1758+
// If var is changed in both, here for extra safety
1759+
it->second = join(val, it->second);
1760+
}
1761+
1762+
// Inform upper branch about changes done for both inner if and else branch
1763+
if (undoLogCopy) {
1764+
for (auto& [VD, val] : undoLogThen)
1765+
undoLogCopy->try_emplace(VD, val);
1766+
for (auto& [VD, val] : undoLogElse)
1767+
undoLogCopy->try_emplace(VD, val);
1768+
}
1769+
1770+
undoLog = undoLogCopy;
1771+
return true;
1772+
}
1773+
16681774
bool VisitVarDecl(VarDecl* VD) {
16691775
Expr* expr = VD->getInit();
16701776
if (expr)
@@ -1675,15 +1781,28 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16751781
}
16761782

16771783
bool VisitBinaryOperator(clang::BinaryOperator* BO) {
1678-
if (BO->getOpcode() != BO_Assign)
1679-
return true;
16801784
Expr* LHS = BO->getLHS();
16811785
LHS = LHS->IgnoreParenCasts();
1682-
if (auto* DRE = dyn_cast<DeclRefExpr>(LHS)) {
1683-
if (auto* VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1684-
Expr* RHS = BO->getRHS();
1685-
varMap[VD] = handleExpr(RHS);
1686-
}
1786+
auto* DRE = dyn_cast<DeclRefExpr>(LHS);
1787+
if (!DRE)
1788+
return true;
1789+
1790+
auto* VD = dyn_cast<VarDecl>(DRE->getDecl());
1791+
if (!VD)
1792+
return true;
1793+
1794+
if (BO->getOpcode() == BO_Assign) {
1795+
if (undoLog)
1796+
undoLog->try_emplace(VD, varMap[VD]);
1797+
Expr* RHS = BO->getRHS();
1798+
varMap[VD] = handleExpr(RHS);
1799+
return true;
1800+
}
1801+
if (BO->isCompoundAssignmentOp()) {
1802+
if (undoLog)
1803+
undoLog->try_emplace(VD, varMap[VD]);
1804+
varMap[VD] = AllocType::Unknown;
1805+
return true;
16871806
}
16881807
return true;
16891808
}
@@ -1695,21 +1814,35 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
16951814
std::optional<AllocType> tmp = handleExpr(retExpr);
16961815
if (!tmp.has_value())
16971816
return true;
1698-
if (!result.has_value())
1817+
if (!result.has_value()) {
16991818
result = tmp;
1819+
return true;
1820+
}
17001821
// If function's allocation behaviour differs between different cases,
17011822
// analyzer returns unknown.
1702-
else if (*result != tmp) {
1703-
result = AllocType::Unknown;
1823+
result = join(result, tmp);
1824+
if (result == AllocType::Unknown)
17041825
return false;
1705-
}
17061826
return true;
17071827
}
17081828

17091829
std::optional<AllocType> handleCall(const clang::CallExpr* CE) {
17101830
if (const auto* FD = CE->getDirectCallee()) {
17111831
if (FD->getBuiltinID() == Builtin::ID::BImalloc)
17121832
return AllocType::Malloc;
1833+
if (FD->getBuiltinID() == Builtin::ID::BI__builtin_operator_new)
1834+
return AllocType::OperatorNew;
1835+
// Detects operator new/new[]/delete/delete[]
1836+
if (FD->isReplaceableGlobalAllocationFunction()) {
1837+
switch (FD->getOverloadedOperator()) {
1838+
case OO_New:
1839+
return AllocType::OperatorNew;
1840+
case OO_Array_New:
1841+
return AllocType::OperatorNewArr;
1842+
default:
1843+
break; // OO_Delete/OO_Array_Delete
1844+
}
1845+
}
17131846
auto it = visitedFuncs.find(FD);
17141847
if (it == visitedFuncs.end()) {
17151848
visitedFuncs[FD] = std::nullopt;
@@ -1722,8 +1855,16 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
17221855
}
17231856

17241857
static AllocType handleNew(const clang::CXXNewExpr* CNE) {
1725-
if (CNE->getNumPlacementArgs() > 0)
1726-
return AllocType::None;
1858+
if (CNE->getNumPlacementArgs() > 0) {
1859+
/*
1860+
Non-allocating placement allocation functions
1861+
void* operator new ( std::size_t count, void* ptr );
1862+
void* operator new[]( std::size_t count, void* ptr );
1863+
*/
1864+
const clang::FunctionDecl* OpNew = CNE->getOperatorNew();
1865+
if (OpNew && OpNew->isReservedGlobalPlacementOperator())
1866+
return AllocType::None;
1867+
}
17271868
if (CNE->isArray())
17281869
return AllocType::NewArr;
17291870
return AllocType::New;
@@ -1749,6 +1890,15 @@ struct AllocationTraverser : RecursiveASTVisitor<AllocationTraverser> {
17491890
// Case: malloc or another func call
17501891
if (const auto* CE = dyn_cast<CallExpr>(finExpr))
17511892
return handleCall(CE);
1893+
1894+
// Case: NULL, nullptr or (int*)(__integerLiteral__)
1895+
const clang::Expr* parExpr = expr->IgnoreParens();
1896+
while (const auto* CE = dyn_cast<CastExpr>(parExpr)) {
1897+
if (CE->getCastKind() == CK_NullToPointer)
1898+
return AllocType::Null;
1899+
parExpr = CE->getSubExpr();
1900+
}
1901+
17521902
return AllocType::None;
17531903
}
17541904
};
@@ -1769,6 +1919,8 @@ AnalyzeAllocType(const clang::FunctionDecl* Fn,
17691919
if (!CmpStmt)
17701920
return AllocType::Unknown;
17711921
AllocationTraverser Traverser(visitedFuncs);
1922+
for (auto* parm : Fn->parameters())
1923+
Traverser.VisitVarDecl(parm);
17721924
Traverser.TraverseStmt(const_cast<clang::CompoundStmt*>(CmpStmt));
17731925
auto res = Traverser.result;
17741926
visitedFuncs[Fn] = res;

0 commit comments

Comments
 (0)