From 6fc3e2ba6dda8ee0b4d368a1dac1bd386ebb9b3b Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Wed, 14 Feb 2024 08:53:47 +0100 Subject: [PATCH] feat: better cli and add tests --- Cargo.lock | 101 +++++++++++++++--- lib/edlang_codegen_llvm/Cargo.toml | 4 - lib/edlang_codegen_llvm/src/codegen.rs | 55 ++++++---- lib/edlang_driver/Cargo.toml | 4 + lib/edlang_driver/src/lib.rs | 57 ++++++++-- lib/edlang_driver/tests/common.rs | 88 +++++++++++++++ lib/edlang_driver/tests/programs.rs | 21 ++++ lib/edlang_driver/tests/programs/basic_ifs.ed | 19 ++++ lib/edlang_driver/tests/programs/simple.ed | 11 ++ lib/edlang_lowering/src/lib.rs | 49 ++++++--- lib/edlang_session/src/lib.rs | 14 +++ programs/example.ed | 2 +- 12 files changed, 361 insertions(+), 64 deletions(-) create mode 100644 lib/edlang_driver/tests/common.rs create mode 100644 lib/edlang_driver/tests/programs.rs create mode 100644 lib/edlang_driver/tests/programs/basic_ifs.ed create mode 100644 lib/edlang_driver/tests/programs/simple.ed diff --git a/Cargo.lock b/Cargo.lock index a09c6e927..96883f79f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,17 +154,14 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - [[package]] name = "cc" -version = "1.0.85" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b918671670962b48bc23753aef0c51d072dca6f52f01f800854ada6ddb7f7d3" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -325,8 +322,6 @@ dependencies = [ name = "edlang_codegen_llvm" version = "0.0.1-alpha.3" dependencies = [ - "bumpalo", - "cc", "edlang_ir", "edlang_parser", "edlang_session", @@ -350,6 +345,8 @@ dependencies = [ "edlang_lowering", "edlang_parser", "edlang_session", + "tempfile", + "test-case", "tracing", "tracing-subscriber", ] @@ -416,6 +413,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eyre" version = "0.6.12" @@ -426,6 +433,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -469,9 +482,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "indenter" @@ -596,6 +609,12 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "llvm-sys" version = "170.0.1" @@ -886,6 +905,19 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -955,6 +987,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "term" version = "0.7.0" @@ -966,6 +1010,39 @@ dependencies = [ "winapi", ] +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "test-case-core", +] + [[package]] name = "thiserror" version = "1.0.57" diff --git a/lib/edlang_codegen_llvm/Cargo.toml b/lib/edlang_codegen_llvm/Cargo.toml index 3cbf1160a..c35cc7dee 100644 --- a/lib/edlang_codegen_llvm/Cargo.toml +++ b/lib/edlang_codegen_llvm/Cargo.toml @@ -13,7 +13,6 @@ repository = "https://github.com/edg-l/edlang" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bumpalo = { version = "3.14.0", features = ["std"] } edlang_ir = { version = "0.0.1-alpha.3", path = "../edlang_ir" } edlang_parser = { version = "0.0.1-alpha.3", path = "../edlang_parser" } edlang_session = { version = "0.0.1-alpha.3", path = "../edlang_session" } @@ -21,6 +20,3 @@ llvm-sys = "170.0.1" inkwell = { git = "https://github.com/TheDan64/inkwell", rev = "c044e3cd8d92972ca75b374fb6c5a2794f5b53ca", features = ["llvm17-0"] } tracing = { workspace = true } edlang_span = { version = "0.0.1-alpha.3", path = "../edlang_span" } - -[build-dependencies] -cc = "1.0.83" diff --git a/lib/edlang_codegen_llvm/src/codegen.rs b/lib/edlang_codegen_llvm/src/codegen.rs index f3a2c4c43..9ad6ea93e 100644 --- a/lib/edlang_codegen_llvm/src/codegen.rs +++ b/lib/edlang_codegen_llvm/src/codegen.rs @@ -17,7 +17,7 @@ use inkwell::{ }; use ir::{LocalKind, ModuleBody, ProgramBody, TypeInfo, ValueTree}; use llvm_sys::debuginfo::LLVMDIFlagPublic; -use tracing::info; +use tracing::{info, trace}; #[derive(Debug, Clone, Copy)] struct CompileCtx<'a> { @@ -78,8 +78,17 @@ pub fn compile(session: &Session, program: &ProgramBody) -> Result inkwell::OptimizationLevel::None, + edlang_session::OptLevel::Less => inkwell::OptimizationLevel::Less, + edlang_session::OptLevel::Default => inkwell::OptimizationLevel::Default, + edlang_session::OptLevel::Aggressive => inkwell::OptimizationLevel::Aggressive, + }, + if session.library { + inkwell::targets::RelocMode::DynamicNoPic + } else { + inkwell::targets::RelocMode::Default + }, inkwell::targets::CodeModel::Default, ) .unwrap(); @@ -131,15 +140,20 @@ pub fn compile(session: &Session, program: &ProgramBody) -> Result Result, fn_id: DefId) { let (arg_types, ret_type) = ctx.ctx.program.function_signatures.get(&fn_id).unwrap(); let body = ctx.ctx.program.functions.get(&fn_id).unwrap(); - info!("compiling fn sig: {}", body.name); + trace!("compiling fn sig: {}", body.name); let args: Vec = arg_types .iter() @@ -242,7 +256,7 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId) { fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> { let body = ctx.ctx.program.functions.get(&fn_id).unwrap(); - info!("compiling fn body: {}", body.name); + trace!("compiling fn body: {}", body.name); let fn_value = ctx.module.get_function(&body.name).unwrap(); let di_program = fn_value.get_subprogram().unwrap(); @@ -343,14 +357,14 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> ctx.builder.build_unconditional_branch(blocks[0])?; for (block, llvm_block) in body.blocks.iter().zip(&blocks) { - info!("compiling block"); + trace!("compiling block"); ctx.builder.position_at_end(*llvm_block); for stmt in &block.statements { if let Some(span) = stmt.span { debug_loc = ctx.set_debug_loc(debug_loc.get_scope(), span); } - info!("compiling stmt"); + trace!("compiling stmt"); match &stmt.kind { ir::StatementKind::Assign(place, rvalue) => { let local = &body.locals[place.local]; @@ -409,7 +423,7 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> } } - info!("compiling terminator"); + trace!("compiling terminator"); match &block.terminator { ir::Terminator::Target(id) => { ctx.builder.build_unconditional_branch(blocks[*id])?; @@ -429,18 +443,14 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> discriminator, targets, } => { - let (condition, condition_ty) = + let (condition, _condition_ty) = compile_load_operand(ctx, fn_id, &locals, discriminator)?; let cond = condition.into_int_value(); - dbg!(&cond); - dbg!(&condition_ty); - let mut cases = Vec::new(); for (value, target) in targets.values.iter().zip(targets.targets.iter()) { let target = *target; let ty_kind = value.get_type(); - dbg!(&ty_kind); let block = blocks[target]; let value = compile_value( ctx, @@ -451,7 +461,6 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> }, )? .into_int_value(); - dbg!(&value); cases.push((value, block)); } diff --git a/lib/edlang_driver/Cargo.toml b/lib/edlang_driver/Cargo.toml index 4ac549359..da4aa7b7c 100644 --- a/lib/edlang_driver/Cargo.toml +++ b/lib/edlang_driver/Cargo.toml @@ -25,3 +25,7 @@ edlang_parser = { version = "0.0.1-alpha.3", path = "../edlang_parser" } edlang_session = { version = "0.0.1-alpha.3", path = "../edlang_session" } tracing = { workspace = true } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } + +[dev-dependencies] +tempfile = "3.10.0" +test-case = "3.3.1" diff --git a/lib/edlang_driver/src/lib.rs b/lib/edlang_driver/src/lib.rs index 18c2de4f0..19e0fc595 100644 --- a/lib/edlang_driver/src/lib.rs +++ b/lib/edlang_driver/src/lib.rs @@ -7,7 +7,7 @@ use edlang_lowering::lower_modules; use edlang_session::{DebugInfo, OptLevel, Session}; #[derive(Parser, Debug)] -#[command(author, version, about, long_about = None, bin_name = "edlang")] +#[command(author, version, about = "edlang compiler driver", long_about = None, bin_name = "edlang")] pub struct CompilerArgs { /// The input file. input: PathBuf, @@ -16,18 +16,33 @@ pub struct CompilerArgs { #[arg(short, long, default_value_t = false)] release: bool, + /// Set the optimization level, 0,1,2,3 + #[arg(short = 'O', long)] + optlevel: Option, + + /// Always add debug info + #[arg(long)] + debug_info: Option, + /// Build as a library. #[arg(short, long, default_value_t = false)] library: bool, - #[arg(long, default_value_t = false)] - mlir: bool, - + /// Print the edlang AST #[arg(long, default_value_t = false)] ast: bool, + /// Print the edlang IR #[arg(long, default_value_t = false)] ir: bool, + + /// Output llvm ir + #[arg(long, default_value_t = false)] + llvm: bool, + + /// Output asm + #[arg(long, default_value_t = false)] + asm: bool, } pub fn main() -> Result<(), Box> { @@ -59,19 +74,34 @@ pub fn main() -> Result<(), Box> { } let output_file = target_dir.join(PathBuf::from(args.input.file_name().unwrap())); let output_file = if args.library { - output_file.with_extension("so") + output_file.with_extension(Session::get_platform_library_ext()) + } else if cfg!(target_os = "windows") { + output_file.with_extension("exe") } else { output_file.with_extension("") }; let session = Session { file_path: args.input, - debug_info: if args.release { + debug_info: if let Some(debug_info) = args.debug_info { + if debug_info { + DebugInfo::Full + } else { + DebugInfo::None + } + } else if args.release { DebugInfo::None } else { DebugInfo::Full }, - optlevel: if args.release { + optlevel: if let Some(optlevel) = args.optlevel { + match optlevel { + 0 => OptLevel::None, + 1 => OptLevel::Less, + 2 => OptLevel::Default, + _ => OptLevel::Aggressive, + } + } else if args.release { OptLevel::Aggressive } else { OptLevel::None @@ -80,8 +110,15 @@ pub fn main() -> Result<(), Box> { library: args.library, target_dir, output_file, + output_asm: args.asm, + output_llvm: args.llvm, }; - tracing::debug!("Compiling with session: {:#?}", session); + tracing::debug!("Input file: {:#?}", session.file_path); + tracing::debug!("Target dir: {:#?}", session.target_dir); + tracing::debug!("Output file: {:#?}", session.output_file); + tracing::debug!("Is library: {:#?}", session.library); + tracing::debug!("Optlevel: {:#?}", session.optlevel); + tracing::debug!("Debug Info: {:#?}", session.debug_info); if args.ast { println!("{:#?}", module); @@ -98,9 +135,9 @@ pub fn main() -> Result<(), Box> { let object_path = edlang_codegen_llvm::compile(&session, &program_ir)?; if session.library { - link_shared_lib(&object_path, &session.output_file.with_extension("so"))?; + link_shared_lib(&object_path, &session.output_file)?; } else { - link_binary(&object_path, &session.output_file.with_extension(""))?; + link_binary(&object_path, &session.output_file)?; } let elapsed = start_time.elapsed(); diff --git a/lib/edlang_driver/tests/common.rs b/lib/edlang_driver/tests/common.rs new file mode 100644 index 000000000..b4865bb6a --- /dev/null +++ b/lib/edlang_driver/tests/common.rs @@ -0,0 +1,88 @@ +use std::{ + borrow::Cow, + fmt, + path::{Path, PathBuf}, + process::Output, +}; + +use ariadne::Source; +use edlang_codegen_llvm::linker::{link_binary, link_shared_lib}; +use edlang_lowering::lower_modules; +use edlang_session::{DebugInfo, OptLevel, Session}; +use tempfile::TempDir; + +#[derive(Debug, Clone)] +struct TestError(Cow<'static, str>); + +impl fmt::Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl std::error::Error for TestError {} + +#[derive(Debug)] +pub struct CompileResult { + pub folder: TempDir, + pub object_file: PathBuf, + pub binary_file: PathBuf, +} + +pub fn compile_program( + source: &str, + name: &str, + library: bool, +) -> Result> { + let module = edlang_parser::parse_ast(source).unwrap(); + + let test_dir = tempfile::tempdir()?; + let test_dir_path = test_dir.path(); + let target_dir = test_dir_path.join("build_artifacts/"); + if !target_dir.exists() { + std::fs::create_dir_all(&target_dir)?; + } + let output_file = target_dir.join(PathBuf::from(name)); + let output_file = if library { + output_file.with_extension(Session::get_platform_library_ext()) + } else if cfg!(target_os = "windows") { + output_file.with_extension("exe") + } else { + output_file.with_extension("") + }; + + let session = Session { + file_path: PathBuf::from(name), + debug_info: DebugInfo::Full, + optlevel: OptLevel::Default, + source: Source::from(source.to_string()), + library, + target_dir, + output_file, + output_llvm: false, + output_asm: false, + }; + + let program_ir = lower_modules(&[module]); + + let object_path = edlang_codegen_llvm::compile(&session, &program_ir)?; + + if library { + link_shared_lib(&object_path, &session.output_file)?; + } else { + link_binary(&object_path, &session.output_file)?; + } + + Ok(CompileResult { + folder: test_dir, + object_file: object_path, + binary_file: session.output_file, + }) +} + +pub fn run_program(program: &Path, args: &[&str]) -> Result { + std::process::Command::new(program) + .args(args) + .spawn()? + .wait_with_output() +} diff --git a/lib/edlang_driver/tests/programs.rs b/lib/edlang_driver/tests/programs.rs new file mode 100644 index 000000000..0166063de --- /dev/null +++ b/lib/edlang_driver/tests/programs.rs @@ -0,0 +1,21 @@ +use crate::common::{compile_program, run_program}; +use test_case::test_case; + +mod common; + +#[test_case(include_str!("programs/simple.ed"), "simple", false, 0, &["1"] ; "simple.ed 1")] +#[test_case(include_str!("programs/simple.ed"), "simple", false, 1, &["a", "b"] ; "simple.ed 3")] +#[test_case(include_str!("programs/basic_ifs.ed"), "basic_ifs", false, 9, &[] ; "basic_ifs")] +fn example_tests(source: &str, name: &str, is_library: bool, status_code: i32, args: &[&str]) { + let program = compile_program(source, name, is_library).unwrap(); + + assert!(program.binary_file.exists(), "program not compiled"); + + let result = run_program(&program.binary_file, args).unwrap(); + assert_eq!( + result.status.code().unwrap(), + status_code, + "Program {} returned a unexpected status code", + name + ); +} diff --git a/lib/edlang_driver/tests/programs/basic_ifs.ed b/lib/edlang_driver/tests/programs/basic_ifs.ed new file mode 100644 index 000000000..897de2d64 --- /dev/null +++ b/lib/edlang_driver/tests/programs/basic_ifs.ed @@ -0,0 +1,19 @@ +mod Main { + fn add(a: i32, b: i32) -> i32 { + return a + b; + } + + fn check(a: i32) -> i32 { + if a == 2 { + return a; + } else { + return 0; + } + } + + pub fn main() -> i32 { + let x: i32 = 2 + 3; + let y: i32 = add(x, 4); + return y; + } +} diff --git a/lib/edlang_driver/tests/programs/simple.ed b/lib/edlang_driver/tests/programs/simple.ed new file mode 100644 index 000000000..0734438de --- /dev/null +++ b/lib/edlang_driver/tests/programs/simple.ed @@ -0,0 +1,11 @@ +mod Main { + pub fn main(argc: i64) -> i64 { + let mut a: i64 = 0; + + if argc > 2 { + a = 1; + } + + return a; + } +} diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index cbcc29e77..0ea2f2a1d 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -210,12 +210,20 @@ fn lower_if_stmt(builder: &mut BodyBuilder, info: &ast::IfStmt, ret_type: &TypeI } // keet idx to change terminator - let last_then_block_idx = builder.body.blocks.len(); - let statements = std::mem::take(&mut builder.statements); - builder.body.blocks.push(BasicBlock { - statements: statements.into(), - terminator: Terminator::Unreachable, - }); + let last_then_block_idx = if !matches!( + builder.body.blocks.last().unwrap().terminator, + Terminator::Return + ) { + let idx = builder.body.blocks.len(); + let statements = std::mem::take(&mut builder.statements); + builder.body.blocks.push(BasicBlock { + statements: statements.into(), + terminator: Terminator::Unreachable, + }); + Some(idx) + } else { + None + }; let first_else_block_idx = builder.body.blocks.len(); @@ -225,12 +233,20 @@ fn lower_if_stmt(builder: &mut BodyBuilder, info: &ast::IfStmt, ret_type: &TypeI } } - let last_else_block_idx = builder.body.blocks.len(); - let statements = std::mem::take(&mut builder.statements); - builder.body.blocks.push(BasicBlock { - statements: statements.into(), - terminator: Terminator::Unreachable, - }); + let last_else_block_idx = if !matches!( + builder.body.blocks.last().unwrap().terminator, + Terminator::Return + ) { + let idx = builder.body.blocks.len(); + let statements = std::mem::take(&mut builder.statements); + builder.body.blocks.push(BasicBlock { + statements: statements.into(), + terminator: Terminator::Unreachable, + }); + Some(idx) + } else { + None + }; let targets = SwitchTarget { values: vec![TypeKind::Bool.get_falsy_value()], @@ -244,8 +260,13 @@ fn lower_if_stmt(builder: &mut BodyBuilder, info: &ast::IfStmt, ret_type: &TypeI builder.body.blocks[current_block_idx].terminator = kind; let next_block_idx = builder.body.blocks.len(); - builder.body.blocks[last_then_block_idx].terminator = Terminator::Target(next_block_idx); - builder.body.blocks[last_else_block_idx].terminator = Terminator::Target(next_block_idx); + if let Some(idx) = last_then_block_idx { + builder.body.blocks[idx].terminator = Terminator::Target(next_block_idx); + } + + if let Some(idx) = last_else_block_idx { + builder.body.blocks[idx].terminator = Terminator::Target(next_block_idx); + } } fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) { diff --git a/lib/edlang_session/src/lib.rs b/lib/edlang_session/src/lib.rs index 4c3f60371..eda263dac 100644 --- a/lib/edlang_session/src/lib.rs +++ b/lib/edlang_session/src/lib.rs @@ -12,6 +12,20 @@ pub struct Session { /// The directory where to store artifacts and intermediate files such as object files. pub target_dir: PathBuf, pub output_file: PathBuf, + pub output_llvm: bool, + pub output_asm: bool, +} + +impl Session { + pub fn get_platform_library_ext() -> &'static str { + if cfg!(target_os = "macos") { + "dylib" + } else if cfg!(target_os = "windows") { + "dll" + } else { + "so" + } + } } #[derive(Clone, Copy, Debug, PartialEq, Hash)] diff --git a/programs/example.ed b/programs/example.ed index 23a532278..7cf278333 100644 --- a/programs/example.ed +++ b/programs/example.ed @@ -11,7 +11,7 @@ mod Main { } } - fn main() -> i32 { + pub fn main(argc: i32) -> i32 { let x: i32 = 2 + 3; let y: i32 = add(x, 4); return y;