Skip to content

Commit

Permalink
Bugfixes, added more UB checks, updated README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
FractalFir committed Nov 5, 2024
1 parent cb8d6ec commit 2d57f02
Show file tree
Hide file tree
Showing 17 changed files with 240 additions and 192 deletions.
49 changes: 40 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# rustc_codegen_clr
# rustc_codegen_clr

> [!WARNING]
> This project is still early in its developement. Bugs, crashes and miscompilations are expected. DO NOT USE IT FOR ANYTHING SERIOUS.
`rustc_codegen_clr` is an experimental Rust to .NET compiler backend. It allows the Rust compiler to turn Rust code into .NET assemblies. This translation is very high-level, and preserves things like types,
field/varaible names.
`rustc_codegen_clr` is an experimental Rust compiler backend(plugin), which allows you to transpile Rust into .NET assebmlies, or C source files.

The end goal of the project is allowing Rust to be used in places where it could not be used before.

## .NET Interop layer

The project aims to provide a way to easily use Rust libraries in .NET. It comes with a Rust/.NET interop layer, which allows you to easily interact with .NET code from Rust:

```
use mychorizza::*;
fn main(){
Expand All @@ -18,7 +22,10 @@ fn main(){
mstring.AppendChar('.');
}
```
The project will also include support for defining .NET classes from Rust. This is currently heavily WIP, and any feedback is appreciated.
This should allow you to integrate Rust code with exisitng .NET codebases, and should allow you to use .NET-specific libraries or APIs from Rust.

The project will also include support for defining .NET classes from Rust, allowing .NET code to easily call Rust.
This is currently heavily WIP, and any feedback is appreciated.
```
// Early WIP syntax, subject to change.
dotnet_typedef! {
Expand All @@ -29,23 +36,47 @@ dotnet_typedef! {
}
}
```

With this approach, the classes and APIs exposed to .NET can be easily used from other .NET languages, like F# or C#. The safety of this glue layer can be checked by the Rust compiler, which should make interop issues much less likely.
## C support

While .NET is the main foccus of my work, this project can also be used to compile Rust to C, by setting the `C_MODE` enviroment flag to `1`.

This may seem like a strange and unrelated feature, but the project was written in such a way that this is not only possible, but relatively easy.

My representation of .NETs IR maps nicely to C, which means that I was able to add support for compiling Rust to C in 2-3K LOC. Almost all of the codebase is reused, with the C and .NET specific code only
present in the very last stage of compilation.

This means that, instead of having to maintain 2 separate projects, I can maintian one project. Bug fixes to the .NET side of things also fix C bugs.
Because of that, the support for C in the project is almost as good as support for .NET

## Current state of the project

The project currently supports most Rust features (except async and proc macros), but it is not bug-free. It can compile a mostly working version of Rust std, but there are many minor bugs make such `std` not 100% functional.
The project currently supports most Rust features (except proc macros), but it is not bug-free. It can compile a mostly working version of Rust std, but there are many minor bugs make such `std` not 100% functional.

Most compoenets of `std` are about 95% working.
Most components of `std` are about 95% working in .NET, and 80% working in C.

Currently, the GCC and clang C compilers are supported, with plans to add support
for `tcc`, and maybe even `sdcc`.

So, you *can* compile a lot of existing Rust code, but it may not necessarily *work*.
### core, std, and alloc uint tests.
.NET

| Name | Pass | Faliure | Crash \ Timeout| OK precentage
|--------------------|--------|-------|-------|------|
| Core tests | 1635 | 38 | 41 | 95.39% |
| Core tests | 1662 | 39 | 12 | 97.02% |
| Alloc tests | 616 |8 | 40 | 92.77% |
| Alloc benches | 464 | 0 | 0 | 100.00% |
| Test Harness tests | 57 | 0 | 100.00% |
| std tests | 955| 33 | 17 | 95.02% |
| Core benches | 490 | 2| | 98.39% |
| std tests | 931 | 43 | 64 | 89.69% |
| Core benches | 491 | 1| | 98.99% |

