Skip to content

Commit 2fb31f8

Browse files
committed
Add lightweight analysis that gathers non-address taken variables
Remove Value<T> boxing in those cases
1 parent dc5584e commit 2fb31f8

444 files changed

Lines changed: 6845 additions & 8245 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎cpp2rust/converter/converter.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ void Converter::EmitHoistedDecls(clang::CompoundStmt *body) {
381381
var && var->isLocalVarDecl() && !IsGlobalVar(var)) {
382382
hoisted_decls_.insert(var);
383383
if (ConvertVarDeclSkipInit(var)) {
384-
StrCat(token::kAssign, ConvertVarDefaultInit(var->getType()),
384+
StrCat(token::kAssign, ConvertVarDefaultInit(var),
385385
token::kSemiColon);
386386
}
387387
}
@@ -522,7 +522,7 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
522522
if (decl->hasInit()) {
523523
ConvertVarInit(decl->getType(), decl->getInit());
524524
} else if (!clang::isa<clang::ParmVarDecl>(decl)) {
525-
StrCat(ConvertVarDefaultInit(decl->getType()));
525+
StrCat(ConvertVarDefaultInit(decl));
526526
}
527527
}
528528

@@ -4155,8 +4155,8 @@ std::string Converter::GetDefaultAsStringFallback(clang::QualType qual_type) {
41554155
return std::format("<{}>::default()", ToString(qual_type));
41564156
}
41574157

