Skip to content

Commit a5a0368

Browse files
committed
Added new atomics + misc bugfixes
1 parent b6551ea commit a5a0368

File tree

15 files changed

+931
-59
lines changed

15 files changed

+931
-59
lines changed

cilly/src/bin/linker/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ fn main() {
323323
)
324324
}),
325325
);
326+
cilly::v2::builtins::atomics::generate_all_atomics(&mut final_assembly, &mut overrides);
326327
final_assembly.patch_missing_methods(externs, modifies_errno, overrides);
327328

328329
add_mandatory_statics(&mut final_assembly);

cilly/src/v2/builtins/atomics.rs

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use crate::v2::{
2+
asm::MissingMethodPatcher, cilnode::MethodKind, cilroot::BranchCond, BasicBlock, BinOp,
3+
CILNode, CILRoot, ClassRef, Int, MethodDef, MethodImpl, MethodRef, Type,
4+
};
5+
6+
use super::{
7+
super::{Assembly, NodeIdx},
8+
math::{int_max, int_min},
9+
};
10+
11+
pub fn generate_atomic(
12+
asm: &mut Assembly,
13+
patcher: &mut MissingMethodPatcher,
14+
op_name: &str,
15+
op: impl Fn(&mut Assembly, NodeIdx, NodeIdx, Int) -> NodeIdx + 'static,
16+
int: Int,
17+
) {
18+
let name = asm.alloc_string(format!("atomic_{op_name}_{int}", int = int.name()));
19+
let generator = move |_, asm: &mut Assembly| {
20+
// Common ops
21+
let ldloc_0 = asm.alloc_node(CILNode::LdLoc(0));
22+
let ldloc_1 = asm.alloc_node(CILNode::LdLoc(1));
23+
let ldarg_0 = asm.alloc_node(CILNode::LdArg(0));
24+
let ldarg_1 = asm.alloc_node(CILNode::LdArg(1));
25+
// Types for which this atomic is implemented
26+
27+
// The OP of this atomic
28+
let op = op(asm, ldloc_0, ldarg_1, int);
29+
30+
let tpe = Type::Int(int);
31+
let tref = asm.nref(tpe);
32+
33+
let cmpxchng_sig = asm.sig([tref, tpe, tpe], tpe);
34+
let interlocked = ClassRef::interlocked(asm);
35+
let interlocked = asm.alloc_class_ref(interlocked);
36+
let compare_exchange = asm.alloc_string("CompareExchange");
37+
let mref = asm.alloc_methodref(MethodRef::new(
38+
interlocked,
39+
compare_exchange,
40+
cmpxchng_sig,
41+
MethodKind::Static,
42+
vec![].into(),
43+
));
44+
let call = asm.alloc_node(CILNode::Call(Box::new((
45+
mref,
46+
Box::new([ldarg_0, op, ldloc_0]),
47+
))));
48+
49+
let loop_block = vec![
50+
asm.alloc_root(CILRoot::StLoc(0, ldloc_1)),
51+
asm.alloc_root(CILRoot::StLoc(1, call)),
52+
asm.alloc_root(CILRoot::Branch(Box::new((
53+
0,
54+
0,
55+
Some(BranchCond::Ne(ldloc_0, ldloc_1)),
56+
)))),
57+
asm.alloc_root(CILRoot::Branch(Box::new((1, 0, None)))),
58+
];
59+
let exit_block = vec![asm.alloc_root(CILRoot::Ret(ldloc_0))];
60+
MethodImpl::MethodBody {
61+
blocks: vec![
62+
BasicBlock::new(loop_block, 0, None),
63+
BasicBlock::new(exit_block, 1, None),
64+
],
65+
locals: vec![(None, asm.alloc_type(tpe)), (None, asm.alloc_type(tpe))],
66+
}
67+
};
68+
patcher.insert(name, Box::new(generator));
69+
}
70+
pub fn generate_atomic_for_ints(
71+
asm: &mut Assembly,
72+
patcher: &mut MissingMethodPatcher,
73+
op_name: &str,
74+
op: impl Fn(&mut Assembly, NodeIdx, NodeIdx, Int) -> NodeIdx + 'static + Clone,
75+
) {
76+
const ATOMIC_INTS: [Int; 6] = [
77+
Int::U32,
78+
Int::U64,
79+
Int::USize,
80+
Int::I32,
81+
Int::I64,
82+
Int::ISize,
83+
];
84+
for int in ATOMIC_INTS {
85+
generate_atomic(asm, patcher, op_name, op.clone(), int)
86+
}
87+
}
88+
pub fn generate_all_atomics(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) {
89+
// XOR
90+
generate_atomic_for_ints(asm, patcher, "xor", |asm, lhs, rhs, _| {
91+
asm.alloc_node(CILNode::BinOp(lhs, rhs, BinOp::XOr))
92+
});
93+
// NAND
94+
generate_atomic_for_ints(asm, patcher, "nand", |asm, lhs, rhs, _| {
95+
let and = asm.alloc_node(CILNode::BinOp(lhs, rhs, BinOp::And));
96+
asm.alloc_node(CILNode::UnOp(and, crate::v2::cilnode::UnOp::Not))
97+
});
98+
// Max
99+
generate_atomic_for_ints(asm, patcher, "max", int_max);
100+
// Max
101+
generate_atomic_for_ints(asm, patcher, "min", int_min)
102+
}
103+
/*
104+
.method public hidebysig static
105+
uint32 atomic_xor (
106+
uint32& addr,
107+
uint32 xorand
108+
) cil managed
109+
{
110+
// Method begins at RVA 0x2050
111+
// Code size 25 (0x19)
112+
.maxstack 3
113+
.locals (
114+
[0] uint32 addr_val,
115+
[1] uint32 got
116+
)
117+
118+
119+
// loop start (head: IL_0013)
120+
IL_0006: ldloc.1
121+
IL_0007: stloc.0
122+
123+
IL_0008: ldarg.0
124+
IL_0009: ldloc.0
125+
IL_000a: ldarg.1
126+
IL_000b: xor
127+
IL_000c: ldloc.0
128+
IL_000d: call uint32 [System.Threading]System.Threading.Interlocked::CompareExchange(uint32&, uint32, uint32)
129+
IL_0012: stloc.1
130+
131+
IL_0013: ldloc.0
132+
IL_0014: ldloc.1
133+
IL_0015: bne.un.s IL_0006
134+
// end loop
135+
IL_0017: ldloc.0
136+
IL_0018: ret
137+
} // end of method Tmp::atomic_xor
138+
139+
*/

