Skip to content

Commit fc0e1ff

Browse files
committed
Add offset!(ptr, idx) macro to create temporary pointers without ref counting overhead
1 parent a4a4b78 commit fc0e1ff

128 files changed

Lines changed: 1762 additions & 1884 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_lib.cpp‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,16 @@ GetAllVars(const clang::Stmt *stmt) {
13381338
return vars;
13391339
}
13401340

1341+
bool ReadsMemory(const clang::Stmt *stmt) {
1342+
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(stmt);
1343+
cast && cast->getCastKind() == clang::CK_LValueToRValue) {
1344+
return true;
1345+
}
1346+
return std::ranges::any_of(stmt->children(), [](const clang::Stmt *child) {
1347+
return child && ReadsMemory(child);
1348+
});
1349+
}
1350+
13411351
bool ReferencesThis(const clang::Stmt *stmt) {
13421352
if (!stmt) {
13431353
return false;

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ GetAllVars(const clang::Stmt *stmt);
242242

243243
bool ReferencesThis(const clang::Stmt *stmt);
244244

245+
// Whether evaluating `stmt` reads a value from memory, e.g., a pointer.
246+
bool ReadsMemory(const clang::Stmt *stmt);
247+
245248
bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs);
246249

247250
bool ArgsMayAlias(const clang::Expr *a, const clang::Expr *b);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,12 +1822,16 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
18221822
return false;
18231823
}
18241824

