Skip to content

Commit b48802c

Browse files
authored
Avoid hoisting for function call arguments that don't alias (#203)
In terms of converter, the PR is small. I moved GetAllVars and MayCauseBorrowMutError from converter to converter_lib because they are converter-agnostic. Then I added the aliasing logic in CollectCallInfo 2 functions in converter_lib: ReferencesThis and ArgsMayAlias. The core idea is: if an argument of a function call does not alias with any other argument, then it can be inlined instead of hoisted. This includes aliases over this, for example `this.method(this.member)`
1 parent f41beca commit b48802c

199 files changed

Lines changed: 1298 additions & 3543 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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,24 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
16811681
}
16821682
}
16831683

1684+
// Inline arguments that don't alias
1685+
clang::Expr *receiver = GetCallObject(expr);
1686+
for (auto &ca : info.args) {
1687+
if (ca.kind != Kind::Hoisted) {
1688+
continue;
1689+
}
1690+
bool aliases = receiver && ArgsMayAlias(ca.expr, receiver);
1691+
for (const auto &other : info.args) {
1692+
if (&other != &ca && ArgsMayAlias(ca.expr, other.expr)) {
1693+
aliases = true;
1694+
break;
1695+
}
1696+
}
1697+
if (!aliases) {
1698+
ca.kind = Kind::Inline;
1699+
}
1700+
}
1701+
16841702
return info;
16851703
}
16861704

cpp2rust/converter/converter_lib.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <cctype>
1414
#include <filesystem>
1515
#include <format>
16+
#include <ranges>
1617
#include <unordered_set>
1718

1819
#include "converter/lex.h"
@@ -643,6 +644,78 @@ clang::Expr *GetCallObject(clang::CallExpr *expr) {
643644
return nullptr;
644645
}
645646

647+
static void GetAllVarsImpl(const clang::Stmt *stmt,
648+
std::unordered_set<const clang::ValueDecl *> &vars) {
649+
if (!stmt) {
650+
return;
651+
}
652+
653+
if (auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(stmt)) {
654+
vars.insert(decl_ref->getDecl());
655+
} else if (auto *member = clang::dyn_cast<clang::MemberExpr>(stmt)) {
656+
vars.insert(member->getMemberDecl());
657+
GetAllVarsImpl(member->getBase(), vars);
658+
}
659+
660+
for (auto *child : stmt->children()) {
661+
GetAllVarsImpl(child, vars);
662+
}
663+
}
664+
665+
std::unordered_set<const clang::ValueDecl *>
666+
GetAllVars(const clang::Stmt *stmt) {
667+
std::unordered_set<const clang::ValueDecl *> vars;
668+
GetAllVarsImpl(stmt, vars);
669+
return vars;
670+
}
671+
672+
bool ReferencesThis(const clang::Stmt *stmt) {
673+
if (!stmt) {
674+
return false;
675+
}
676+
if (clang::isa<clang::CXXThisExpr>(stmt)) {
677+
return true;
678+
}
679+
for (auto *child : stmt->children()) {
680+
if (ReferencesThis(child)) {
681+
return true;
682+
}
683+
}
684+
return false;
685+
}
686+
687+
bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs) {
688+
auto lhs_vars = GetAllVars(lhs);
689+
auto rhs_vars = GetAllVars(rhs);
690+
691+
auto predicate = [lhs](auto *var) {
692+
auto qual_type = var->getType();
693+
return (qual_type->isPointerType() || qual_type->isReferenceType()) &&
694+
qual_type->getPointeeType()
695+
.getCanonicalType()
696+
.getUnqualifiedType() ==
697+
lhs->getType().getCanonicalType().getUnqualifiedType();
698+
};
699+
700+
if (std::ranges::any_of(rhs_vars, predicate) ||
701+
(std::ranges::any_of(lhs_vars, predicate) && !rhs_vars.empty())) {
702+
return true;
703+
}
704+
705+
for (auto *lhs_var : lhs_vars) {
706+
if (rhs_vars.count(lhs_var))
707+
return true;
708+
}
709+
return false;
710+
}
711+
712+
bool ArgsMayAlias(const clang::Expr *a, const clang::Expr *b) {
713+
if (ReferencesThis(a) && ReferencesThis(b)) {
714+
return true;
715+
}
716+
return MayCauseBorrowMutError(a, b) || MayCauseBorrowMutError(b, a);
717+
}
718+
646719
std::vector<clang::Expr *>
647720
BuildUnifiedArgs(clang::Expr *expr, clang::Expr **args, unsigned num_args) {
648721
std::vector<clang::Expr *> all_args;

cpp2rust/converter/converter_lib.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <optional>
1313
#include <string>
1414
#include <string_view>
15+
#include <unordered_set>
1516
#include <vector>
1617

1718
#include "logging.h"
@@ -139,6 +140,15 @@ void ForEachTemplateArgument(
139140

140141
clang::Expr *GetCallObject(clang::CallExpr *expr);
141142

143+
std::unordered_set<const clang::ValueDecl *>
144+
GetAllVars(const clang::Stmt *stmt);
145+
146+
bool ReferencesThis(const clang::Stmt *stmt);
147+
148+
bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs);
149+
150+
bool ArgsMayAlias(const clang::Expr *a, const clang::Expr *b);
151+
142152
clang::Expr *GetCalleeOrExpr(clang::Expr *expr);
143153

144154
bool HasReceiver(clang::Expr *expr);

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,51 +1826,6 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type,
18261826
}
18271827
}
18281828