cilly/src/v2/builtins/math.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::v2::{cilnode::MethodKind, Assembly, CILNode, ClassRef, Int, MethodRef, NodeIdx, Type};
2+
3+
pub fn int_max(asm: &mut Assembly, lhs: NodeIdx, rhs: NodeIdx, int: Int) -> NodeIdx {
4+
let math = ClassRef::math(asm);
5+
let math = asm.alloc_class_ref(math);
6+
let max = asm.alloc_string("Max");
7+
let sig = asm.sig([Type::Int(int), Type::Int(int)], Type::Int(int));
8+
let mref = asm.alloc_methodref(MethodRef::new(
9+
math,
10+
max,
11+
sig,
12+
MethodKind::Static,
13+
vec![].into(),
14+
));
15+
asm.alloc_node(CILNode::Call(Box::new((mref, Box::new([lhs, rhs])))))
16+
}
17+
18+
pub fn int_min(asm: &mut Assembly, lhs: NodeIdx, rhs: NodeIdx, int: Int) -> NodeIdx {
19+
let math = ClassRef::math(asm);
20+
let math = asm.alloc_class_ref(math);
21+
let max = asm.alloc_string("Min");
22+
let sig = asm.sig([Type::Int(int), Type::Int(int)], Type::Int(int));
23+
let mref = asm.alloc_methodref(MethodRef::new(
24+
math,
25+
max,
26+
sig,
27+
MethodKind::Static,
28+
vec![].into(),
29+
));
30+
asm.alloc_node(CILNode::Call(Box::new((mref, Box::new([lhs, rhs])))))
31+
}

