Skip to content

Commit 76d56ee

Browse files
committed
Move instead of clone for returned vars
1 parent aac5b9e commit 76d56ee

44 files changed

Lines changed: 726 additions & 192 deletions

Some content is hidden

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

‎cpp2rust/converter/models/boxed_vars.cpp‎

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55

66
#include <clang/AST/RecursiveASTVisitor.h>
77

8+
#include <unordered_map>
9+
#include <vector>
10+
811
#include "converter/converter_lib.h"
12+
#include "converter/mapper.h"
913

1014
namespace cpp2rust {
1115
namespace {
@@ -31,6 +35,38 @@ bool CanUnbox(const clang::VarDecl *decl) {
3135
!IsVaListType(decl->getType());
3236
}
3337

38+
clang::Expr *IgnoreNoOpCasts(clang::Expr *expr) {
39+
while (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(expr)) {
40+
if (cast->getCastKind() != clang::CK_NoOp) {
41+
break;
42+
}
43+
expr = cast->getSubExpr();
44+
}
45+
return expr;
46+
}
47+
48+
// The object copied by a trivial copy or move constructor, which copies its
49+
// value, like an lvalue-to-rvalue conversion in C.
50+
clang::Expr *GetTrivialCopySource(clang::CXXConstructExpr *expr) {
51+
auto *ctor = expr->getConstructor();
52+
if (!ctor->isCopyOrMoveConstructor() || !ctor->isTrivial()) {
53+
return nullptr;
54+
}
55+
return IgnoreNoOpCasts(expr->getArg(0));
56+
}
57+
58+
// Whether expr is a call to a trivial copy or move assignment operator, which
59+
// assigns the value of its right operand, like a built-in assignment, unless
60+
// it is translated to a call to the operator.
61+
bool IsTrivialAssignment(clang::CXXOperatorCallExpr *expr) {
62+
auto *method =
63+
clang::dyn_cast_or_null<clang::CXXMethodDecl>(expr->getDirectCallee());
64+
return method &&
65+
(method->isCopyAssignmentOperator() ||
66+
method->isMoveAssignmentOperator()) &&
67+
method->isTrivial() && !IsUserOperatorCall(expr);
68+
}
69+
3470
// Statements are visited before their children, so the uses of a variable
3571
// that only access its value are recorded before the variable is reached.
3672
class AddressTakenVisitor
@@ -70,6 +106,21 @@ class AddressTakenVisitor
70106
return true;
71107
}
72108

109+
bool VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
110+
if (auto *source = GetTrivialCopySource(expr)) {
111+
AddValueUse(source);
112+
}
113+
return true;
114+
}
115+
116+
bool VisitCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr) {
117+
if (IsTrivialAssignment(expr)) {
118+
AddValueUse(expr->getArg(0));
119+
AddValueUse(IgnoreNoOpCasts(expr->getArg(1)));
120+
}
121+
return true;
122+
}
123+
73124
bool VisitBinaryOperator(clang::BinaryOperator *expr) {
74125
if (expr->isAssignmentOp()) {
75126
AddValueUse(expr->getLHS());
@@ -143,4 +194,85 @@ BoxedVars::BoxedVars(clang::ASTContext &ctx) {
143194
bool BoxedVars::contains(const clang::VarDecl *decl) const {
144195
return !CanUnbox(decl) || address_taken_.contains(decl);
145196
}
197+
198+
namespace {
199+
class MovableReadsVisitor
200+
: public clang::RecursiveASTVisitor<MovableReadsVisitor> {
201+
public:
202+
explicit MovableReadsVisitor(clang::ASTContext &ctx) : ctx_(ctx) {}
203+
204+
bool shouldVisitTemplateInstantiations() const { return true; }
205+
206+
std::unordered_set<const clang::DeclRefExpr *> Find(clang::Expr *expr) {
207+
TraverseStmt(expr);
208+
std::unordered_set<const clang::DeclRefExpr *> movable;
209+
for (auto *read : reads_) {
210+
if (refs_[read->getDecl()] == 1) {
211+
movable.insert(read);
212+
}
213+
}
214+
return movable;
215+
}
216+
217+
bool dataTraverseStmtPre(clang::Stmt *stmt) {
218+
excluded_ += IsExcluded(stmt);
219+
return true;
220+
}
221+
222+
bool dataTraverseStmtPost(clang::Stmt *stmt) {
223+
excluded_ -= IsExcluded(stmt);
224+
return true;
225+
}
226+
227+
bool VisitDeclRefExpr(clang::DeclRefExpr *expr) {
228+
++refs_[expr->getDecl()];
229+
return true;
230+
}
231+
232+
bool VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
233+
if (expr->getCastKind() == clang::CK_LValueToRValue) {
234+
AddRead(expr->getSubExpr());
235+
}
236+
return true;
237+
}
238+
239+
bool VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
240+
if (auto *source = GetTrivialCopySource(expr)) {
241+
AddRead(source);
242+
}
243+
return true;
244+
}
245+
246+
private:
247+
void AddRead(clang::Expr *expr) {
248+
if (auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr->IgnoreParens());
249+
ref && excluded_ == 0) {
250+
reads_.push_back(ref);
251+
}
252+
}
253+
254+
// Whether the reads in stmt may be translated more than once, or evaluated
255+
// more than once, e.g., in a loop or in a closure.
256+
bool IsExcluded(clang::Stmt *stmt) const {
257+
if (auto *call = clang::dyn_cast<clang::CallExpr>(stmt)) {
258+
return Mapper::Contains(ctx_, call->getCallee());
259+
}
260+
if (auto *construct = clang::dyn_cast<clang::CXXConstructExpr>(stmt)) {
261+
return !GetTrivialCopySource(construct);
262+
}
263+
return clang::isa<clang::CXXNewExpr, clang::StmtExpr, clang::LambdaExpr,
264+
clang::BinaryConditionalOperator>(stmt);
265+
}
266+
267+
clang::ASTContext &ctx_;
268+
unsigned excluded_ = 0;
269+
std::unordered_map<const clang::ValueDecl *, unsigned> refs_;
270+
std::vector<const clang::DeclRefExpr *> reads_;
271+
};
272+
} // namespace
273+
274+
std::unordered_set<const clang::DeclRefExpr *>
275+
FindMovableReads(clang::ASTContext &ctx, clang::Expr *expr) {
276+
return MovableReadsVisitor(ctx).Find(expr);
277+
}
146278
} // namespace cpp2rust

