Skip to content

Commit 52509a7

Browse files
committed
Support for some basic atomic operations
1 parent 7189c8e commit 52509a7

File tree

13 files changed

+316
-83
lines changed

13 files changed

+316
-83
lines changed

cargo_tests/build_std/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ fn main() {
107107
std::hint::black_box(&string);
108108
unsafe{puts(string.as_ptr())};
109109
unsafe{puts("Testing some cool shit\n\0".as_ptr())};
110-
let mut f = std::fs::File::create("foo.txt").unwrap();
110+
//let mut f = std::fs::File::create("foo.txt").unwrap();
111111

112-
std::hint::black_box(f);
112+
//std::hint::black_box(f);
113113
std::io::stdout().write_all(b"hello world\n").unwrap();
114114
let s = format!("Hello {}\0",8);
115115
unsafe{puts(s.as_ptr())};

src/aggregate.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ fn aggregate_adt<'tyctx>(
232232
let field_type =
233233
type_cache.type_from_cache(field_type, tyctx, Some(method_instance));
234234
// Seting a void field is a no-op.
235-
if field_type == Type::Void{
235+
if field_type == Type::Void {
236236
continue;
237237
}
238238
ops.extend(obj_getter.iter().cloned());
@@ -290,7 +290,7 @@ fn aggregate_adt<'tyctx>(
290290
Some(method_instance),
291291
);
292292
// Seting a void field is a no-op.
293-
if field_type == Type::Void{
293+
if field_type == Type::Void {
294294
continue;
295295
}
296296
ops.extend(variant_address.clone());
@@ -332,7 +332,6 @@ fn aggregate_adt<'tyctx>(
332332
);
333333
let mut ops: Vec<CILOp> = Vec::with_capacity(fields.len() * 2);
334334
for field in fields {
335-
336335
let field_def = adt
337336
.all_fields()
338337
.nth(field.0 as usize)
@@ -342,7 +341,7 @@ fn aggregate_adt<'tyctx>(
342341
let field_type =
343342
type_cache.type_from_cache(field_type, tyctx, Some(method_instance));
344343
// Seting a void field is a no-op.
345-
if field_type == Type::Void{
344+
if field_type == Type::Void {
346345
continue;
347346
}
348347
ops.extend(obj_getter.iter().cloned());

src/assembly.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ impl Assembly {
304304
match &block_data.terminator {
305305
Some(term) => {
306306
if crate::INSERT_MIR_DEBUG_COMMENTS {
307-
rustc_middle::ty::print::with_no_trimmed_paths! {ops.push(CILOp::Comment(format!("{term:?}").into()))};
307+
//rustc_middle::ty::print::with_no_trimmed_paths! {ops.push(CILOp::Comment(format!("{term:?}").into()))};
308308
}
309309
let term_ops = Self::terminator_to_ops(term, mir, tcx, instance, cache);
310310
if term_ops != [CILOp::Ret] {

src/assembly_exporter/ilasm_op.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ pub fn type_cil(tpe: &Type) -> Cow<'static, str> {
417417
Type::ISize => "native int".into(),
418418
Type::USize => "native uint".into(),
419419
Type::Ptr(inner) => format!("{inner}*", inner = type_cil(inner)).into(),
420+
Type::ManagedReference(inner) => format!("{inner}&", inner = type_cil(inner)).into(),
420421
Type::DotnetType(dotnet_type) => dotnet_type_ref_cli(dotnet_type).into(),
421422
//Special type
422423
Type::Unresolved => "valuetype Unresolved".into(),

src/ffi/atomics.rs

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
use crate::{
2+
assembly::Assembly,
3+
cil::{CILOp, CallSite, FieldDescriptor},
4+
function_sig::FnSig,
5+
method::Method,
6+
r#type::{DotnetTypeRef, Type},
7+
};
8+
//TODO: propely mimic the semantics used in atomics!
9+
pub fn add_atomics(asm: &mut Assembly) {
10+
add_atomic_load_acquire(asm);
11+
add_atomic_cxchgweak_acquire_acquire(asm);
12+
}
13+
fn add_atomic_load_acquire(asm: &mut Assembly) {
14+
let atomic_load_acquire_calls: Box<[_]> = asm
15+
.call_sites()
16+
.filter(|call_site| {
17+
call_site.signature().inputs().len() == 1
18+
&& matches!(call_site.signature().inputs()[0], Type::Ptr(_))
19+
&& call_site.name().contains("atomic_load_acquire")
20+
})
21+
.cloned()
22+
.collect();
23+
for call in atomic_load_acquire_calls.iter() {
24+
let mut method = Method::new(
25+
crate::access_modifier::AccessModifer::Private,
26+
true,
27+
call.signature().clone(),
28+
call.name(),
29+
vec![],
30+
);
31+
match call.signature().output() {
32+
//Those can't be implemented using System.Threading.Interlocked, since there is no Read(Uintptr).
33+
Type::Ptr(_) => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndISize, CILOp::Ret]),
34+
Type::ISize => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndISize, CILOp::Ret]),
35+
Type::USize => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndISize, CILOp::Ret]),
36+
//Those can be implemented using System.Threading.Interlocked.
37+
Type::I64 => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndI64, CILOp::Ret]),
38+
Type::U64 => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndI64, CILOp::Ret]),
39+
//Those can't be implemented using System.Threading.Interlocked.
40+
Type::U8 => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndI8, CILOp::Ret]),
41+
Type::I8 => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndI8, CILOp::Ret]),
42+
Type::U32 => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndI32, CILOp::Ret]),
43+
Type::I32 => method.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndI32, CILOp::Ret]),
44+
_ => {
45+
eprintln!(
46+
"Warning: `atomic_load_acquire` used with unsuported type({:?})!",
47+
call.signature().output()
48+
);
49+
continue;
50+
}
51+
}
52+
asm.add_method(method);
53+
}
54+
}
55+
fn interlocked() -> DotnetTypeRef {
56+
DotnetTypeRef::new(Some("System.Threading"), "System.Threading.Interlocked")
57+
.with_valuetype(false)
58+
}
59+
60+
fn add_atomic_cxchgweak_acquire_acquire(asm: &mut Assembly) {
61+
let atomic_cxchgweak_acquire_acquire_calls: Box<[_]> = asm
62+
.call_sites()
63+
.filter(|call_site| {
64+
call_site.signature().inputs().len() == 3
65+
&& call_site
66+
.name()
67+
.contains("atomic_cxchgweak_acquire_acquire")
68+
})
69+
.cloned()
70+
.collect();
71+
for call in atomic_cxchgweak_acquire_acquire_calls.iter() {
72+
let ret_tuple = call.signature().output();
73+
let ret_tuple_dotnet = ret_tuple.as_dotnet().expect("atomic_cxchgweak_acquire_acquire: return tuple invalid!");
74+
let mut method = Method::new(
75+
crate::access_modifier::AccessModifer::Private,
76+
true,
77+
call.signature().clone(),
78+
call.name(),
79+
vec![(None, call.signature().inputs()[1].clone()),(Some("return_tuple".into()),ret_tuple.clone())],
80+
);
81+
match call.signature().inputs()[1]{
82+
//Those can't be implemented using System.Threading.Interlocked, since there is no Read(Uintptr).
83+
Type::USize | Type::ISize | Type::Ptr(_) => {
84+
let call_sig = FnSig::new(
85+
&[
86+
Type::ManagedReference(Type::USize.into()),
87+
Type::USize,
88+
Type::USize,
89+
],
90+
&Type::USize,
91+
);
92+
method.set_ops(vec![
93+
94+
CILOp::LDArg(0),
95+
CILOp::LDArg(1),
96+
CILOp::LDArg(2),
97+
CILOp::Call(
98+
CallSite::new(
99+
Some(interlocked()),
100+
"CompareExchange".into(),
101+
call_sig,
102+
true,
103+
)
104+
.into(),
105+
),
106+
CILOp::STLoc(0),
107+
CILOp::LDLocA(1),
108+
CILOp::LDLoc(0),
109+
CILOp::STField(Box::new(FieldDescriptor::new(ret_tuple_dotnet.clone(), call.signature().inputs()[1].clone(),"Item1".into()))),
110+
CILOp::LDLocA(1),
111+
CILOp::LDLoc(0),
112+
CILOp::LDArg(1),
113+
CILOp::Eq,
114+
CILOp::Not,
115+
CILOp::STField(Box::new(FieldDescriptor::new(ret_tuple_dotnet,Type::Bool,"Item2".into()))),
116+
CILOp::LDLoc(1),
117+
CILOp::Ret,
118+
]);
119+
}
120+
Type::U32 | Type::I32 => {
121+
let call_sig = FnSig::new(
122+
&[
123+
Type::ManagedReference(Type::I32.into()),
124+
Type::I32,
125+
Type::I32,
126+
],
127+
&Type::I32,
128+
);
129+
method.set_ops(vec![
130+
131+
CILOp::LDArg(0),
132+
CILOp::LDArg(1),
133+
CILOp::LDArg(2),
134+
CILOp::Call(
135+
CallSite::new(
136+
Some(interlocked()),
137+
"CompareExchange".into(),
138+
call_sig,
139+
true,
140+
)
141+
.into(),
142+
),
143+
CILOp::STLoc(0),
144+
CILOp::LDLocA(1),
145+
CILOp::LDLoc(0),
146+
CILOp::STField(Box::new(FieldDescriptor::new(ret_tuple_dotnet.clone(), call.signature().inputs()[1].clone(),"Item1".into()))),
147+
CILOp::LDLocA(1),
148+
CILOp::LDLoc(0),
149+
CILOp::LDArg(1),
150+
CILOp::Eq,
151+
CILOp::Not,
152+
CILOp::STField(Box::new(FieldDescriptor::new(ret_tuple_dotnet,Type::Bool,"Item2".into()))),
153+
CILOp::LDLoc(1),
154+
CILOp::Ret,
155+
]);
156+
}
157+
Type::U64 | Type::I64 => {
158+
let call_sig = FnSig::new(
159+
&[
160+
Type::ManagedReference(Type::U64.into()),
161+
Type::U64,
162+
Type::U64,
163+
],
164+
&Type::U64,
165+
);
166+
method.set_ops(vec![
167+
168+
CILOp::LDArg(0),
169+
CILOp::LDArg(1),
170+
CILOp::LDArg(2),
171+
CILOp::Call(
172+
CallSite::new(
173+
Some(interlocked()),
174+
"CompareExchange".into(),
175+
call_sig,
176+
true,
177+
)
178+
.into(),
179+
),
180+
CILOp::STLoc(0),
181+
CILOp::LDLocA(1),
182+
CILOp::LDLoc(0),
183+
CILOp::STField(Box::new(FieldDescriptor::new(ret_tuple_dotnet.clone(), call.signature().inputs()[1].clone(),"Item1".into()))),
184+
CILOp::LDLocA(1),
185+
CILOp::LDLoc(0),
186+
CILOp::LDArg(1),
187+
CILOp::Eq,
188+
CILOp::Not,
189+
CILOp::STField(Box::new(FieldDescriptor::new(ret_tuple_dotnet,Type::Bool,"Item2".into()))),
190+
CILOp::LDLoc(1),
191+
CILOp::Ret,
192+
]);
193+
}
194+
//Type::ISize=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndISize,CILOp::Ret]),
195+
// Type::USize=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndISize,CILOp::Ret]),
196+
//Those can be implemented using System.Threading.Interlocked.
197+
//Type::I64=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndI64,CILOp::Ret]),
198+
// Type::U64=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndI64,CILOp::Ret]),
199+
//Those can't be implemented using System.Threading.Interlocked.
200+
//Type::U8=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndI8,CILOp::Ret]),
201+
// Type::I8=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndI8,CILOp::Ret]),
202+
//Type::U32=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndI32,CILOp::Ret]),
203+
//Type::I32=> method.set_ops(vec![CILOp::LDArg(0),CILOp::LDIndI32,CILOp::Ret]),
204+
_ => {
205+
eprintln!(
206+
"Warning: `atomic_cxchgweak_acquire_acquire` used with unsuported type({:?})!",
207+
call.signature().inputs()[1]
208+
);
209+
continue;
210+
}
211+
}
212+
asm.add_method(method);
213+
}
214+
}
215+
//TODO: implement cmp by using System.Threading.Interlocked.CompareExchange<T>(T, T, T)

