Skip to content

Commit 69e6e5a

Browse files
committed
Fixed the runtime config and added some new debugging tools.
1 parent ef0f5f8 commit 69e6e5a

File tree

7 files changed

+209
-100
lines changed

7 files changed

+209
-100
lines changed

cilly/src/bin/asmedit.rs

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{
22
collections::VecDeque,
33
io::{stdin, Read},
44
num::NonZeroU32,
5+
panic::catch_unwind,
56
time::Instant,
67
};
78

@@ -234,49 +235,82 @@ fn main() {
234235
"" => continue,
235236
"misolate" => {
236237
let isolate_id = parse_id(body, &asm);
237-
let externs: Vec<_> = asm
238-
.methods_with(|_, _, def| *def.access() == Access::Extern)
239-
.map(|(id, _)| id)
240-
.copied()
241-
.collect();
242-
for extern_id in externs {
243-
asm.modify_methodef(|_, method| method.set_access(Access::Public), extern_id);
244-
}
245238
let Some(isolate_id) = asm.method_ref_to_def(isolate_id) else {
246239
eprintln!("Invalid method!");
247240
continue;
248241
};
249-
let main_module = asm.main_module();
250-
let entrypoint = asm.alloc_string("entrypoint");
251-
let sig = asm.sig([], cilly::Type::Void);
252-
let fn_ptr = asm.alloc_node(CILNode::LdFtn(*isolate_id));
253-
let roots = [
254-
asm.alloc_root(CILRoot::Pop(fn_ptr)),
255-
asm.alloc_root(CILRoot::VoidRet),
256-
];
257-
let implementation = MethodImpl::MethodBody {
258-
blocks: vec![BasicBlock::new(roots.into(), 0, None)],
259-
locals: vec![],
260-
};
261-
let entrypoint = MethodDef::new(
262-
Access::Extern,
263-
main_module,
264-
entrypoint,
265-
sig,
266-
cilly::cilnode::MethodKind::Static,
267-
implementation,
268-
vec![],
269-
);
270-
asm.new_method(entrypoint);
271-
asm.eliminate_dead_code();
272-
// GC
273-
asm = asm.clone().link(Assembly::default());
274-
asm.remove_dead_statics();
242+
misolate(&mut asm, *isolate_id)
275243
}
244+
"find_invalid_c" => find_invalid_c(&asm),
245+
"asmstats" => println!(
246+
"methoddefs:{}",
247+
asm.method_refs()
248+
.iter_keys()
249+
.filter_map(|key| asm.method_ref_to_def(key))
250+
.count()
251+
),
276252
_ => eprintln!("unknown command {cmd:?}"),
277253
}
278254
}
279255
}
256+
fn find_invalid_c(asm: &Assembly) {
257+
let mut fail_id = 0;
258+
for key in asm
259+
.method_refs()
260+
.iter_keys()
261+
.filter_map(|key| asm.method_ref_to_def(key))
262+
{
263+
let mut copy = asm.clone();
264+
misolate(&mut copy, *key);
265+
if !is_valid_c(&copy, fail_id) {
266+
fail_id += 1;
267+
eprintln!("Invalid c code methodid {key:?} fail_id:{fail_id}")
268+
} else {
269+
eprintln!("Method ok.")
270+
}
271+
}
272+
println!("Found {} faliures, saved to tmp", fail_id)
273+
}
274+
fn is_valid_c(asm: &Assembly, id: u32) -> bool {
275+
catch_unwind(|| asm.export(format!("/tmp/test{id}.out"), CExporter::new(false))).is_ok()
276+
}
277+
fn misolate(asm: &mut Assembly, isolate_id: MethodRefIdx) {
278+
let externs: Vec<_> = asm
279+
.methods_with(|_, _, def| *def.access() == Access::Extern)
280+
.map(|(id, _)| id)
281+
.copied()
282+
.collect();
283+
for extern_id in externs {
284+
asm.modify_methodef(|_, method| method.set_access(Access::Public), extern_id);
285+
}
286+
287+
let main_module = asm.main_module();
288+
let entrypoint = asm.alloc_string("entrypoint");
289+
let sig = asm.sig([], cilly::Type::Void);
290+
let fn_ptr = asm.alloc_node(CILNode::LdFtn(isolate_id));
291+
let roots = [
292+
asm.alloc_root(CILRoot::Pop(fn_ptr)),
293+
asm.alloc_root(CILRoot::VoidRet),
294+
];
295+
let implementation = MethodImpl::MethodBody {
296+
blocks: vec![BasicBlock::new(roots.into(), 0, None)],
297+
locals: vec![],
298+
};
299+
let entrypoint = MethodDef::new(
300+
Access::Extern,
301+
main_module,
302+
entrypoint,
303+
sig,
304+
cilly::cilnode::MethodKind::Static,
305+
implementation,
306+
vec![],
307+
);
308+
asm.new_method(entrypoint);
309+
asm.eliminate_dead_code();
310+
// GC
311+
*asm = asm.clone().link(Assembly::default());
312+
asm.remove_dead_statics();
313+
}
280314
fn parse_id(id: &str, asm: &Assembly) -> MethodRefIdx {
281315
if let Ok(id) = id.parse::<u32>() {
282316
unsafe { MethodRefIdx::from_raw(NonZeroU32::new(id).unwrap()) }
Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::io::Write;
2-
static DOTNET_ASSEMBLY:&[u8] = include_bytes!("{exec_file}");
3-
static RUNTIME_COFIG:&[u8] = b"{{
2+
static DOTNET_ASSEMBLY: &[u8] = include_bytes!("{exec_file}");
3+
static RUNTIME_COFIG: &[u8] = b"{{
44
\"runtimeOptions\": {{
5-
\"tfm\": \"netcoreapp3.1\",
5+
\"tfm\": \"net8.0\",
66
\"framework\": {{
77
\"name\": \"Microsoft.NETCore.App\",
88
\"version\": \"8.0.1\"
@@ -13,55 +13,84 @@ static RUNTIME_COFIG:&[u8] = b"{{
1313
}}
1414
}}
1515
}}";
16-
macro_rules! include_bytes_if{{
17-
(true,$path:literal)=>{{
18-
include_bytes!($path)
19-
}};
20-
(false,$path:literal)=>{{b""}};
21-
}}
22-
23-
static BUNDLED_SHARED_LIB:&[u8] = include_bytes_if!({has_native_companion},"{native_companion_file}");
24-
static BUNDLED_PDB:&[u8] = include_bytes_if!({has_pdb},"{pdb_file}");
25-
fn main(){{
26-
let curr_path = std::env::current_exe().unwrap();
27-
let dll_path = curr_path.with_extension("dll");
28-
let config = curr_path.with_extension("runtimeconfig.json");
29-
let pdb_file = curr_path.with_file_name("{pdb_file}");
30-
let mut requires_refresh = false;
31-
if dll_path.exists() {{
32-
let ondisk_len = std::fs::File::open(dll_path.clone()).expect("Could not create a file to unpack the .NET assembly").metadata().unwrap().len();
33-
// If the length on disk is != expected, write the new file TODO: this can very rarely not detect if an update is needed. Check assembly GUID too, to ensure recompilation works.
34-
if ondisk_len != DOTNET_ASSEMBLY.len() as u64{{
35-
requires_refresh = true;
36-
}}
37-
}}
38-
if !dll_path.exists() || requires_refresh {{
39-
let mut file = std::fs::File::create(dll_path.clone()).expect("Could not create a file to unpack the .NET assembly");
40-
file.write_all(DOTNET_ASSEMBLY).expect("Could not unpack the .NET assembly");
41-
}}
42-
if !config.exists() ||requires_refresh {{
43-
let mut file = std::fs::File::create(config).expect("Could not create a file to save .NET runtime settings.");
44-
file.write_all(RUNTIME_COFIG).expect("Could not save .NET runtime settings");
45-
}}
46-
if {has_native_companion} {{
47-
if !std::path::Path::new("{native_companion_file}").exists() || requires_refresh{{
48-
let mut file = std::fs::File::create("{native_companion_file}").expect("Could not create a file to provide the native companion.");
49-
file.write_all(BUNDLED_SHARED_LIB).expect("Could create a file to provide the native companion");
50-
}}
51-
52-
}}
53-
if {has_pdb}{{
54-
if !pdb_file.exists() || requires_refresh{{
55-
println!("creating the pdb file");
56-
let mut file = std::fs::File::create(pdb_file).expect("Could not create a file to provide the pdb debug info.");
57-
file.write_all(BUNDLED_PDB).expect("Could create a file to provide the pdb debug info.");
58-
}}
59-
else{{
60-
println!("Not creating the pdb file");
61-
}}
62-
}}
63-
let args:Vec<String> = std::env::args().collect();
64-
let args = &args[1..];
65-
std::process::Command::new("{jumpstart_cmd}").arg(dll_path).args(args).status().expect("Could not start the .NET runtime.");
66-
}}
16+
macro_rules! include_bytes_if {}
6717