cilly/src/v2/builtins/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod atomics;
2+
pub mod math;

cilly/src/v2/cilnode.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -429,23 +429,23 @@ impl CILNode {
429429
CILNode::IntCast {
430430
input: asm.alloc_node(node),
431431
target: Int::I8,
432-
extend: ExtendKind::ZeroExtend,
432+
extend: ExtendKind::SignExtend,
433433
}
434434
}
435435
V1Node::ConvI16(inner) => {
436436
let node = Self::from_v1(inner, asm);
437437
CILNode::IntCast {
438438
input: asm.alloc_node(node),
439439
target: Int::I16,
440-
extend: ExtendKind::ZeroExtend,
440+
extend: ExtendKind::SignExtend,
441441
}
442442
}
443443
V1Node::ConvI32(inner) => {
444444
let node = Self::from_v1(inner, asm);
445445
CILNode::IntCast {
446446
input: asm.alloc_node(node),
447447
target: Int::I32,
448-
extend: ExtendKind::ZeroExtend,
448+
extend: ExtendKind::SignExtend,
449449
}
450450
}
451451
V1Node::ConvF32(inner) => {

cilly/src/v2/class.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ impl ClassRef {
5858
generics,
5959
}
6060
}
61-
61+
pub fn interlocked(asm: &mut super::Assembly) -> Self {
62+
let name = asm.alloc_string("System.Threading.Interlocked");
63+
let asm = Some(asm.alloc_string("System.Threading"));
64+
Self::new(name, asm, false, vec![].into())
65+
}
6266
pub fn from_v1(dotnet_type: &V1ClassRef, asm: &mut super::Assembly) -> ClassRef {
6367
match dotnet_type {
6468
V1ClassRef::Full {
@@ -92,6 +96,12 @@ impl ClassRef {
9296
pub fn generics(&self) -> &[Type] {
9397
&self.generics
9498
}
99+
/// The .NET math class
100+
pub fn math(asm: &mut Assembly) -> Self {
101+
let name = asm.alloc_string("System.Math");
102+
let asm = Some(asm.alloc_string("System.Runtime"));
103+
Self::new(name, asm, false, vec![].into())
104+
}
95105
}
96106
#[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
97107
pub struct ClassDef {

cilly/src/v2/il_exporter/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -738,12 +738,12 @@ impl ILExporter {
738738
self.export_node(asm, out, *a)?;
739739
self.export_node(asm, out, *b)?;
740740
if branch.1 == 0 {
741-
writeln!(out, "bne bb{}", branch.0)
741+
writeln!(out, "bne.un bb{}", branch.0)
742742
} else if is_handler {
743-
writeln!(out, "bne h{}_{}", branch.0, branch.1)
743+
writeln!(out, "bne.un h{}_{}", branch.0, branch.1)
744744
}
745745
else {
746-
writeln!(out, "bne jp{}_{}", branch.0, branch.1)
746+
writeln!(out, "bne.un jp{}_{}", branch.0, branch.1)
747747
}
748748
}
749749
Some(BranchCond::Lt(a, b, kind)) => {

cilly/src/v2/int.rs

+18
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,21 @@ impl From<Int> for Type {
2222
Self::Int(value)
2323
}
2424
}
25+
impl Int {
26+
pub fn name(&self) -> &'static str {
27+
match self {
28+
Int::U8 => "u8",
29+
Int::U16 => "u16",
30+
Int::U32 => "u32",
31+
Int::U64 => "u64",
32+
Int::U128 => "u128",
33+
Int::USize => "usize",
34+
Int::I8 => "i8",
35+
Int::I16 => "i16",
36+
Int::I32 => "i32",
37+
Int::I64 => "i64",
38+
Int::I128 => "i128",
39+
Int::ISize => "isize",
40+
}
41+
}
42+
}

cilly/src/v2/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub mod asm;
2222
pub mod asm_link;
2323
pub mod basic_block;
2424
pub mod bimap;
25+
pub mod builtins;
2526
pub mod c_exporter;
2627
pub mod cilnode;
2728
pub mod cilroot;

src/binop/checked/mod.rs

+38-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{assembly::MethodCompileCtx, casts};
22
use cilly::{
3-
and, call, call_site::CallSite, cil_node::CILNode, cil_root::CILRoot, conv_i64, conv_isize,
4-
conv_u64, conv_usize, field_desc::FieldDescriptor, gt, gt_un, ldc_i32, ldc_i64, ldc_u32,
5-
ldc_u64, lt, mul, or, size_of, DotnetTypeRef, FnSig, Type,
3+
and, call, call_site::CallSite, cil_node::CILNode, cil_root::CILRoot, conv_i16, conv_i32,
4+
conv_i64, conv_i8, conv_isize, conv_u64, conv_usize, field_desc::FieldDescriptor, gt, gt_un,
5+
ldc_i32, ldc_i64, ldc_u32, ldc_u64, lt, mul, or, size_of, DotnetTypeRef, FnSig, Type,
66
};
77
use rustc_middle::ty::{IntTy, Ty, TyKind, UintTy};
88

@@ -118,8 +118,8 @@ fn max(ty: Ty) -> CILNode {
118118
TyKind::Uint(UintTy::U8) => CILNode::LdcU8(u8::MAX),
119119
TyKind::Uint(UintTy::U16) => CILNode::LdcU16(u16::MAX),
120120
TyKind::Uint(UintTy::U32) => ldc_u32!(u32::MAX),
121-
TyKind::Int(IntTy::I8) => CILNode::LdcI8(i8::MIN),
122-
TyKind::Int(IntTy::I16) => CILNode::LdcI16(i16::MIN),
121+
TyKind::Int(IntTy::I8) => CILNode::LdcI8(i8::MAX),
122+
TyKind::Int(IntTy::I16) => CILNode::LdcI16(i16::MAX),
123123
TyKind::Int(IntTy::I32) => ldc_i32!(i32::MAX),
124124
TyKind::Uint(UintTy::U64) => ldc_u64!(u64::MAX),
125125
TyKind::Int(IntTy::I64) => ldc_i64!(i64::MAX),
@@ -399,6 +399,39 @@ pub fn add_signed<'tcx>(
399399
ctx: &mut MethodCompileCtx<'tcx, '_, '_>,
400400
) -> CILNode {
401401
let tpe = ctx.type_from_cache(ty);
402+
match ty.kind() {
403+
TyKind::Int(IntTy::I8) => {
404+
let sum = conv_i16!(ops_a.clone()) + conv_i16!(ops_b.clone());
405+
return result_tuple(
406+
tpe,
407+
or!(
408+
lt!(sum.clone(), conv_i16!(ldc_i32!(i8::MIN.into()))),
409+
gt!(sum.clone(), conv_i16!(ldc_i32!(i8::MAX.into())))
410+
),
411+
conv_i8!(sum),
412+
);
413+
}
414+
TyKind::Int(IntTy::I16) => {
415+
let sum = conv_i32!(ops_a.clone()) + conv_i32!(ops_b.clone());
416+
return result_tuple(
417+
tpe,
418+
or!(
419+
lt!(sum.clone(), (ldc_i32!(i16::MIN.into()))),
420+
gt!(sum.clone(), (ldc_i32!(i16::MAX.into())))
421+
),
422+
conv_i16!(sum),
423+
);
424+
}
425+
TyKind::Int(IntTy::I32) => {
426+
let sum = conv_i64!(ops_a.clone()) + conv_i64!(ops_b.clone());
427+
let out_of_range = or!(
428+
lt!(sum.clone(), conv_i64!(ldc_i32!(i32::MIN))),
429+
gt!(sum.clone(), conv_i64!(ldc_i32!(i32::MAX.into())))
430+
);
431+
return result_tuple(tpe, out_of_range, conv_i32!(sum));
432+
}
433+
_ => (),
434+
}
402435
let res = super::add_unchecked(ty, ty, ctx, ops_a.clone(), ops_b.clone());
403436
result_tuple(
404437
tpe,

0 commit comments

Comments
 (0)