4158-
std::string Converter::ConvertVarDefaultInit(clang::QualType qual_type) {
4159-
return GetDefaultAsString(qual_type);
4158+
std::string Converter::ConvertVarDefaultInit(const clang::VarDecl *decl) {
4159+
return GetDefaultAsString(decl->getType());
41604160
}
41614161

41624162
std::string

‎cpp2rust/converter/converter.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
586586

587587
virtual std::string GetDefaultAsStringFallback(clang::QualType qual_type);
588588

589-
virtual std::string ConvertVarDefaultInit(clang::QualType qual_type);
589+
virtual std::string ConvertVarDefaultInit(const clang::VarDecl *decl);
590590

591591
virtual std::string
592592
GetOverloadedFunctionName(const clang::CXXMethodDecl *decl);
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) 2022-present INESC-ID.
2+
// Distributed under the MIT license that can be found in the LICENSE file.
3+
4+
#include "converter/models/boxed_vars.h"
5+
6+
#include <clang/AST/RecursiveASTVisitor.h>
7+
8+
#include "converter/converter_lib.h"
9+
10+
namespace cpp2rust {
11+
namespace {
12+
// Whether decl may be stored without a Value, if its address is not taken:
13+
// scalars and arrays of non-arrays.
14+
bool CanUnbox(const clang::VarDecl *decl) {
15+
auto type = decl->getType();
16+
if (type->isConstantArrayType()) {
17+
if (type->getAsArrayTypeUnsafe()->getElementType()->isArrayType()) {
18+
return false;
19+
}
20+
} else if (!type->isScalarType()) {
21+
return false;
22+
}
23+
return (decl->isLocalVarDecl() || clang::isa<clang::ParmVarDecl>(decl)) &&
24+
!IsGlobalVar(decl) && !decl->isInitCapture() &&
25+
!IsVaListType(decl->getType());
26+
}
27+
28+
// Statements are visited before their children, so the uses of a variable
29+
// that only access its value are recorded before the variable is reached.
30+
class AddressTakenVisitor
31+
: public clang::RecursiveASTVisitor<AddressTakenVisitor> {
32+
public:
33+
explicit AddressTakenVisitor(std::unordered_set<const clang::VarDecl *> &vars)
34+
: address_taken_(vars) {}
35+
36+
bool shouldVisitTemplateInstantiations() const { return true; }
37+
38+
bool VisitDeclRefExpr(clang::DeclRefExpr *expr) {
39+
if (!value_uses_.contains(expr)) {
40+
AddressTaken(expr->getDecl());
41+
}
42+
return true;
43+
}
44+
45+
bool VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
46+
if (expr->getCastKind() == clang::CK_LValueToRValue) {
47+
AddValueUse(expr->getSubExpr());
48+
}
49+
return true;
50+
}
51+
52+
bool VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) {
53+
if (expr->getCastKind() == clang::CK_ToVoid) {
54+
AddValueUse(expr->getSubExpr());
55+
}
56+
return true;
57+
}
58+
59+
bool VisitBinaryOperator(clang::BinaryOperator *expr) {
60+
if (expr->isAssignmentOp()) {
61+
AddValueUse(expr->getLHS());
62+
}
63+
return true;
64+
}
65+
66+
bool VisitUnaryOperator(clang::UnaryOperator *expr) {
67+
if (expr->isIncrementDecrementOp()) {
68+
AddValueUse(expr->getSubExpr());
69+
}
70+
return true;
71+
}
72+
73+
bool VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr *expr) {
74+
if (!expr->isArgumentType()) {
75+
AddValueUse(expr->getArgumentExpr());
76+
}
77+
return true;
78+
}
79+
80+
bool VisitLambdaExpr(clang::LambdaExpr *expr) {
81+
for (const auto &capture : expr->captures()) {
82+
if (capture.capturesVariable()) {
83+
AddressTaken(capture.getCapturedVar());
84+
}
85+
}
86+
return true;
87+
}
88+
89+
private:
90+
// An element of an array is accessed without making a pointer to it.
91+
void AddValueUse(clang::Expr *expr) {
92+
expr = expr->IgnoreParens();
93+
if (auto *subscript = clang::dyn_cast<clang::ArraySubscriptExpr>(expr)) {
94+
auto *cast =
95+
clang::dyn_cast<clang::ImplicitCastExpr>(subscript->getBase());
96+
if (cast && cast->getCastKind() == clang::CK_ArrayToPointerDecay) {
97+
AddValueUse(cast->getSubExpr());
98+
}
99+
} else if (auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr)) {
100+
value_uses_.insert(ref);
101+
}
102+
}
103+
104+
void AddressTaken(const clang::ValueDecl *decl) {
105+
if (auto *var = clang::dyn_cast<clang::VarDecl>(decl);
106+
var && CanUnbox(var)) {
107+
address_taken_.insert(var);
108+
}
109+
}
110+
111+
std::unordered_set<const clang::VarDecl *> &address_taken_;
112+
// The references to variables that only access their value.
113+
std::unordered_set<const clang::DeclRefExpr *> value_uses_;
114+
};
115+
} // namespace
116+
117+
BoxedVars::BoxedVars(clang::ASTContext &ctx) {
118+
AddressTakenVisitor(address_taken_)
119+
.TraverseDecl(ctx.getTranslationUnitDecl());
120+
}
121+
122+
bool BoxedVars::contains(const clang::VarDecl *decl) const {
123+
return !CanUnbox(decl) || address_taken_.contains(decl);
124+
}
125+
} // namespace cpp2rust
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
// Copyright (c) 2022-present INESC-ID.
4+
// Distributed under the MIT license that can be found in the LICENSE file.
5+
6+
#include <clang/AST/ASTContext.h>
7+
#include <clang/AST/Decl.h>
8+
9+
#include <unordered_set>
10+
11+
namespace cpp2rust {
12+
// The local variables and parameters that must be stored in a Value, as
13+
// pointers to them are made: their address is taken, either explicitly with &
14+
// or by binding a reference to them, e.g., when they are passed by reference
15+
// to a function, or captured by a lambda. Any use that doesn't just read or
16+
// write the value of a variable is taken to make a pointer to it.
17+
//
18+
// Only scalars and arrays are stored directly; other variables are always
19+
// boxed.
20+
class BoxedVars {
21+
public:
22+
explicit BoxedVars(clang::ASTContext &ctx);
23+
24+
bool contains(const clang::VarDecl *decl) const;
25+
26+
private:
27+
std::unordered_set<const clang::VarDecl *> address_taken_;
28+
};
29+
} // namespace cpp2rust

0 commit comments

Comments
 (0)