18+
static BUNDLED_SHARED_LIB: &[u8] =
19+
include_bytes_if!({ has_native_companion }, "{native_companion_file}");
20+
static BUNDLED_PDB: &[u8] = include_bytes_if!({ has_pdb }, "{pdb_file}");
21+
fn main() {
22+
{
23+
let curr_path = std::env::current_exe().unwrap();
24+
let dll_path = curr_path.with_extension("dll");
25+
let config = curr_path.with_extension("runtimeconfig.json");
26+
let pdb_file = curr_path.with_file_name("{pdb_file}");
27+
let mut requires_refresh = false;
28+
if dll_path.exists() {
29+
{
30+
let ondisk_len = std::fs::File::open(dll_path.clone())
31+
.expect("Could not create a file to unpack the .NET assembly")
32+
.metadata()
33+
.unwrap()
34+
.len();
35+
// If the length on disk is != expected, write the new file TODO: this can very rarely not detect if an update is needed. Check assembly GUID too, to ensure recompilation works.
36+
if ondisk_len != DOTNET_ASSEMBLY.len() as u64 {
37+
{
38+
requires_refresh = true;
39+
}
40+
}
41+
}
42+
}
43+
if !dll_path.exists() || requires_refresh {
44+
{
45+
let mut file = std::fs::File::create(dll_path.clone())
46+
.expect("Could not create a file to unpack the .NET assembly");
47+
file.write_all(DOTNET_ASSEMBLY)
48+
.expect("Could not unpack the .NET assembly");
49+
}
50+
}
51+
if !config.exists() || requires_refresh {
52+
{
53+
let mut file = std::fs::File::create(config)
54+
.expect("Could not create a file to save .NET runtime settings.");
55+
file.write_all(RUNTIME_COFIG)
56+
.expect("Could not save .NET runtime settings");
57+
}
58+
}
59+
if { has_native_companion } {
60+
{
61+
if !std::path::Path::new("{native_companion_file}").exists() || requires_refresh {
62+
{
63+
let mut file = std::fs::File::create("{native_companion_file}")
64+
.expect("Could not create a file to provide the native companion.");
65+
file.write_all(BUNDLED_SHARED_LIB)
66+
.expect("Could create a file to provide the native companion");
67+
}
68+
}
69+
}
70+
}
71+
if { has_pdb } {
72+
{
73+
if !pdb_file.exists() || requires_refresh {
74+
{
75+
println!("creating the pdb file");
76+
let mut file = std::fs::File::create(pdb_file)
77+
.expect("Could not create a file to provide the pdb debug info.");
78+
file.write_all(BUNDLED_PDB)
79+
.expect("Could create a file to provide the pdb debug info.");
80+
}
81+
} else {
82+
{
83+
println!("Not creating the pdb file");
84+
}
85+
}
86+
}
87+
}
88+
let args: Vec<String> = std::env::args().collect();
89+
let args = &args[1..];
90+
std::process::Command::new("{jumpstart_cmd}")
91+
.arg(dll_path)
92+
.args(args)
93+
.status()
94+
.expect("Could not start the .NET runtime.");
95+
}
96+
}