C

| Name | Pass | Faliure | OK precentage
|--------------------|--------|-------|------|
| Core tests | 1419 | 294 | 82.83% |
## FAQ

### Q: What is it?
Expand Down
1 change: 1 addition & 0 deletions cilly/src/bin/linker/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ fn main() {
cilly::v2::builtins::int128::i128_mul_ovf_check(&mut final_assembly, &mut overrides);
cilly::v2::builtins::f16::generate_f16_ops(&mut final_assembly, &mut overrides, *C_MODE);
cilly::v2::builtins::atomics::generate_all_atomics(&mut final_assembly, &mut overrides);
cilly::v2::builtins::stack_addr(&mut final_assembly, &mut overrides);
if *C_MODE {
cilly::v2::builtins::insert_exeception_stub(&mut final_assembly, &mut overrides);
externs.insert("__dso_handle", LIBC.clone());
Expand Down
20 changes: 12 additions & 8 deletions cilly/src/cil_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,17 @@ pub enum CILNode {
}

impl CILNode {
pub fn stack_addr(val: Self, tpe: TypeIdx) -> Self {
pub fn stack_addr(val: Self, tpe_idx: TypeIdx, asm: &mut Assembly) -> Self {
/*let main_module = *asm.main_module();
let tpe = asm[tpe_idx];
let sig = asm.sig([tpe], Type::Ptr(tpe_idx));
let mref = asm.new_methodref(main_module, "stack_addr", sig, MethodKind::Static, vec![]);
CILNode::Call(Box::new(CallOpArgs {
args: Box::new([val]),
site: mref,
}))*/
CILNode::TemporaryLocal(Box::new((
tpe,
tpe_idx,
[CILRoot::SetTMPLocal { value: val }].into(),
CILNode::LoadAddresOfTMPLocal,
)))
Expand Down Expand Up @@ -403,13 +411,9 @@ impl CILNode {
)))
}
pub fn transmute_on_stack(self, src: Type, target: Type, asm: &mut Assembly) -> Self {
let tmp_loc = Self::TemporaryLocal(Box::new((
asm.alloc_type(src),
Box::new([CILRoot::SetTMPLocal { value: self }]),
CILNode::LoadAddresOfTMPLocal,
)));
let stack_addr = Self::stack_addr(self, asm.alloc_type(src), asm);
Self::LdObj {
ptr: Box::new(CILNode::MRefToRawPtr(Box::new(tmp_loc)).cast_ptr(asm.nptr(target))),
ptr: Box::new(stack_addr.cast_ptr(asm.nptr(target))),
obj: Box::new(target),
}
}
Expand Down
14 changes: 14 additions & 0 deletions cilly/src/v2/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,20 @@ fn insert_catch_unwind(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) {
}
const ALLOC_CAP: u64 = u32::MAX as u64;
pub(crate) const UNMANAGED_THREAD_START: &str = "UnmanagedThreadStart";
/// THIS BUILTIN MUST ALWAYS BE INLINED!
pub fn stack_addr(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) {
let name = asm.alloc_string("stack_addr");
let generator = move |_, asm: &mut Assembly| {
let addr = asm.alloc_node(CILNode::LdArgA(0));
let ptr = asm.alloc_node(CILNode::RefToPtr(addr));
let ret = asm.alloc_root(CILRoot::Ret(ptr));
MethodImpl::MethodBody {
blocks: vec![BasicBlock::new(vec![ret], 0, None)],
locals: vec![],
}
};
patcher.insert(name, Box::new(generator));
}
pub fn argc_argv_init(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) {
let name = asm.alloc_string("argc_argv_init");
let generator = move |_, asm: &mut Assembly| {
Expand Down
2 changes: 1 addition & 1 deletion cilly/src/v2/c_exporter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ impl Exporter for CExporter {
.arg("-o")
.arg(exe_out)
.arg("-g")
.args(["-fsanitize=undefined","-fno-sanitize-recover"])
.args(["-fsanitize=undefined,address,alignment","-fno-sanitize=leak","-fno-sanitize-recover"])
.arg("-Ofast")
// .arg("-FOLD") saves up on space, consider enabling.
;
Expand Down
5 changes: 4 additions & 1 deletion cilly/src/v2/cilnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,10 @@ impl CILNode {
V1Node::LDFtn(method_ref) => Self::LdFtn(*method_ref),
V1Node::Volatile(inner) => {
let mut tmp = Self::from_v1(inner, asm);
if let Self::LdInd { volatile: volitale, .. } = &mut tmp {
if let Self::LdInd {
volatile: volitale, ..
} = &mut tmp
{
*volitale = true;
} else {
panic!()
Expand Down
2 changes: 1 addition & 1 deletion cilly/src/v2/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ impl ClassRef {
asm.alloc_class_ref(ClassRef::new(name, asm_name, true, [].into()))
}
#[must_use]
pub fn fixed_array(element: Type, length: usize, asm: &mut Assembly) -> ClassRefIdx {
pub fn fixed_array(element: Type, length: u64, asm: &mut Assembly) -> ClassRefIdx {
let name = format!("{element}_{length}", element = element.mangle(asm));
let name = asm.alloc_string(name);
let cref = ClassRef::new(name, None, true, [].into());
Expand Down
1 change: 1 addition & 0 deletions cilly/src/v2/il_exporter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ impl ILExporter {
name: &str,
sig: SigIdx,
) -> std::io::Result<()> {
//assert_ne!(name,"stack_addr", "The builtin 'stack_addr' cilly function must always be inlined, and can't be exported otherwise.");
match mimpl{
MethodImpl::MethodBody { blocks, locals } => {
let locals_string:String = locals.iter().map(|(name,tpe)|match name {
Expand Down
2 changes: 1 addition & 1 deletion src/aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub fn handle_aggregate<'tcx>(
}
let element = ctx.monomorphize(*element);
let element = ctx.type_from_cache(element);
let array_type = ClassRef::fixed_array(element, value_index.len(), ctx);
let array_type = ClassRef::fixed_array(element, value_index.len() as u64, ctx);
let array_getter = super::place::place_adress(target_location, ctx);
let sig = FnSig::new(
Box::new([ctx.nref(array_type), Type::Int(Int::USize), element]),
Expand Down
11 changes: 11 additions & 0 deletions src/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,17 @@ pub fn add_allocation(alloc_id: u64, asm: &mut cilly::v2::Assembly, tcx: TyCtxt<

CILNode::LDStaticField(Box::new(field_desc))
}
/*
pub fn alloc_buff_tpe(asm: &mut cilly::v2::Assembly, len: u64) -> Option<Type> {
match len {
0 => None,
1 => Some(Type::Int(Int::U8)),
2 => Some(Type::Int(Int::U16)),
4 => Some(Type::Int(Int::U32)),
8 => Some(Type::Int(Int::U64)),
_ => array_type(),
}
}*/
pub fn add_const_value(asm: &mut cilly::v2::Assembly, bytes: u128) -> StaticFieldDesc {
let uint8_ptr = Type::Int(Int::U128);
let main_module_id = asm.main_module();
Expand Down
40 changes: 7 additions & 33 deletions src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,41 +66,14 @@ pub(crate) fn load_const_value<'tcx>(
CILNode::uninit_val(tpe, ctx)
}
ConstValue::Slice { data, meta } => {
let slice_type = get_type(const_ty, ctx);
let slice_type = ctx.type_from_cache(const_ty);
let slice_dotnet = slice_type.as_class_ref().expect("Slice type invalid!");
let metadata_field = FieldDesc::new(
slice_dotnet,
ctx.alloc_string(crate::METADATA),
cilly::v2::Type::Int(Int::USize),
);
let ptr_field = FieldDesc::new(
slice_dotnet,
ctx.alloc_string(crate::DATA_PTR),
ctx.nptr(cilly::v2::Type::Void),
);
// TODO: find a better way to get an alloc_id. This is likely to be incoreect.

let alloc_id = ctx.tcx().reserve_and_set_memory_alloc(data);
let alloc_id: u64 = crate::utilis::alloc_id_to_u64(alloc_id);
let slice_type = ctx.type_from_cache(const_ty);
CILNode::TemporaryLocal(Box::new((
ctx.alloc_type(slice_type),
[
CILRoot::SetField {
addr: Box::new(CILNode::LoadAddresOfTMPLocal),
value: Box::new(CILNode::V2(ctx.alloc_node(Const::USize(meta)))),
desc: ctx.alloc_field(metadata_field),
},
CILRoot::SetField {
addr: Box::new(CILNode::LoadAddresOfTMPLocal),
value: Box::new(
CILNode::LoadGlobalAllocPtr { alloc_id }.cast_ptr(ctx.nptr(Type::Void)),
),
desc: ctx.alloc_field(ptr_field),
},
]
.into(),
CILNode::LoadTMPLocal,
)))
let meta = CILNode::V2(ctx.alloc_node(Const::USize(meta)));
let ptr = CILNode::LoadGlobalAllocPtr { alloc_id }.cast_ptr(ctx.nptr(Type::Void));
CILNode::create_slice(slice_dotnet, ctx, meta, ptr)
}
ConstValue::Indirect { alloc_id, offset } => {
create_const_from_data(const_ty, alloc_id, offset.bytes(), ctx)
Expand Down Expand Up @@ -152,6 +125,7 @@ fn load_scalar_ptr(
site: ctx.alloc_methodref(mref),
})),
ctx.alloc_type(u8_ptr_ptr),
ctx,
);
}
let attrs = ctx.tcx().codegen_fn_attrs(def_id);
Expand All @@ -160,7 +134,7 @@ fn load_scalar_ptr(
// TODO: this could cause issues if the pointer to the static is not imediatly dereferenced.
let site = get_fn_from_static_name(&name, ctx);
let ptr_sig = Type::FnPtr(ctx[site].sig());
return CILNode::stack_addr(CILNode::LDFtn(site), ctx.alloc_type(ptr_sig));
return CILNode::stack_addr(CILNode::LDFtn(site), ctx.alloc_type(ptr_sig), ctx);
}
if let Some(section) = attrs.link_section {
panic!("static {name} requires special linkage in section {section:?}");
Expand Down
2 changes: 1 addition & 1 deletion src/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub(crate) fn operand_address<'tcx>(
Operand::Constant(const_val) => {
let local_type = ctx.type_from_cache(operand.ty(ctx.body(), ctx.tcx()));
let constant = crate::constant::handle_constant(const_val, ctx);
let ptr = CILNode::stack_addr(constant, ctx.alloc_type(local_type));
let ptr = CILNode::stack_addr(constant, ctx.alloc_type(local_type), ctx);
crate::place::deref_op(
crate::place::PlaceTy::Ty(operand.ty(ctx.body(), ctx.tcx())),
ctx,
Expand Down
2 changes: 1 addition & 1 deletion src/place/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ fn body_field<'a>(
CILNode::stack_addr(CILNode::transmute_on_stack(CILNode::LdObj {
ptr: Box::new(parrent_node),
obj: Box::new(curr_type),
}, curr_type, field_type, ctx),ctx.alloc_type(field_type))
}, curr_type, field_type, ctx),ctx.alloc_type(field_type), ctx)
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub fn handle_rvalue<'tcx>(
dst,
) => (vec![], ptr_to_ptr(ctx, operand, *dst)),
Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize, _), operand, target) => {
(vec![], crate::unsize::unsize2(ctx, operand, *target))
crate::unsize::unsize2(ctx, operand, *target, *target_location)
}
Rvalue::BinaryOp(binop, operands) => (
vec![],
Expand Down
Loading

0 comments on commit 2d57f02

Please sign in to comment.