1829-
static std::unordered_set<const clang::ValueDecl *>
1830-
GetAllVars(const clang::Stmt *stmt) {
1831-
std::unordered_set<const clang::ValueDecl *> vars;
1832-
1833-
if (auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(stmt)) {
1834-
vars.insert(decl_ref->getDecl());
1835-
} else if (auto *member = clang::dyn_cast<clang::MemberExpr>(stmt)) {
1836-
vars.insert(member->getMemberDecl());
1837-
auto child_vars = GetAllVars(member->getBase());
1838-
vars.insert(child_vars.begin(), child_vars.end());
1839-
}
1840-
1841-
for (auto *child : stmt->children()) {
1842-
auto child_vars = GetAllVars(child);
1843-
vars.insert(child_vars.begin(), child_vars.end());
1844-
}
1845-
return vars;
1846-
}
1847-
1848-
bool ConverterRefCount::MayCauseBorrowMutError(const clang::Expr *lhs,
1849-
const clang::Expr *rhs) {
1850-
auto lhs_vars = GetAllVars(lhs);
1851-
auto rhs_vars = GetAllVars(rhs);
1852-
1853-
auto predicate = [lhs](auto *var) {
1854-
auto qual_type = var->getType();
1855-
return (qual_type->isPointerType() || qual_type->isReferenceType()) &&
1856-
qual_type->getPointeeType()
1857-
.getCanonicalType()
1858-
.getUnqualifiedType() ==
1859-
lhs->getType().getCanonicalType().getUnqualifiedType();
1860-
};
1861-
1862-
if (std::ranges::any_of(rhs_vars, predicate) ||
1863-
(std::ranges::any_of(lhs_vars, predicate) && !rhs_vars.empty())) {
1864-
return true;
1865-
}
1866-
1867-
for (auto *lhs_var : lhs_vars) {
1868-
if (rhs_vars.count(lhs_var))
1869-
return true;
1870-
}
1871-
return false;
1872-
}
1873-
18741829
void ConverterRefCount::EmitSetOrAssign(clang::Expr *lhs,
18751830
std::string_view rhs) {
18761831
auto lhs_str = ConvertLValue(lhs);

cpp2rust/converter/models/converter_refcount.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,6 @@ class ConverterRefCount final : public Converter {
142142
std::vector<const char *>
143143
GetStructAttributes(const clang::RecordDecl *decl) override;
144144

145-
bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs);
146-
147145
bool Convert(clang::QualType qual_type) override;
148146
bool
149147
Convert(clang::Expr *expr,

tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ fn main_0() -> i32 {
2020
p: Rc::new(RefCell::new(Ptr::<opaque>::null())),
2121
x: Rc::new(RefCell::new(42)),
2222
}));
23-
({
24-
let _c: Ptr<container> = (c.as_pointer());
25-
touch_0(_c)
26-
});
23+
({ touch_0((c.as_pointer())) });
2724
assert!(((((*(*c.borrow()).x.borrow()) == 42) as i32) != 0));
2825
assert!(((((*(*c.borrow()).p.borrow()).is_null()) as i32) != 0));
2926
return 0;

tests/multi-file/opaque_forward_decl/out/unsafe/opaque_forward_decl.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ unsafe fn main_0() -> i32 {
2222
p: std::ptr::null_mut(),
2323
x: 42,
2424
};
25-
(unsafe {
26-
let _c: *mut container = (&mut c as *mut container);
27-
touch_0(_c)
28-
});
25+
(unsafe { touch_0((&mut c as *mut container)) });
2926
assert!(((((c.x) == (42)) as i32) != 0));
3027
assert!(((((c.p).is_null()) as i32) != 0));
3128
return 0;

tests/ub/out/refcount/dangling-prvalue-as-lvalue.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ pub fn main() {
1414
}
1515
fn main_0() -> i32 {
1616
let v: Value<Vec<i32>> = Rc::new(RefCell::new(vec![1, 2]));
17-
let b: Ptr<i32> = ({
18-
let _a: Ptr<i32> = (v.as_pointer() as Ptr<i32>);
19-
foo_0(_a)
20-
});
17+
let b: Ptr<i32> = ({ foo_0((v.as_pointer() as Ptr<i32>)) });
2118
(*v.borrow_mut()).clear();
2219
return (b.read());
2320
}

tests/ub/out/refcount/ub12.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ pub fn main() {
1515
}
1616
fn main_0() -> i32 {
1717
let alloc: Value<Ptr<i32>> = Rc::new(RefCell::new(Ptr::alloc(1)));
18-
({
19-
let _ptr: Ptr<i32> = (*alloc.borrow()).clone();
20-
escape_0(_ptr)
21-
});
18+
({ escape_0((*alloc.borrow()).clone()) });
2219
(*alloc.borrow()).delete();
2320
return 0;
2421
}

tests/ub/out/refcount/ub13.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ pub fn main() {
1515
}
1616
fn main_0() -> i32 {
1717
let p1: Value<Ptr<i32>> = Rc::new(RefCell::new(Ptr::alloc(1)));
18-
({
19-
let _p: Ptr<i32> = (*p1.borrow()).clone();
20-
escape_0(_p)
21-
});
18+
({ escape_0((*p1.borrow()).clone()) });
2219
return ((*p1.borrow()).read());
2320
}

0 commit comments

Comments
 (0)