Skip to content

Commit 76bd8b2

Browse files
committed
Unbox struct locals and optimize compound ptr assignments to not copy the pointer
Around 40% speedup on Brunsli
1 parent 8fd005f commit 76bd8b2

96 files changed

Lines changed: 996 additions & 790 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/models/boxed_vars.cpp‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,19 @@
1010
namespace cpp2rust {
1111
namespace {
1212
// Whether decl may be stored without a Value, if its address is not taken:
13-
// scalars and arrays of non-arrays.
13+
// scalars, arrays of non-arrays, and user-defined structs, unless they have a
14+
// destructor, which is called with a pointer to them.
1415
bool CanUnbox(const clang::VarDecl *decl) {
1516
auto type = decl->getType();
1617
if (type->isConstantArrayType()) {
1718
if (type->getAsArrayTypeUnsafe()->getElementType()->isArrayType()) {
1819
return false;
1920
}
21+
} else if (type->isStructureOrClassType()) {
22+
if (!IsUserDefinedDecl(type->getAsRecordDecl()) ||
23+
TypeNeedsDestruction(type)) {
24+
return false;
25+
}
2026
} else if (!type->isScalarType()) {
2127
return false;
2228
}
@@ -87,10 +93,17 @@ class AddressTakenVisitor
8793
}
8894

8995
private:
90-
// An element of an array is accessed without making a pointer to it.
96+
// An element of an array, or a field of a struct, is accessed without
97+
// making a pointer to it.
9198
void AddValueUse(clang::Expr *expr) {
9299
expr = expr->IgnoreParens();
93-
if (auto *subscript = clang::dyn_cast<clang::ArraySubscriptExpr>(expr)) {
100+
if (auto *member = clang::dyn_cast<clang::MemberExpr>(expr)) {
101+
if (!member->isArrow() &&
102+
clang::isa<clang::FieldDecl>(member->getMemberDecl())) {
103+
AddValueUse(member->getBase());
104+
}
105+
} else if (auto *subscript =
106+
clang::dyn_cast<clang::ArraySubscriptExpr>(expr)) {
94107
auto *cast =
95108
clang::dyn_cast<clang::ImplicitCastExpr>(subscript->getBase());
96109
if (cast && cast->getCastKind() == clang::CK_ArrayToPointerDecay) {

‎cpp2rust/converter/models/boxed_vars.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ namespace cpp2rust {
1515
// to a function, or captured by a lambda. Any use that doesn't just read or
1616
// write the value of a variable is taken to make a pointer to it.
1717
//
18-
// Only scalars and arrays are stored directly; other variables are always
19-
// boxed.
18+
// Only scalars, arrays and user-defined structs are stored directly; other
19+
// variables are always boxed.
2020
class BoxedVars {
2121
public:
2222
explicit BoxedVars(clang::ASTContext &ctx);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ bool ConverterRefCount::IsUnboxedVar(const clang::ValueDecl *decl) const {
197197
return var && !boxed_vars_->contains(var);
198198
}
199199

200+
bool ConverterRefCount::IsUnboxedPlace(const clang::Expr *expr) const {
201+
expr = expr->IgnoreParenImpCasts();
202+
// Fields of unions and references are accessed through pointers.
203+
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());
207+
}
208+
auto *ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
209+
return ref && IsUnboxedVar(ref->getDecl());
210+
}
211+
200212
ConverterRefCount::ConversionKind
201213
ConverterRefCount::VarConversionKind(const clang::VarDecl *decl) const {
202214
if (in_function_formals_) {
@@ -1914,7 +1926,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
19141926
return false;
19151927
}
19161928

1917-
if (isAddrOf() && IsArrayFieldPtr(ctx_, expr)) {
1929+
if (isAddrOf() && IsArrayFieldPtr(ctx_, expr) && !IsUnboxedPlace(expr)) {
19181930
StrCat(std::format("array_field_ptr!({}, {})", ConvertRecordPtr(expr),
19191931
GetNamedDeclAsString(member)));
19201932
computed_expr_type_ = ComputedExprType::FreshPointer;
@@ -1980,6 +1992,13 @@ void ConverterRefCount::ConvertInlineField(clang::MemberExpr *expr) {
19801992
if (isRValue() && !record_ptr_ &&
19811993
(TypeIsCopyable(type) || type->isPointerType() || IsUniquePtr(type) ||
19821994
expr == copied_expr_)) {
1995+
// A pointer in an unboxed struct is used in place, like a pointer
1996+
// variable, and only copied when needed.
1997+
if (type->isPointerType() && IsUnboxedPlace(expr)) {
1998+
StrCat(ReadField(expr, ""));
1999+
computed_expr_type_ = ComputedExprType::Pointer;
2000+
return;
2001+
}
19832002
StrCat(ReadField(expr));
19842003
SetFreshType(type);
19852004
return;
@@ -2034,6 +2053,9 @@ std::string ConverterRefCount::ReadField(clang::MemberExpr *expr,
20342053
if (!TypeIsCopyable(expr->getMemberDecl()->getType())) {
20352054
str += copy;
20362055
}
2056+
if (IsUnboxedPlace(expr)) {
2057+
return str;
2058+
}
20372059
return ptr.empty() ? std::format("{{ {} }}", str)
20382060
: std::format("{}.with(|__s| {})", ptr, str);
20392061
}
@@ -2567,10 +2589,20 @@ void ConverterRefCount::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
25672589
auto ptr = pending_deref_.take();
25682590
auto op = assign_operator;
25692591
op.remove_suffix(1); // remove '='
2570-
{
2571-
PushBrace brace(*this);
2592+
PushBrace brace(*this);
2593+
if (lhs->getType()->isPointerType()) {
25722594
StrCat(std::format("let _ptr = {}{};", ptr, fresh ? "" : ".clone()"));
25732595
StrCat(std::format("_ptr.write(_ptr.read() {} {})", op, rhs_as_string));
2596+
} else {
2597+
// The place is accessed once, after the assigned value is computed,
2598+
// which may access it too.
2599+
if (rhs_as_string != "__rhs" && !rhs->isEvaluatable(ctx_)) {
2600+
StrCat(keyword::kLet, "__rhs", token::kAssign, rhs_as_string,
2601+
token::kSemiColon);
2602+
rhs_as_string = "__rhs";
2603+
}
2604+
StrCat(std::format("{}.with_mut(|__v| *__v = *__v {} {})", ptr, op,
2605+
rhs_as_string));
25742606
}
25752607
} else {
25762608
StrCat(lhs_str, assign_operator, rhs_as_string);

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ class ConverterRefCount final : public Converter {
410410
// Whether decl is a local variable or a parameter that is stored directly
411411
// instead of in a Value, as no pointer to it is ever made.
412412
bool IsUnboxedVar(const clang::ValueDecl *decl) const;
413+
// Whether expr is an unboxed variable or a field of one, which is accessed
414+
// in place.
415+
bool IsUnboxedPlace(const clang::Expr *expr) const;
413416
std::shared_ptr<const BoxedVars> boxed_vars_;
414417

415418
// Set when a constructor's translation refers to `this`, which then needs

‎libcc2rs-macros/src/record.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
7272
const OFFSETS: #offsets = #offsets { #(#idents: #values,)* };
7373

7474
#[allow(unused_comparisons)]
75+
#[inline(always)]
7576
fn locate(
7677
&self,
7778
offset: usize,
@@ -85,6 +86,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
8586
}
8687

8788
#[allow(unused_comparisons)]
89+
#[inline(always)]
8890
fn locate_mut(
8991
&mut self,
9092
offset: usize,

‎tests/multi-file/cross_tu_tag_collision/out/refcount/cross_tu_tag_collision.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ pub struct widget {
1313
pub id: i32,
1414
}
1515
pub fn a_value_0() -> i32 {
16-
let w: Value<widget> = <Value<widget>>::default();
17-
(*w.borrow_mut()).id = 11;
18-
return { (*w.borrow()).id };
16+
let mut w: widget = <widget>::default();
17+
w.id = 11;
18+
return w.id;
1919
}
2020
pub fn main() {
2121
__cpp2rust_init_globals();

‎tests/multi-file/out_of_line_methods_cross_tu/out/refcount/out_of_line_methods_cross_tu.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ impl SImpl for Ptr<S> {
7777
}
7878
fn add(&self, mut x: i32) -> i32 {
7979
{
80-
let _ptr = field!((*self), v);
81-
_ptr.write(_ptr.read() + x)
80+
let __rhs = x;
81+
field!((*self), v).with_mut(|__v| *__v = *__v + __rhs)
8282
};
8383
return (*self).with(|__s| __s.v);
8484
}

‎tests/ub/out/refcount/ctor_ref_member.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ pub fn main() {
2323
std::process::exit(main_0());
2424
}
2525
fn main_0() -> i32 {
26-
let s: Value<S> = Rc::new(RefCell::new({
26+
let mut s: S = {
2727
let __tmp_0: Value<i32> = Rc::new(RefCell::new(5));
2828
S::new({ __tmp_0.as_pointer() })
29-
}));
30-
assert!((({ (*s.borrow()).r.clone() }.read()) == 5));
29+
};
30+
assert!((({ s.r.clone() }.read()) == 5));
3131
return 0;
3232
}
3333
pub fn __cpp2rust_init_globals() {}

‎tests/unit/out/refcount/10_struct.rs‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,24 @@ pub fn main() {
6161
std::process::exit(main_0());
6262
}
6363
fn main_0() -> i32 {
64-
let g: Value<Graph> = Rc::new(RefCell::new(Graph {
64+
let mut g: Graph = Graph {
6565
V: 5_u32,
6666
adj: Ptr::<Ptr<GraphNode>>::null(),
67-
}));
67+
};
6868
let arr: Value<Box<[i32]>> = Rc::new(RefCell::new(Box::new([3, 1, 4])));
69-
let it: Value<Partial> = Rc::new(RefCell::new(Partial::new_1({
70-
(arr.as_pointer() as Ptr<i32>)
71-
})));
72-
if ({ { (*it.borrow()).p.clone() } } != { (arr.as_pointer() as Ptr<i32>) }) {
69+
let mut it: Partial = Partial::new_1({ (arr.as_pointer() as Ptr<i32>) });
70+
if ({ (it.p).clone() } != { (arr.as_pointer() as Ptr<i32>) }) {
7371
return 1;
7472
}
75-
let def: Value<Partial> = Rc::new(RefCell::new(<Partial>::default()));
76-
if !(({ (*def.borrow()).p.clone() }).is_null()) {
73+
let mut def: Partial = <Partial>::default();
74+
if !((def.p).is_null()) {
7775
return 1;
7876
}
79-
let s: Value<S> = Rc::new(RefCell::new(S {
77+
let mut s: S = S {
8078
i: 7,
8179
d: Ptr::<Declared>::null(),
82-
}));
83-
if ({ (*s.borrow()).i } != 7) || (!(({ (*s.borrow()).d.clone() }).is_null())) {
80+
};
81+
if (s.i != 7) || (!((s.d).is_null())) {
8482
return 1;
8583
}
8684
return 0;

‎tests/unit/out/refcount/anonymous-struct.rs‎

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -86,42 +86,42 @@ pub fn main() {
8686
std::process::exit(main_0());
8787
}
8888
fn main_0() -> i32 {
89-
let o: Value<Outer> = Rc::new(RefCell::new(<Outer>::default()));
90-
(*o.borrow_mut()).named.a = 1;
91-
(*o.borrow_mut()).named.b = 2;
92-
(*o.borrow_mut()).anonymous_named_0.c = 3;
93-
(*o.borrow_mut()).anonymous_named_0.d = 4;
94-
(*o.borrow_mut()).anonymous_named_1.g = 5;
95-
(*o.borrow_mut()).anonymous_named_1.h = 6;
96-
(*o.borrow_mut()).anon_2.e = 7;
97-
(*o.borrow_mut()).anon_2.f = 8;
98-
(*o.borrow_mut()).anon_3.i = 9;
99-
(*o.borrow_mut()).anon_3.inner_named.j = 10;
100-
(*o.borrow_mut()).anon_3.anon_5.k = 11;
101-
assert!(({ (*o.borrow()).named.a } == 1));
102-
assert!(({ (*o.borrow()).named.b } == 2));
103-
assert!(({ (*o.borrow()).anonymous_named_0.c } == 3));
104-
assert!(({ (*o.borrow()).anonymous_named_0.d } == 4));
105-
assert!(({ (*o.borrow()).anonymous_named_1.g } == 5));
106-
assert!(({ (*o.borrow()).anonymous_named_1.h } == 6));
107-
assert!(({ (*o.borrow()).anon_2.e } == 7));
108-
assert!(({ (*o.borrow()).anon_2.f } == 8));
109-
assert!(({ (*o.borrow()).anon_3.i } == 9));
110-
assert!(({ (*o.borrow()).anon_3.inner_named.j } == 10));
111-
assert!(({ (*o.borrow()).anon_3.anon_5.k } == 11));
112-
let s: Value<anon_6> = Rc::new(RefCell::new(<anon_6>::default()));
113-
(*s.borrow_mut()).x = 1;
114-
(*s.borrow_mut()).z = 2;
89+
let mut o: Outer = <Outer>::default();
90+
o.named.a = 1;
91+
o.named.b = 2;
92+
o.anonymous_named_0.c = 3;
93+
o.anonymous_named_0.d = 4;
94+
o.anonymous_named_1.g = 5;
95+
o.anonymous_named_1.h = 6;
96+
o.anon_2.e = 7;
97+
o.anon_2.f = 8;
98+
o.anon_3.i = 9;
99+
o.anon_3.inner_named.j = 10;
100+
o.anon_3.anon_5.k = 11;
101+
assert!((o.named.a == 1));
102+
assert!((o.named.b == 2));
103+
assert!((o.anonymous_named_0.c == 3));
104+
assert!((o.anonymous_named_0.d == 4));
105+
assert!((o.anonymous_named_1.g == 5));
106+
assert!((o.anonymous_named_1.h == 6));
107+
assert!((o.anon_2.e == 7));
108+
assert!((o.anon_2.f == 8));
109+
assert!((o.anon_3.i == 9));
110+
assert!((o.anon_3.inner_named.j == 10));
111+
assert!((o.anon_3.anon_5.k == 11));
112+
let mut s: anon_6 = <anon_6>::default();
113+
s.x = 1;
114+
s.z = 2;
115115
assert!(
116116
({
117-
(*s.borrow_mut()).x = 1;
118-
{ (*s.borrow()).x }
117+
s.x = 1;
118+
s.x
119119
} != 0)
120120
);
121121
assert!(
122122
({
123-
(*s.borrow_mut()).z = 2;
124-
{ (*s.borrow()).z }
123+
s.z = 2;
124+
s.z
125125
} != 0)
126126
);
127127
return 0;

0 commit comments

Comments
 (0)