‎cpp2rust/converter/models/boxed_vars.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <clang/AST/ASTContext.h>
77
#include <clang/AST/Decl.h>
8+
#include <clang/AST/Expr.h>
89

910
#include <unordered_set>
1011

@@ -26,4 +27,12 @@ class BoxedVars {
2627
private:
2728
std::unordered_set<const clang::VarDecl *> address_taken_;
2829
};
30+
31+
// The reads of the value of variables in a returned expression that are the
32+
// only reference to their variable in it. As the variables die on return,
33+
// these reads can move the value instead of copying it. Excluded are the
34+
// reads that may be translated more than once, or in a loop, such as the
35+
// arguments of calls translated by rules.
36+
std::unordered_set<const clang::DeclRefExpr *>
37+
FindMovableReads(clang::ASTContext &ctx, clang::Expr *expr);
2938
} // namespace cpp2rust

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,19 @@ bool ConverterRefCount::IsUnboxedPlace(const clang::Expr *expr) const {
201201
expr = expr->IgnoreParenImpCasts();
202202
// Fields of unions and references are accessed through pointers.
203203
if (auto *member = clang::dyn_cast<clang::MemberExpr>(expr)) {
204-
return !member->isArrow() && !member->getBase()->getType()->isUnionType() &&
205-
!member->getMemberDecl()->getType()->isReferenceType() &&
206-
IsUnboxedPlace(member->getBase());
204+
return !member->getMemberDecl()->getType()->isReferenceType() &&
205+
IsFieldOfUnboxedPlace(member);
207206
}
208207
auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
209208
return ref && IsUnboxedVar(ref->getDecl());
210209
}
211210

