-
Notifications
You must be signed in to change notification settings - Fork 277
transpile
: Support unaligned reads and writes
#1257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1641,10 +1641,29 @@ class TranslateASTVisitor final | |
|
||
bool VisitUnaryOperator(UnaryOperator *UO) { | ||
std::vector<void *> childIds = {UO->getSubExpr()}; | ||
encode_entry(UO, TagUnaryOperator, childIds, [UO](CborEncoder *array) { | ||
|
||
encode_entry(UO, TagUnaryOperator, childIds, [this, UO](CborEncoder *array) { | ||
cbor_encode_string(array, UO->getOpcodeStr(UO->getOpcode()).str()); | ||
cbor_encode_boolean(array, UO->isPrefix()); | ||
|
||
if (UO->getOpcode() == UO_Deref) { | ||
QualType eltTy = UO->getSubExpr()->getType()->getPointeeType(); | ||
CharUnits align = Context->getTypeAlignInChars(eltTy); | ||
|
||
if (auto *TD = eltTy->getAs<TypedefType>()) { | ||
QualType naturalTy = TD->getDecl()->getUnderlyingType(); | ||
CharUnits naturalAlign = Context->getPreferredTypeAlignInChars(naturalTy); | ||
|
||
if (align < naturalAlign) { | ||
cbor_encode_int(array, align.getQuantity()); | ||
} else { | ||
cbor_encode_int(array, -1); | ||
} | ||
|
||
} | ||
} | ||
}); | ||
|
||
Comment on lines
+1645
to
+1666
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have LLM'd my way to a solution here, this is probably terrible c++ code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not super familiar with libclang either. Looks like it might work? I can look into it more later. |
||
return true; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1230,28 +1230,28 @@ pub enum CastKind { | |
/// Represents a unary operator in C (6.5.3 Unary operators) and GNU C extensions | ||
#[derive(Debug, Clone, Copy)] | ||
pub enum UnOp { | ||
AddressOf, // &x | ||
Deref, // *x | ||
Plus, // +x | ||
PostIncrement, // x++ | ||
PreIncrement, // ++x | ||
Negate, // -x | ||
PostDecrement, // x-- | ||
PreDecrement, // --x | ||
Complement, // ~x | ||
Not, // !x | ||
Real, // [GNU C] __real x | ||
Imag, // [GNU C] __imag x | ||
Extension, // [GNU C] __extension__ x | ||
Coawait, // [C++ Coroutines] co_await x | ||
AddressOf, // &x | ||
Deref { unaligned: bool }, // *x | ||
Plus, // +x | ||
PostIncrement, // x++ | ||
PreIncrement, // ++x | ||
Negate, // -x | ||
PostDecrement, // x-- | ||
PreDecrement, // --x | ||
Complement, // ~x | ||
Not, // !x | ||
Real, // [GNU C] __real x | ||
Imag, // [GNU C] __imag x | ||
Extension, // [GNU C] __extension__ x | ||
Coawait, // [C++ Coroutines] co_await x | ||
Comment on lines
+1233
to
+1246
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formatting here is annoying 😩. Could you add another commit that just makes all of the same-line |
||
} | ||
|
||
impl UnOp { | ||
pub fn as_str(&self) -> &'static str { | ||
use UnOp::*; | ||
match self { | ||
AddressOf => "&", | ||
Deref => "*", | ||
Deref { .. } => "*", | ||
Plus => "+", | ||
PreIncrement => "++", | ||
PostIncrement => "++", | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -283,6 +283,11 @@ impl<'c> Translation<'c> { | |||||
.get_qual_type() | ||||||
.ok_or_else(|| format_err!("bad initial lhs type"))?; | ||||||
|
||||||
let is_unaligned = matches!( | ||||||
initial_lhs, | ||||||
CExprKind::Unary(_, c_ast::UnOp::Deref { unaligned: true }, _, _) | ||||||
); | ||||||
|
||||||
let bitfield_id = match initial_lhs { | ||||||
CExprKind::Member(_, _, decl_id, _, _) => { | ||||||
let kind = &self.ast_context[*decl_id].kind; | ||||||
|
@@ -357,7 +362,17 @@ impl<'c> Translation<'c> { | |||||
use c_ast::BinOp::*; | ||||||
let assign_stmt = match op { | ||||||
// Regular (possibly volatile) assignment | ||||||
Assign if !is_volatile => WithStmts::new_val(mk().assign_expr(write, rhs)), | ||||||
Assign if !is_volatile => { | ||||||
if is_unaligned { | ||||||
WithStmts::new_val(self.unaligned_write( | ||||||
write, | ||||||
initial_lhs_type_id, | ||||||
rhs, | ||||||
)?) | ||||||
} else { | ||||||
WithStmts::new_val(mk().assign_expr(write, rhs)) | ||||||
} | ||||||
} | ||||||
Assign => WithStmts::new_val(self.volatile_write( | ||||||
write, | ||||||
initial_lhs_type_id, | ||||||
|
@@ -822,7 +837,7 @@ impl<'c> Translation<'c> { | |||||
|
||||||
match arg_kind { | ||||||
// C99 6.5.3.2 para 4 | ||||||
CExprKind::Unary(_, c_ast::UnOp::Deref, target, _) => { | ||||||
CExprKind::Unary(_, c_ast::UnOp::Deref { unaligned: _ }, target, _) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
If we don't care about the alignment, could it just be this? Like you did for the other |
||||||
return self.convert_expr(ctx, *target) | ||||||
} | ||||||
// An AddrOf DeclRef/Member is safe to not decay if the translator isn't already giving a hard | ||||||
|
@@ -889,7 +904,7 @@ impl<'c> Translation<'c> { | |||||
c_ast::UnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg), | ||||||
c_ast::UnOp::PostIncrement => self.convert_post_increment(ctx, cqual_type, true, arg), | ||||||
c_ast::UnOp::PostDecrement => self.convert_post_increment(ctx, cqual_type, false, arg), | ||||||
c_ast::UnOp::Deref => { | ||||||
c_ast::UnOp::Deref { unaligned } => { | ||||||
match self.ast_context[arg].kind { | ||||||
CExprKind::Unary(_, c_ast::UnOp::AddressOf, arg_, _) => { | ||||||
self.convert_expr(ctx.used(), arg_) | ||||||
|
@@ -903,7 +918,23 @@ impl<'c> Translation<'c> { | |||||
Ok(unwrap_function_pointer(val)) | ||||||
} else if let Some(_vla) = self.compute_size_of_expr(ctype) { | ||||||
Ok(val) | ||||||
} else if unaligned { | ||||||
// We should use read_unaligned here: | ||||||
// mk().method_call_expr(val, "read_unaligned", vec![]); | ||||||
// but that interferes with `write_unaligned` | ||||||
|
||||||
let mut val = | ||||||
mk().unary_expr(UnOp::Deref(Default::default()), val); | ||||||
|
||||||
// If the type on the other side of the pointer we are dereferencing is volatile and | ||||||
// this whole expression is not an LValue, we should make this a volatile read | ||||||
if lrvalue.is_rvalue() && cqual_type.qualifiers.is_volatile | ||||||
{ | ||||||
val = self.volatile_read(val, cqual_type)? | ||||||
} | ||||||
Ok(val) | ||||||
Comment on lines
+921
to
+935
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by the value of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. adding to my confusion, this will not actually emit a uint32_t volatile_read(volatile void* ptr) {
return *((volatile unaligned_uint32*)ptr);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm yeah this just seems wrong https://c.godbolt.org/z/sPcMM4b1T. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, that's a bug, right? I can open an issue for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the volatile code here just for trying to figure out how to emit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, nevermind, it's already existing code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, that is what makes this code confusing: I don't think it can ever be an Also in general that means we don't have great way of distinguishing whether a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also there are already a bunch of open issues wrt volatile reads and writes. They just don't actually currently work as far as I can tell. |
||||||
} else { | ||||||
dbg!("otherwise"); | ||||||
let mut val = | ||||||
mk().unary_expr(UnOp::Deref(Default::default()), val); | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#include <stdint.h> | ||
|
||
typedef __attribute__((aligned(1))) uint32_t unaligned_uint32; | ||
|
||
uint32_t unaligned_read(const void* ptr) { | ||
return *((const unaligned_uint32*)ptr); | ||
} | ||
|
||
uint32_t aligned_read(const void* ptr) { | ||
return *((const uint32_t*)ptr); | ||
} | ||
|
||
void unaligned_write(void* ptr, uint32_t value) { | ||
*((unaligned_uint32*)ptr) = value; | ||
} | ||
|
||
void aligned_write(const void* ptr, uint32_t value) { | ||
*((uint32_t*)ptr) = value; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
--- | ||
source: c2rust-transpile/tests/snapshots.rs | ||
expression: "&rust" | ||
input_file: c2rust-transpile/tests/snapshots/aligned_read_write.c | ||
--- | ||
#![allow( | ||
dead_code, | ||
mutable_transmutes, | ||
non_camel_case_types, | ||
non_snake_case, | ||
non_upper_case_globals, | ||
unused_assignments, | ||
unused_mut | ||
)] | ||
pub type __uint32_t = std::ffi::c_uint; | ||
pub type uint32_t = __uint32_t; | ||
pub type unaligned_uint32 = uint32_t; | ||
#[no_mangle] | ||
pub unsafe extern "C" fn unaligned_read(mut ptr: *const std::ffi::c_void) -> uint32_t { | ||
return *(ptr as *const unaligned_uint32); | ||
} | ||
Comment on lines
+19
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so this is still wrong. I can make this one work, but then the unaligned write below is no longer correct. |
||
#[no_mangle] | ||
pub unsafe extern "C" fn aligned_read(mut ptr: *const std::ffi::c_void) -> uint32_t { | ||
return *(ptr as *const uint32_t); | ||
} | ||
#[no_mangle] | ||
pub unsafe extern "C" fn unaligned_write( | ||
mut ptr: *mut std::ffi::c_void, | ||
mut value: uint32_t, | ||
) { | ||
(ptr as *mut unaligned_uint32).write_unaligned(value); | ||
} | ||
#[no_mangle] | ||
pub unsafe extern "C" fn aligned_write( | ||
mut ptr: *const std::ffi::c_void, | ||
mut value: uint32_t, | ||
) { | ||
*(ptr as *mut uint32_t) = value; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overalignment can be useful to handle as well, so maybe just
cbor_encode_uint
both the actual and natural alignment?