src/ffi/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod atomics;
12
use crate::r#type::DotnetTypeRef;
23
use crate::{
34
access_modifier::AccessModifer,
@@ -36,7 +37,6 @@ macro_rules! add_method {
3637
}
3738
};
3839
}
39-
4040
/// Inserts a small subset of libc and some standard types into an assembly.
4141
pub fn insert_ffi_functions(asm: &mut Assembly, tyctx: TyCtxt) {
4242
let c_void = Type::c_void(tyctx);
@@ -169,6 +169,7 @@ pub fn insert_ffi_functions(asm: &mut Assembly, tyctx: TyCtxt) {
169169
CILOp::Ret,
170170
]);
171171
asm.add_method(free);
172+
//TODO: add volatile prefix to volatile loads
172173
let mut volatile_load = Method::new(
173174
AccessModifer::Private,
174175
true,
@@ -187,6 +188,9 @@ pub fn insert_ffi_functions(asm: &mut Assembly, tyctx: TyCtxt) {
187188
);
188189
volatile_load.set_ops(vec![CILOp::LDArg(0), CILOp::LDIndISize, CILOp::Ret]);
189190
asm.add_method(volatile_load);
191+
192+
atomics::add_atomics(asm);
193+
190194
abort(asm);
191195
}
192196

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ extern crate stable_mir;
6262
// Debug config
6363

