diff --git a/nac3artiq/src/lib.rs b/nac3artiq/src/lib.rs index 18eeb5d..68e837b 100644 --- a/nac3artiq/src/lib.rs +++ b/nac3artiq/src/lib.rs @@ -660,7 +660,8 @@ impl Nac3 { let pass_options = PassBuilderOptions::create(); pass_options.set_merge_functions(true); - let result = main.run_passes("default", &target_machine, pass_options); + let passes = format!("default", self.llvm_options.opt_level as u32); + let result = main.run_passes(passes.as_str(), &target_machine, pass_options); if let Err(err) = result { panic!("Failed to run optimization for module `main`: {}", err.to_string()); } diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 28e3524..3300d3c 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -365,6 +365,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { let mut return_slot = None; if fun.count_params() > 0 { let sret_id = Attribute::get_named_enum_kind_id("sret"); + let byref_id = Attribute::get_named_enum_kind_id("byref"); let byval_id = Attribute::get_named_enum_kind_id("byval"); let offset = if fun.get_enum_attribute(AttributeLoc::Param(0), sret_id).is_some() { @@ -376,7 +377,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { 0 }; for (i, param) in params.iter().enumerate() { - if fun.get_enum_attribute(AttributeLoc::Param((i + offset) as u32), byval_id).is_some() { + let loc = AttributeLoc::Param((i + offset) as u32); + if fun.get_enum_attribute(loc, byref_id).is_some() || fun.get_enum_attribute(loc, byval_id).is_some() { // lazy update if loc_params.is_empty() { loc_params.extend(params[0..i+offset].iter().copied()); @@ -745,11 +747,11 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>( Some(ctx.get_llvm_abi_type(generator, fun.0.ret)) }; let has_sret = ret_type.map_or(false, |ret_type| need_sret(ctx.ctx, ret_type)); - let mut byvals = Vec::new(); + let mut byrefs = Vec::new(); let mut params = args.iter().enumerate() .map(|(i, arg)| match ctx.get_llvm_abi_type(generator, arg.ty) { BasicTypeEnum::StructType(ty) if is_extern => { - byvals.push((i, ty)); + byrefs.push((i, ty)); ty.ptr_type(AddressSpace::default()).into() }, x => x @@ -770,9 +772,19 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>( } else { 0 }; - for (i, ty) in byvals { - fun_val.add_attribute(AttributeLoc::Param((i as u32) + offset), - ctx.ctx.create_type_attribute(Attribute::get_named_enum_kind_id("byval"), ty.as_any_type_enum())); + + // The attribute ID used to mark arguments of a structure type. + // Structure-Typed parameters of extern functions must **not** be marked as `byval`, as + // `byval` explicitly specifies that the argument is to be passed on the stack, which breaks + // on most ABIs where the first several arguments are expected to be passed in registers. + let passing_attr_id = Attribute::get_named_enum_kind_id( + if is_extern { "byref" } else { "byval" } + ); + for (i, ty) in byrefs { + fun_val.add_attribute( + AttributeLoc::Param((i as u32) + offset), + ctx.ctx.create_type_attribute(passing_attr_id, ty.as_any_type_enum()) + ); } fun_val }); diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 67176c8..8d2e39f 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -328,7 +328,6 @@ impl WorkerRegistry { self.llvm_options.opt_level ).expect(format!("could not create target machine from properties {:?}", self.llvm_options.target).as_str()); let passes = format!("default", self.llvm_options.opt_level as u32); - let result = module.run_passes(passes.as_str(), &target_machine, pass_options); if let Err(err) = result { panic!("Failed to run optimization for module `{}`: {}", diff --git a/nac3standalone/demo/demo.rs b/nac3standalone/demo/demo.rs index c7e4452..06b1b51 100644 --- a/nac3standalone/demo/demo.rs +++ b/nac3standalone/demo/demo.rs @@ -1,3 +1,7 @@ +use std::io; +use std::io::Write; +use std::process::exit; + mod cslice { // copied from https://github.com/dherman/cslice use std::marker::PhantomData; @@ -55,6 +59,14 @@ pub extern "C" fn output_asciiart(x: i32) { } } +#[no_mangle] +pub extern "C" fn output_str(x: &cslice::CSlice) { + for e in x.as_ref().iter() { + print!("{}", char::from(*e)); + } + println!(); +} + #[no_mangle] pub extern "C" fn output_int32_list(x: &cslice::CSlice) { print!("["); @@ -75,8 +87,14 @@ pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _conte } #[no_mangle] -pub extern "C" fn __nac3_raise(_state: u32, _exception_object: u32, _context: u32) -> u32 { - unimplemented!(); +pub extern "C" fn __nac3_raise(state: u32, exception_object: u32, context: u32) -> u32 { + writeln!(io::stderr(), + "__nac3_raise(state: {:#010x}, exception_object: {:#010x}, context: {:#010x})", + state, + exception_object, + context + ).unwrap(); + exit(101); } extern "C" { diff --git a/nac3standalone/demo/interpret_demo.py b/nac3standalone/demo/interpret_demo.py index 71bb426..dcee200 100755 --- a/nac3standalone/demo/interpret_demo.py +++ b/nac3standalone/demo/interpret_demo.py @@ -58,7 +58,8 @@ def patch(module): "output_int32_list", "output_uint32", "output_uint64", - "output_float64" + "output_float64", + "output_str", }: return print else: diff --git a/nac3standalone/demo/src/demo_test.py b/nac3standalone/demo/src/demo_test.py new file mode 100644 index 0000000..3a99887 --- /dev/null +++ b/nac3standalone/demo/src/demo_test.py @@ -0,0 +1,60 @@ +@extern +def output_int32(x: int32): + ... + +@extern +def output_int64(x: int64): + ... + +@extern +def output_uint32(x: uint32): + ... + +@extern +def output_uint64(x: uint64): + ... + +@extern +def output_int32_list(x: list[int32]): + ... + +@extern +def output_asciiart(x: int32): + ... + +@extern +def output_str(x: str): + ... + +def test_output_int32(): + output_int32(-128) + +def test_output_int64(): + output_int64(int64(-256)) + +def test_output_uint32(): + output_uint32(uint32(128)) + +def test_output_uint64(): + output_uint64(uint64(256)) + +def test_output_asciiart(): + for i in range(17): + output_asciiart(i) + output_asciiart(0) + +def test_output_int32_list(): + output_int32_list([0, 1, 3, 5, 10]) + +def test_output_str_family(): + output_str("hello world") + +def run() -> int32: + test_output_int32() + test_output_int64() + test_output_uint32() + test_output_uint64() + test_output_asciiart() + test_output_int32_list() + test_output_str_family() + return 0 \ No newline at end of file diff --git a/nac3standalone/src/main.rs b/nac3standalone/src/main.rs index d45b875..16f3aae 100644 --- a/nac3standalone/src/main.rs +++ b/nac3standalone/src/main.rs @@ -370,7 +370,8 @@ fn main() { let pass_options = PassBuilderOptions::create(); pass_options.set_merge_functions(true); - let result = main.run_passes("default", &target_machine, pass_options); + let passes = format!("default", opt_level as u32); + let result = main.run_passes(passes.as_str(), &target_machine, pass_options); if let Err(err) = result { panic!("Failed to run optimization for module `main`: {}", err.to_string()); }