cilly/src/v2/asm.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -510,19 +510,25 @@ impl Assembly {
510510
self.new_method(cctor_def)
511511
}
512512
}
513-
/// Returns a reference to the static initializer
514-
pub fn cctor(&mut self) -> MethodDefIdx {
513+
fn cctor_mref(&mut self) -> MethodRefIdx {
515514
let main_module = self.main_module();
516515
let user_init = self.alloc_string(CCTOR);
517516
let ctor_sig = self.sig([], Type::Void);
518-
let mref = MethodRef::new(
517+
self.alloc_methodref(MethodRef::new(
519518
*main_module,
520519
user_init,
521520
ctor_sig,
522521
MethodKind::Static,
523522
vec![].into(),
524-
);
525-
let mref = self.alloc_methodref(mref);
523+
))
524+
}
525+
pub fn has_cctor(&mut self) -> bool {
526+
let cctor = self.cctor_mref();
527+
self.method_ref_to_def(cctor).is_some()
528+
}
529+
/// Returns a reference to the static initializer
530+
pub fn cctor(&mut self) -> MethodDefIdx {
531+
let mref = self.cctor_mref();
526532
if self.method_defs.contains_key(&MethodDefIdx(mref)) {
527533
MethodDefIdx(mref)
528534
} else {
@@ -534,6 +540,9 @@ impl Assembly {
534540
)],
535541
locals: vec![],
536542
};
543+
let main_module = self.main_module();
544+
let user_init = self.alloc_string(CCTOR);
545+
let ctor_sig = self.sig([], Type::Void);
537546
let cctor_def = MethodDef::new(
538547
Access::Extern,
539548
main_module,
@@ -1013,6 +1022,10 @@ impl Assembly {
10131022
},
10141023
}
10151024
}
1025+
1026+
pub fn method_refs(&self) -> &BiMap<MethodRefIdx, MethodRef> {
1027+
&self.method_refs
1028+
}
10161029
}
10171030
/// An initializer, which runs before everything else. By convention, it is used to initialize static / const data. Should not execute any user code
10181031
pub const CCTOR: &str = ".cctor";
@@ -1024,7 +1037,11 @@ pub const USER_INIT: &str = "static_init";
10241037
pub const ENTRYPOINT: &str = "entrypoint";
10251038
/// Main class of this module
10261039
pub const MAIN_MODULE: &str = "MainModule";
1027-
1040+
#[test]
1041+
fn test_encoded_stats() {
1042+
assert_eq!(encoded_stats(&u64::MAX), (type_name::<u64>(), 10));
1043+
assert_eq!(encoded_stats(&0_i32), (type_name::<i32>(), 1));
1044+
}
10281045
fn encoded_stats<T: Serialize + for<'a> Deserialize<'a>>(val: &T) -> (&'static str, usize) {
10291046
let buff = postcard::to_allocvec(val).unwrap();
10301047
let start = std::time::Instant::now();

cilly/src/v2/basic_block.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,22 @@ impl BasicBlock {
110110
&mut self.roots,
111111
)
112112
}
113-
/// Checks if this basic block consists of nothing more than an unconditional jump to another block
113+
/// Checks if this basic block consists of nothing more than an unconditional jump to another block.
114+
/// ```
115+
/// # use cilly::*;
116+
/// # use cilly::v2::BasicBlock;
117+
/// # let mut asm = Assembly::default();
118+
/// # let mut void_ret = asm.alloc_root(CILRoot::VoidRet);
119+
/// # let mut rethrow = asm.alloc_root(CILRoot::ReThrow);
120+
/// # let mut val = asm.alloc_node(0);
121+
/// # let mut do_sth = asm.alloc_root(CILRoot::StLoc(0,val));
122+
/// let target = 11;
123+
/// let mut jump = asm.alloc_root(CILRoot::Branch(Box::new((target,0,None))));
124+
/// assert_eq!(BasicBlock::new(vec![],0,None).is_direct_jump(&asm),None);
125+
/// assert_eq!(BasicBlock::new(vec![void_ret],0,None).is_direct_jump(&asm),None);
126+
/// assert_eq!(BasicBlock::new(vec![jump],0,None).is_direct_jump(&asm),Some((target,0)));
127+
/// assert_eq!(BasicBlock::new(vec![do_sth,jump],0,None).is_direct_jump(&asm),None);
128+
/// ```
114129
#[must_use]
115130
pub fn is_direct_jump(&self, asm: &Assembly) -> Option<(BlockId, BlockId)> {
116131
let mut meningfull_root = self.meaningfull_roots(asm);
@@ -125,6 +140,19 @@ impl BasicBlock {
125140
}
126141
}
127142
/// Checks if this basic block consists of nothing more thaan an uncondtional rethrow
143+
/// ```
144+
/// # use cilly::*;
145+
/// # use cilly::v2::BasicBlock;
146+
/// # let mut asm = Assembly::default();
147+
/// # let mut void_ret = asm.alloc_root(CILRoot::VoidRet);
148+
/// # let mut rethrow = asm.alloc_root(CILRoot::ReThrow);
149+
/// # let mut val = asm.alloc_node(0);
150+
/// # let mut do_sth = asm.alloc_root(CILRoot::StLoc(0,val));
151+
/// assert!(!BasicBlock::new(vec![],0,None).is_only_rethrow(&asm));
152+
/// assert!(!BasicBlock::new(vec![void_ret],0,None).is_only_rethrow(&asm));
153+
/// assert!(BasicBlock::new(vec![rethrow],0,None).is_only_rethrow(&asm));
154+
/// assert!(!BasicBlock::new(vec![do_sth,rethrow],0,None).is_only_rethrow(&asm));
155+
/// ```
128156
#[must_use]
129157
pub fn is_only_rethrow(&self, asm: &Assembly) -> bool {
130158
let mut meningfull_root = self.meaningfull_roots(asm);

cilly/src/v2/bimap.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ impl<Key: IntoBiMapIndex + Eq + Hash + Clone + Debug, Value: Eq + Hash + Clone +
6262
pub fn contais_val(&self, def: Value) -> bool {
6363
self.1.contains_key(&def)
6464
}
65+
pub fn iter_keys(&self) -> impl Iterator<Item = Key> {
66+
(1..(self.0.len() as u32)).map(|key| Key::from_index(NonZeroU32::new(key).unwrap()))
67+
}
6568
}
6669
pub type BiMapIndex = NonZeroU32;
6770
pub trait IntoBiMapIndex {

0 commit comments

Comments
 (0)