6464
/// Tells the codegen to insert comments containing the MIR statemtens after each one of them.
65-
const INSERT_MIR_DEBUG_COMMENTS: bool = false;
65+
const INSERT_MIR_DEBUG_COMMENTS: bool = true;
6666
/// Prints local types of all compiled MIR functions.
6767
const PRINT_LOCAL_TYPES: bool = false;
6868
/// Tells the codegen to optmize the emiited CIL.

src/method.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,17 @@ impl Method {
5454
}
5555
}
5656
pub(crate) fn ensure_valid(&mut self) {
57-
if let Some(CILOp::Ret) = self.ops.iter().last() {
58-
//Do nothing
59-
} else {
60-
self.ops.push(CILOp::Ret);
57+
let last = self.ops.iter().last();
58+
let last = match last {
59+
Some(last) => last,
60+
None => return,
61+
};
62+
match last{
63+
CILOp::Ret=>(),
64+
CILOp::Throw=>(),
65+
CILOp::Rethrow=>(),
66+
CILOp::GoTo(_)=>(),
67+
_=>self.ops.extend(CILOp::throw_msg("Critical error: reached the end of a function not termianted with a return statement")),
6168
}
6269
}
6370
/// Adds a local variable of type `local`

src/place/mod.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// FIXME: This file may contain unnecesary morphize calls.
22

33
use crate::cil::CILOp;
4-
use crate::r#type::{DotnetTypeRef, Type, pointer_to_is_fat};
4+
use crate::r#type::{pointer_to_is_fat, DotnetTypeRef, Type};
55

66
use rustc_middle::mir::Place;
77

@@ -128,24 +128,28 @@ pub fn deref_op<'ctx>(
128128
type_cache.type_from_cache(derefed_type, tyctx, Some(*method_instance));
129129
vec![CILOp::LdObj(derefed_type.into())]
130130
}
131-
TyKind::Ref(_, inner, _) => if pointer_to_is_fat(*inner,tyctx,Some(*method_instance)) {
132-
vec![CILOp::LdObj(
133-
type_cache
134-
.type_from_cache(derefed_type, tyctx, Some(*method_instance))
135-
.into(),
136-
)]
137-
}else{
138-
vec![CILOp::LDIndISize]
139-
},
140-
TyKind::RawPtr(type_and_mut) => if pointer_to_is_fat(type_and_mut.ty,tyctx,Some(*method_instance)) {
141-
vec![CILOp::LdObj(
142-
type_cache
143-
.type_from_cache(derefed_type, tyctx, Some(*method_instance))
144-
.into(),
145-
)]
146-
}else{
147-
vec![CILOp::LDIndISize]
148-
},
131+
TyKind::Ref(_, inner, _) => {
132+
if pointer_to_is_fat(*inner, tyctx, Some(*method_instance)) {
133+
vec![CILOp::LdObj(
134+
type_cache
135+
.type_from_cache(derefed_type, tyctx, Some(*method_instance))
136+
.into(),
137+
)]
138+
} else {
139+
vec![CILOp::LDIndISize]
140+
}
141+
}
142+
TyKind::RawPtr(type_and_mut) => {
143+
if pointer_to_is_fat(type_and_mut.ty, tyctx, Some(*method_instance)) {
144+
vec![CILOp::LdObj(
145+
type_cache
146+
.type_from_cache(derefed_type, tyctx, Some(*method_instance))
147+
.into(),
148+
)]
149+
} else {
150+
vec![CILOp::LDIndISize]
151+
}
152+
}
149153
TyKind::Array(_, _) => {
150154
let derefed_type =
151155
type_cache.type_from_cache(derefed_type, tyctx, Some(*method_instance));

0 commit comments

Comments
 (0)