211+
bool ConverterRefCount::IsFieldOfUnboxedPlace(
212+
const clang::MemberExpr *expr) const {
213+
return !expr->isArrow() && !expr->getBase()->getType()->isUnionType() &&
214+
IsUnboxedPlace(expr->getBase());
215+
}
216+
212217
ConverterRefCount::ConversionKind
213218
ConverterRefCount::VarConversionKind(const clang::VarDecl *decl) const {
214219
if (in_function_formals_) {
@@ -527,7 +532,7 @@ bool ConverterRefCount::VisitArraySubscriptExpr(
527532
StrCat(GetPointerDerefPrefix(expr->getType()));
528533
ConvertArraySubscript(base, expr->getIdx(), expr->getType());
529534
StrCat(GetPointerDerefSuffix(expr->getType()));
530-
SetValueFreshness(expr->getType());
535+
SetDerefFreshness(expr->getType());
531536
} else {
532537
ConvertArraySubscript(base, expr->getIdx(), expr->getType());
533538
}
@@ -996,6 +1001,8 @@ void ConverterRefCount::ConvertDeclRefValue(clang::Expr *expr,
9961001
return;
9971002
}
9981003
StrCat(DerefPtrExpr(str, ref->getPointeeType()));
1004+
SetDerefFreshness(ref->getPointeeType());
1005+
return;
9991006
}
10001007
SetValueFreshness(expr->getType());
10011008
}
@@ -1005,7 +1012,10 @@ void ConverterRefCount::ConvertDeclRefValue(clang::Expr *expr,
10051012
if (IsUnboxedVar(decl)) {
10061013
assert(!isAddrOf() && "pointer to an unboxed variable");
10071014
StrCat(str);
1008-
if (decl_t->isPointerType()) {
1015+
if (isRValue() &&
1016+
moved_reads_.contains(clang::dyn_cast<clang::DeclRefExpr>(expr))) {
1017+
SetFreshType(expr->getType());
1018+
} else if (decl_t->isPointerType()) {
10091019
computed_expr_type_ = ComputedExprType::Pointer;
10101020
} else {
10111021
SetValueFreshness(expr->getType());
@@ -1277,7 +1287,7 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {
12771287
str = std::format("{{ {} {} }}", ctx->temporary_bindings, str);
12781288
}
12791289
StrCat(str);
1280-
SetValueFreshness(ref->getPointeeType());
1290+
SetDerefFreshness(ref->getPointeeType());
12811291
return false;
12821292
}
12831293

@@ -1361,7 +1371,9 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
13611371

13621372
if (expr->isXValue() && sub_expr->isLValue()) {
13631373
Convert(sub_expr);
1364-
computed_expr_type_ = ComputedExprType::Value;
1374+
if (!isFresh()) {
1375+
computed_expr_type_ = ComputedExprType::Value;
1376+
}
13651377
return false;
13661378
}
13671379

@@ -1667,6 +1679,16 @@ bool ConverterRefCount::VisitUnaryExprOrTypeTraitExpr(
16671679
return Converter::VisitUnaryExprOrTypeTraitExpr(expr);
16681680
}
16691681

1682+
bool ConverterRefCount::VisitReturnStmt(clang::ReturnStmt *stmt) {
1683+
auto *value = stmt->getRetValue();
1684+
auto moved_reads =
1685+
std::exchange(moved_reads_, value ? FindMovableReads(ctx_, value)
1686+
: decltype(moved_reads_){});
1687+
Converter::VisitReturnStmt(stmt);
1688+
moved_reads_ = std::move(moved_reads);
1689+
return false;
1690+
}
1691+
16701692
bool ConverterRefCount::VisitStmtExpr(clang::StmtExpr *expr) {
16711693
PushConversionKind push(*this, ConversionKind::FullRefCount);
16721694
Converter::VisitStmtExpr(expr);
@@ -1879,7 +1901,7 @@ void ConverterRefCount::ConvertUnionMemberAccessor(clang::MemberExpr *expr) {
18791901
return;
18801902
}
18811903
StrCat(DerefPtrExpr(str, member->getType()));
1882-
SetValueFreshness(member->getType());
1904+
SetDerefFreshness(member->getType());
18831905
}
18841906

18851907
bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
@@ -1941,8 +1963,11 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
19411963
if (known) {
19421964
str = GetMappedAsString(expr);
19431965
} else if (clang::isa<clang::FieldDecl>(member)) {
1944-
// The Value or the pointer that the field holds.
1945-
str = ReadField(expr, value_field_ptr ? ".as_pointer()" : ".clone()");
1966+
// The Value or the pointer that the field holds, which is used in place
1967+
// in an unboxed struct.
1968+
str = ReadField(expr, value_field_ptr ? ".as_pointer()"
1969+
: IsFieldOfUnboxedPlace(expr) ? ""
1970+
: ".clone()");
19461971
} else {
19471972
Buffer buf(*this);
19481973
PushExprKind push(*this, ExprKind::RValue);
@@ -1967,7 +1992,10 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
19671992
return false;
19681993
}
19691994
StrCat(DerefPtrExpr(str, member->getType().getNonReferenceType()));
1970-
} else if (isRValue()) {
1995+
SetDerefFreshness(member->getType().getNonReferenceType());
1996+
return false;
1997+
}
1998+
if (isRValue()) {
19711999
StrCat(std::format("(*{}.borrow())", std::move(str)));
19722000
} else {
19732001
StrCat(std::format("(*{}.borrow_mut())", std::move(str)));
@@ -2053,7 +2081,7 @@ std::string ConverterRefCount::ReadField(clang::MemberExpr *expr,
20532081
if (!TypeIsCopyable(expr->getMemberDecl()->getType())) {
20542082
str += copy;
20552083
}
2056-
if (IsUnboxedPlace(expr)) {
2084+
if (IsFieldOfUnboxedPlace(expr)) {
20572085
return str;
20582086
}
20592087
return ptr.empty() ? std::format("{{ {} }}", str)
@@ -2710,7 +2738,7 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
27102738
Convert(expr->getArg(0));
27112739
if (deref) {
27122740
StrCat(GetPointerDerefSuffix(expr->getType()));
2713-
SetValueFreshness(expr->getType());
2741+
SetDerefFreshness(expr->getType());
27142742
}
27152743
}
27162744
break;
@@ -2779,7 +2807,7 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
27792807
computed_expr_type_ = ComputedExprType::FreshPointer;
27802808
} else {
27812809
StrCat(GetPointerDerefSuffix(expr->getType()));
2782-
SetValueFreshness(expr->getType());
2810+
SetDerefFreshness(expr->getType());
27832811
}
27842812
}
27852813
break;
@@ -2922,7 +2950,7 @@ void ConverterRefCount::ConvertPointerSubscript(
29222950
}
29232951
if (deref) {
29242952
StrCat(GetPointerDerefSuffix(expr->getType()));
2925-
SetValueFreshness(expr->getType());
2953+
SetDerefFreshness(expr->getType());
29262954
}
29272955
}
29282956

@@ -2986,7 +3014,7 @@ void ConverterRefCount::ConvertDeref(clang::Expr *expr) {
29863014
Convert(expr);
29873015
if (deref) {
29883016
StrCat(GetPointerDerefSuffix(pointee_type));
2989-
SetValueFreshness(pointee_type);
3017+
SetDerefFreshness(pointee_type);
29903018
}
29913019
}
29923020
str = std::move(buf).str();
@@ -3022,7 +3050,7 @@ void ConverterRefCount::ConvertArrow(clang::Expr *expr) {
30223050
}
30233051
auto ptr = ToString(expr);
30243052
StrCat(DerefPtrExpr(ptr, pointee_type));
3025-
SetValueFreshness(pointee_type);
3053+
SetDerefFreshness(pointee_type);
30263054
return;
30273055
}
30283056

@@ -3060,12 +3088,21 @@ void ConverterRefCount::ConvertConstructedValue(clang::QualType type,
30603088
ConvertVarInit(type, ctor);
30613089
}
30623090

3091+
bool ConverterRefCount::DerefReadsValue(clang::QualType pointee_type) {
3092+
return pointee_type.isPODType(ctx_) && !pointee_type->isRecordType();
3093+
}
3094+
30633095
const char *
30643096
ConverterRefCount::GetPointerDerefSuffix(clang::QualType pointee_type) {
3065-
if (pointee_type.isPODType(ctx_) && !pointee_type->isRecordType()) {
3066-
return ".read()";
3097+
return DerefReadsValue(pointee_type) ? ".read()" : ".upgrade().deref()";
3098+
}
3099+
3100+
void ConverterRefCount::SetDerefFreshness(clang::QualType pointee_type) {
3101+
if (DerefReadsValue(pointee_type)) {
3102+
SetFreshType(pointee_type);
3103+
} else {
3104+
SetValueFreshness(pointee_type);
30673105
}
3068-
return ".upgrade().deref()";
30693106
}
30703107

30713108
const char *

0 commit comments

Comments
 (0)