1825+
// A pointer to a Value field is made without copying the Value out.
1826+
bool value_field_ptr = isAddrOf() && !known &&
1827+
clang::isa<clang::FieldDecl>(member) &&
1828+
!member->getType()->isReferenceType();
18251829
std::string str;
18261830
if (known) {
18271831
str = GetMappedAsString(expr);
18281832
} else if (clang::isa<clang::FieldDecl>(member)) {
18291833
// The Value or the pointer that the field holds.
1830-
str = ReadField(expr);
1834+
str = ReadField(expr, value_field_ptr ? ".as_pointer()" : ".clone()");
18311835
} else {
18321836
Buffer buf(*this);
18331837
PushExprKind push(*this, ExprKind::RValue);
@@ -1840,7 +1844,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
18401844
if (member->getType()->isReferenceType()) {
18411845
computed_expr_type_ = ComputedExprType::Pointer;
18421846
} else {
1843-
StrCat(".as_pointer()");
1847+
StrCat(value_field_ptr ? "" : ".as_pointer()");
18441848
computed_expr_type_ = ComputedExprType::FreshPointer;
18451849
}
18461850
return false;
@@ -1913,7 +1917,8 @@ std::string ConverterRefCount::ConvertRecordPtr(clang::MemberExpr *expr) {
19131917
return std::move(buf).str();
19141918
}
19151919

1916-
std::string ConverterRefCount::ReadField(clang::MemberExpr *expr) {
1920+
std::string ConverterRefCount::ReadField(clang::MemberExpr *expr,
1921+
std::string_view copy) {
19171922
// Unless the struct is reached through a pointer, in which case the field
19181923
// is read in a closure, the borrow of the struct ends at the end of the
19191924
// block.
@@ -1928,7 +1933,7 @@ std::string ConverterRefCount::ReadField(clang::MemberExpr *expr) {
19281933
str = std::move(buf).str();
19291934
}
19301935
if (!TypeIsCopyable(expr->getMemberDecl()->getType())) {
1931-
str += ".clone()";
1936+
str += copy;
19321937
}
19331938
return ptr.empty() ? std::format("{{ {} }}", str)
19341939
: std::format("{}.with(|__s| {})", ptr, str);
@@ -2444,7 +2449,10 @@ void ConverterRefCount::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
24442449

24452450
PushBrace brace(*this, isRValue());
24462451

2447-
if (MayCauseBorrowMutError(lhs, rhs)) {
2452+
// C++ evaluates the assigned value first, which may modify what the place
2453+
// is computed from, like a pointer it is reached through.
2454+
if (MayCauseBorrowMutError(lhs, rhs) ||
2455+
(rhs->HasSideEffects(ctx_) && ReadsMemory(lhs))) {
24482456
if (CanBraceAssignedValue(lhs, rhs, assign_operator)) {
24492457
rhs_as_string = std::format("{{ {} }}", rhs_as_string);
24502458
} else {
@@ -2607,7 +2615,7 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
26072615
if (isLValue()) {
26082616
PushConversionKind push_ck(*this, ConversionKind::Unboxed);
26092617
pending_deref_.set(
2610-
std::format("({} as {}).offset({})",
2618+
std::format("elem!(({} as {}), {})",
26112619
ConvertObject(expr->getArg(0), ObjectShape::Element),
26122620
ConvertPtrType(expr->getArg(0)->getType()),
26132621
ConvertSubscriptIndex(expr->getArg(1))),
@@ -2627,10 +2635,12 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
26272635
}
26282636

26292637
PushConversionKind push(*this, ConversionKind::Unboxed);
2630-
StrCat(std::format("({} as {}).offset({})",
2631-
ConvertObject(expr->getArg(0), ObjectShape::Element),
2632-
ConvertPtrType(expr->getArg(0)->getType()),
2633-
ConvertSubscriptIndex(expr->getArg(1))));
2638+
auto ptr = std::format(
2639+
"({} as {})", ConvertObject(expr->getArg(0), ObjectShape::Element),
2640+
ConvertPtrType(expr->getArg(0)->getType()));
2641+
auto idx = ConvertSubscriptIndex(expr->getArg(1));
2642+
StrCat(deref && !is_inner_boxed ? std::format("elem!({}, {})", ptr, idx)
2643+
: std::format("{}.offset({})", ptr, idx));
26342644

26352645
if (is_inner_boxed) {
26362646
StrCat(GetPointerDerefSuffix(expr->getType()), ".as_pointer()");
@@ -2757,6 +2767,12 @@ void ConverterRefCount::ConvertArraySubscript(clang::Expr *base,
27572767
}
27582768
}
27592769

2770+
void ConverterRefCount::ConvertPointerElem(clang::Expr *base,
2771+
clang::Expr *idx) {
2772+
StrCat(std::format("elem!({}, {})", ToString(base), ConvertRValue(idx)));
2773+
computed_expr_type_ = ComputedExprType::FreshPointer;
2774+
}
2775+
27602776
void ConverterRefCount::ConvertPointerSubscript(
27612777
clang::ArraySubscriptExpr *expr) {
27622778
auto *base = expr->getBase();
@@ -2765,7 +2781,7 @@ void ConverterRefCount::ConvertPointerSubscript(
27652781
if (isLValue()) {
27662782
pending_deref_.assert_consumed();
27672783
Buffer buf(*this);
2768-
ConvertPointerOffset(base, idx);
2784+
ConvertPointerElem(base, idx);
27692785
pending_deref_.set_unchecked(std::move(buf).str(), isFresh(), expr);
27702786
return;
27712787
}
@@ -2774,8 +2790,10 @@ void ConverterRefCount::ConvertPointerSubscript(
27742790
PushParen paren(*this, deref);
27752791
if (deref) {
27762792
StrCat(GetPointerDerefPrefix(expr->getType()));
2793+
ConvertPointerElem(base, idx);
2794+
} else {
2795+
ConvertPointerOffset(base, idx);
27772796
}
2778-
ConvertPointerOffset(base, idx);
27792797
if (deref) {
27802798
StrCat(GetPointerDerefSuffix(expr->getType()));
27812799
SetValueFreshness(expr->getType());

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ class ConverterRefCount final : public Converter {
155155

156156
// Copies the value of the field `expr` out of its struct, such that the
157157
// struct doesn't stay borrowed.
158-
std::string ReadField(clang::MemberExpr *expr);
158+
std::string ReadField(clang::MemberExpr *expr,
159+
std::string_view copy = ".clone()");
159160

160161
// A field that is read through a pointer to its struct is read in a
161162
// closure, `p.with(|__s| __s.x)`. While converting the struct whose field
@@ -387,6 +388,9 @@ class ConverterRefCount final : public Converter {
387388

388389
std::string ConvertSubscriptIndex(clang::Expr *idx);
389390

391+
// Element `idx` of pointer `base`, accessed without creating a pointer to it.
392+
void ConvertPointerElem(clang::Expr *base, clang::Expr *idx);
393+
390394
std::string GetSafeTypeAsString(clang::QualType qual_type) const;
391395

392396
bool NeedsMut(const clang::VarDecl *decl, clang::QualType type,

‎libcc2rs/src/field.rs‎

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::any::{Any, TypeId};
1717
use std::cell::{Cell, Ref, RefCell, RefMut};
1818
use std::rc::{Rc, Weak};
1919

20-
use crate::rc::{AsPointer, Ptr, PtrKind, Value, null_deref};
20+
use crate::rc::{AsPointer, Ptr, PtrKind, StrongPtr, Value, null_deref};
2121
use crate::reinterpret::ByteRepr;
2222

2323
// A struct whose fields can be pointed to. Implemented by #[derive(Record)],
@@ -326,6 +326,79 @@ macro_rules! field {
326326
};
327327
}
328328

329+
// Element `idx` of the sequence that pointer `base` points into, which is
330+
// accessed through `base` without creating a pointer to it as
331+
// `base.offset(idx)` does. Written by `elem!(p, i)`.
332+
pub struct ElemPlace<'a, T> {
333+
pub base: &'a Ptr<T>,
334+
pub idx: isize,
335+
}
336+
337+
impl<T> Clone for ElemPlace<'_, T> {
338+
#[inline]
339+
fn clone(&self) -> Self {
340+
*self
341+
}
342+
}
343+
344+
impl<T> Copy for ElemPlace<'_, T> {}
345+
346+
impl<T: ByteRepr> ElemPlace<'_, T> {
347+
#[inline]
348+
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
349+
self.base.with_at(self.idx, f)
350+
}
351+
352+
#[inline]
353+
pub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
354+
self.base.with_mut_at(self.idx, f)
355+
}
356+
357+
#[inline]
358+
pub fn read(&self) -> T
359+
where
360+
T: Clone,
361+
{
362+
self.base.read_at(self.idx)
363+
}
364+
365+
#[inline]
366+
pub fn write(&self, value: T) {
367+
self.base.write_at(self.idx, value)
368+
}
369+
370+
#[inline]
371+
pub fn upgrade(&self) -> StrongPtr<T> {
372+
self.base.offset(self.idx).upgrade()
373+
}
374+
}
375+
376+
impl<T: ByteRepr> Place for ElemPlace<'_, T> {
377+
type Target = T;
378+
#[inline]
379+
fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
380+
ElemPlace::with(self, f)
381+
}
382+
#[inline]
383+
fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
384+
ElemPlace::with_mut(self, f)
385+
}
386+
}
387+
388+
// Element `idx` of the sequence that pointer `base` points into, e.g.,
389+
// `elem!(p, i).write(1)` for `p[i] = 1`. It is a struct expression, like
390+
// field!, so that a temporary base lives as long as the element when bound to
391+
// a variable.
392+
#[macro_export]
393+
macro_rules! elem {
394+
($base:expr, $idx:expr) => {
395+
$crate::ElemPlace {
396+
base: &$base,
397+
idx: ($idx) as isize,
398+
}
399+
};
400+
}
401+
329402
// Finds the object at a byte offset of a field for Record::locate. The
330403
// implementation is chosen based on the type of the field, by autoref-based
331404
// specialization: `(&&Locate(&field)).locate(...)` picks LocateRecord if the

‎libcc2rs/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod rc;
1010
pub use rc::*;
1111

1212
mod field;
13-
pub use field::{FieldPlace, FieldPtr, Place, Record};
13+
pub use field::{ElemPlace, FieldPlace, FieldPtr, Place, Record};
1414
// Used by #[derive(Record)] and size_of_field!.
1515
#[doc(hidden)]
1616
pub mod __field {

0 commit comments

Comments
 (0)