diff --git a/Changelog.md b/Changelog.md index 7a356c6..59ede2b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,15 @@ # Changelog +[0.3.4] + +- Added support for `loadReagent`, which maps to the `lr` IC10 instruction + - Shorthand is `lr` + - Longform is `loadReagent` +- Update various Rust dependencies +- Added more optimizations, prioritizing `pop` instead of `get` when available + when backing up / restoring registers for function invocations. This should + save approximately 2 lines per backed up register + [0.3.3] - Fixed bug where negative temperature literals were converted to Kelvin diff --git a/ModData/About/About.xml b/ModData/About/About.xml index 9c0357c..5e0bd4d 100644 --- a/ModData/About/About.xml +++ b/ModData/About/About.xml @@ -2,7 +2,7 @@ Slang JoeDiertay - 0.3.3 + 0.3.4 [h1]Slang: High-Level Programming for Stationeers[/h1] diff --git a/csharp_mod/Plugin.cs b/csharp_mod/Plugin.cs index d067bdd..75ed957 100644 --- a/csharp_mod/Plugin.cs +++ b/csharp_mod/Plugin.cs @@ -41,7 +41,7 @@ namespace Slang { public const string PluginGuid = "com.biddydev.slang"; public const string PluginName = "Slang"; - public const string PluginVersion = "0.3.3"; + public const string PluginVersion = "0.3.4"; public static Mod MOD = new Mod(PluginName, PluginVersion); diff --git a/rust_compiler/Cargo.lock b/rust_compiler/Cargo.lock index 511d7ef..4a2af3c 100644 --- a/rust_compiler/Cargo.lock +++ b/rust_compiler/Cargo.lock @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytecheck" @@ -930,7 +930,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slang" -version = "0.3.2" +version = "0.3.4" dependencies = [ "anyhow", "clap", @@ -1063,18 +1063,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.4+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -1084,9 +1084,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" dependencies = [ "winnow", ] diff --git a/rust_compiler/Cargo.toml b/rust_compiler/Cargo.toml index 68d925b..4bdc1b7 100644 --- a/rust_compiler/Cargo.toml +++ b/rust_compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "slang" -version = "0.3.3" +version = "0.3.4" edition = "2021" [workspace] diff --git a/rust_compiler/libs/compiler/src/test/binary_expression.rs b/rust_compiler/libs/compiler/src/test/binary_expression.rs index 18d7885..5effd99 100644 --- a/rust_compiler/libs/compiler/src/test/binary_expression.rs +++ b/rust_compiler/libs/compiler/src/test/binary_expression.rs @@ -54,9 +54,7 @@ fn nested_binary_expressions() -> Result<()> { move r15 r2 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: push 10 diff --git a/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs b/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs index e4e8fa7..aa797bb 100644 --- a/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs +++ b/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs @@ -18,9 +18,7 @@ fn no_arguments() -> anyhow::Result<()> { doSomething: push ra __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal doSomething @@ -61,9 +59,7 @@ fn let_var_args() -> anyhow::Result<()> { move r15 r1 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: __internal_L2: @@ -71,9 +67,7 @@ fn let_var_args() -> anyhow::Result<()> { push r8 push r8 jal mul2 - sub r0 sp 1 - get r8 db r0 - sub sp sp 1 + pop r8 move r9 r15 pow r1 r9 2 move r9 r1 @@ -129,9 +123,7 @@ fn inline_literal_args() -> anyhow::Result<()> { move r15 5 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: move r8 123 @@ -139,9 +131,7 @@ fn inline_literal_args() -> anyhow::Result<()> { push 12 push 34 jal doSomething - sub r0 sp 1 - get r8 db r0 - sub sp sp 1 + pop r8 move r9 r15 " } @@ -171,9 +161,7 @@ fn mixed_args() -> anyhow::Result<()> { pop r9 push ra __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: move r8 123 @@ -181,9 +169,7 @@ fn mixed_args() -> anyhow::Result<()> { push r8 push 456 jal doSomething - sub r0 sp 1 - get r8 db r0 - sub sp sp 1 + pop r8 move r9 r15 " } @@ -216,9 +202,7 @@ fn with_return_statement() -> anyhow::Result<()> { move r15 456 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: push 123 @@ -252,9 +236,7 @@ fn with_negative_return_literal() -> anyhow::Result<()> { push ra move r15 -1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal doSomething diff --git a/rust_compiler/libs/compiler/src/test/declaration_literal.rs b/rust_compiler/libs/compiler/src/test/declaration_literal.rs index 58927c4..5f438d3 100644 --- a/rust_compiler/libs/compiler/src/test/declaration_literal.rs +++ b/rust_compiler/libs/compiler/src/test/declaration_literal.rs @@ -135,9 +135,7 @@ fn test_boolean_return() -> anyhow::Result<()> { move r15 1 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal getTrue diff --git a/rust_compiler/libs/compiler/src/test/function_declaration.rs b/rust_compiler/libs/compiler/src/test/function_declaration.rs index 2c909f6..8e24a54 100644 --- a/rust_compiler/libs/compiler/src/test/function_declaration.rs +++ b/rust_compiler/libs/compiler/src/test/function_declaration.rs @@ -5,7 +5,12 @@ use pretty_assertions::assert_eq; fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> { let compiled = compile!(debug r#" // we need more than 4 params to 'spill' into a stack var - fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {}; + fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { + return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7 + arg8 + arg9; + }; + + let item1 = 1; + let returned = doSomething(item1, 2, 3, 4, 5, 6, 7, 8, 9); "#); assert_eq!( @@ -21,11 +26,39 @@ fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> { pop r13 pop r14 push ra + sub r0 sp 3 + get r1 db r0 + sub r0 sp 2 + get r2 db r0 + add r3 r1 r2 + add r4 r3 r14 + add r5 r4 r13 + add r6 r5 r12 + add r7 r6 r11 + add r1 r7 r10 + add r2 r1 r9 + add r3 r2 r8 + move r15 r3 + j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 3 + pop ra + sub sp sp 2 j ra + main: + move r8 1 + push r8 + push r8 + push 2 + push 3 + push 4 + push 5 + push 6 + push 7 + push 8 + push 9 + jal doSomething + pop r8 + move r9 r15 "} ); @@ -60,9 +93,7 @@ fn test_early_return() -> anyhow::Result<()> { move r8 3 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal doSomething @@ -91,9 +122,7 @@ fn test_function_declaration_with_register_params() -> anyhow::Result<()> { pop r9 push ra __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra "} ); diff --git a/rust_compiler/libs/compiler/src/test/syscall.rs b/rust_compiler/libs/compiler/src/test/syscall.rs index 8456448..f7df25e 100644 --- a/rust_compiler/libs/compiler/src/test/syscall.rs +++ b/rust_compiler/libs/compiler/src/test/syscall.rs @@ -208,3 +208,29 @@ fn test_set_slot() -> anyhow::Result<()> { Ok(()) } + +#[test] +fn test_load_reagent() -> anyhow::Result<()> { + let compiled = compile! { + debug + r#" + device thingy = "d0"; + + let something = lr(thingy, "Contents", hash("Iron")); + "# + }; + + assert_eq!( + compiled, + indoc! { + " + j main + main: + lr r15 d0 Contents -666742878 + move r8 r15 + " + } + ); + + Ok(()) +} diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index db2282b..69cf3e9 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -1115,43 +1115,26 @@ impl<'a> Compiler<'a> { Some(name.span), )?; - for register in active_registers { - let VariableLocation::Stack(stack_offset) = stack - .get_location_of(&Cow::from(format!("temp_{register}")), None) - .map_err(Error::Scope)? - else { - // This shouldn't happen if we just added it - return Err(Error::Unknown( - format!("Failed to recover temp_{register}"), - Some(name.span), - )); - }; + // cleanup spilled temporary variables + let total_stack_usage = stack.stack_offset(); + let saved_regs_count = active_registers.len() as u16; + + if total_stack_usage > saved_regs_count { + let spill_amount = total_stack_usage - saved_regs_count; self.write_instruction( Instruction::Sub( - Operand::Register(VariableScope::TEMP_STACK_REGISTER), Operand::StackPointer, - Operand::Number(stack_offset.into()), - ), - Some(name.span), - )?; - - self.write_instruction( - Instruction::Get( - Operand::Register(register), - Operand::Device(Cow::from("db")), - Operand::Register(VariableScope::TEMP_STACK_REGISTER), + Operand::StackPointer, + Operand::Number(spill_amount.into()), ), Some(name.span), )?; } - if stack.stack_offset() > 0 { + // restore the registers in reverse order from the stack, now using `pop` + for register in active_registers.iter().rev() { self.write_instruction( - Instruction::Sub( - Operand::StackPointer, - Operand::StackPointer, - Operand::Number(Decimal::from(stack.stack_offset())), - ), + Instruction::Pop(Operand::Register(*register)), Some(name.span), )?; } @@ -2296,6 +2279,48 @@ impl<'a> Compiler<'a> { Ok(None) } + System::LoadReagent(device, reagent_mode, reagent_hash) => { + let Spanned { + node: LiteralOrVariable::Variable(device_spanned), + .. + } = device + else { + return Err(Error::AgrumentMismatch( + "Arg1 expected to be a variable".into(), + span, + )); + }; + + let (device, device_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Variable(device_spanned), + scope, + )?; + + let (reagent_mode, reagent_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Literal(reagent_mode.node), + scope, + )?; + + let (reagent_hash, reagent_hash_cleanup) = + self.compile_operand(*reagent_hash, scope)?; + + self.write_instruction( + Instruction::LoadReagent( + Operand::Register(VariableScope::RETURN_REGISTER), + device, + reagent_mode, + reagent_hash, + ), + Some(span), + )?; + + cleanup!(reagent_cleanup, reagent_hash_cleanup, device_cleanup); + + Ok(Some(CompileLocation { + location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER), + temp_name: None, + })) + } } } @@ -2693,33 +2718,49 @@ impl<'a> Compiler<'a> { self.write_instruction(Instruction::LabelDef(return_label.clone()), Some(span))?; - self.write_instruction( - Instruction::Sub( - Operand::Register(VariableScope::TEMP_STACK_REGISTER), - Operand::StackPointer, - Operand::Number(ra_stack_offset.into()), - ), - Some(span), - )?; + if ra_stack_offset == 1 { + self.write_instruction(Instruction::Pop(Operand::ReturnAddress), Some(span))?; - self.write_instruction( - Instruction::Get( - Operand::ReturnAddress, - Operand::Device(Cow::from("db")), - Operand::Register(VariableScope::TEMP_STACK_REGISTER), - ), - Some(span), - )?; - - if block_scope.stack_offset() > 0 { + let remaining_cleanup = block_scope.stack_offset() - 1; + if remaining_cleanup > 0 { + self.write_instruction( + Instruction::Sub( + Operand::StackPointer, + Operand::StackPointer, + Operand::Number(remaining_cleanup.into()), + ), + Some(span), + )?; + } + } else { self.write_instruction( Instruction::Sub( + Operand::Register(VariableScope::TEMP_STACK_REGISTER), Operand::StackPointer, - Operand::StackPointer, - Operand::Number(block_scope.stack_offset().into()), + Operand::Number(ra_stack_offset.into()), ), Some(span), )?; + + self.write_instruction( + Instruction::Get( + Operand::ReturnAddress, + Operand::Device(Cow::from("db")), + Operand::Register(VariableScope::TEMP_STACK_REGISTER), + ), + Some(span), + )?; + + if block_scope.stack_offset() > 0 { + self.write_instruction( + Instruction::Sub( + Operand::StackPointer, + Operand::StackPointer, + Operand::Number(block_scope.stack_offset().into()), + ), + Some(span), + )?; + } } self.write_instruction(Instruction::Jump(Operand::ReturnAddress), Some(span))?; diff --git a/rust_compiler/libs/helpers/src/syscall.rs b/rust_compiler/libs/helpers/src/syscall.rs index 5ec38c5..e329129 100644 --- a/rust_compiler/libs/helpers/src/syscall.rs +++ b/rust_compiler/libs/helpers/src/syscall.rs @@ -10,6 +10,7 @@ macro_rules! with_syscalls { "loadBatched", "loadBatchedNamed", "loadSlot", + "loadReagent", "set", "setBatched", "setBatchedNamed", @@ -35,6 +36,7 @@ macro_rules! with_syscalls { "lb", "lbn", "ls", + "lr", "s", "sb", "sbn", diff --git a/rust_compiler/libs/il/src/lib.rs b/rust_compiler/libs/il/src/lib.rs index 62affd8..1a3fdc3 100644 --- a/rust_compiler/libs/il/src/lib.rs +++ b/rust_compiler/libs/il/src/lib.rs @@ -191,6 +191,9 @@ pub enum Instruction<'a> { /// `sbn deviceHash nameHash type value` - Set Batch Named StoreBatchNamed(Operand<'a>, Operand<'a>, Operand<'a>, Operand<'a>), + /// `lr register device reagentMode int` + LoadReagent(Operand<'a>, Operand<'a>, Operand<'a>, Operand<'a>), + /// `j label` - Unconditional Jump Jump(Operand<'a>), /// `jal label` - Jump and Link (Function Call) @@ -311,6 +314,9 @@ impl<'a> fmt::Display for Instruction<'a> { Instruction::StoreBatchNamed(d_hash, n_hash, typ, val) => { write!(f, "sbn {} {} {} {}", d_hash, n_hash, typ, val) } + Instruction::LoadReagent(reg, device, reagent_mode, reagent_hash) => { + write!(f, "lr {} {} {} {}", reg, device, reagent_mode, reagent_hash) + } Instruction::Jump(lbl) => write!(f, "j {}", lbl), Instruction::JumpAndLink(lbl) => write!(f, "jal {}", lbl), Instruction::JumpRelative(off) => write!(f, "jr {}", off), diff --git a/rust_compiler/libs/optimizer/src/lib.rs b/rust_compiler/libs/optimizer/src/lib.rs index ea75c06..855f913 100644 --- a/rust_compiler/libs/optimizer/src/lib.rs +++ b/rust_compiler/libs/optimizer/src/lib.rs @@ -565,6 +565,7 @@ fn get_destination_reg(instr: &Instruction) -> Option { | Instruction::Sqrt(Operand::Register(r), _) | Instruction::Tan(Operand::Register(r), _) | Instruction::Trunc(Operand::Register(r), _) + | Instruction::LoadReagent(Operand::Register(r), _, _, _) | Instruction::Pop(Operand::Register(r)) => Some(*r), _ => None, } @@ -595,6 +596,9 @@ fn set_destination_reg<'a>(instr: &Instruction<'a>, new_reg: u8) -> Option { + Some(Instruction::LoadReagent(r, b.clone(), c.clone(), d.clone())) + } Instruction::SetEq(_, a, b) => Some(Instruction::SetEq(r, a.clone(), b.clone())), Instruction::SetNe(_, a, b) => Some(Instruction::SetNe(r, a.clone(), b.clone())), Instruction::SetGt(_, a, b) => Some(Instruction::SetGt(r, a.clone(), b.clone())), @@ -657,6 +661,14 @@ fn reg_is_read(instr: &Instruction, reg: u8) -> bool { Instruction::BranchEqZero(a, _) | Instruction::BranchNeZero(a, _) => check(a), + Instruction::LoadReagent(_, device, _, item_hash) => check(device) || check(item_hash), + + Instruction::LoadSlot(_, dev, slot, _) => check(dev) || check(slot), + Instruction::LoadBatch(_, dev, _, mode) => check(dev) || check(mode), + Instruction::LoadBatchNamed(_, d_hash, n_hash, _, mode) => { + check(d_hash) || check(n_hash) || check(mode) + } + Instruction::SetEq(_, a, b) | Instruction::SetNe(_, a, b) | Instruction::SetGt(_, a, b) diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index 66d0d99..4cb4618 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -1909,6 +1909,20 @@ impl<'a> Parser<'a> { Box::new(expr), ))) } + "loadReagent" | "lr" => { + let mut args = args!(3); + let next = args.next(); + let device = literal_or_variable!(next); + let next = args.next(); + let reagent_mode = get_arg!(Literal, literal_or_variable!(next)); + let reagent_hash = args.next().ok_or(Error::UnexpectedEOF)?; + + Ok(SysCall::System(System::LoadReagent( + device, + reagent_mode, + Box::new(reagent_hash), + ))) + } // Math SysCalls "acos" => { diff --git a/rust_compiler/libs/parser/src/sys_call.rs b/rust_compiler/libs/parser/src/sys_call.rs index 00a48ef..0251e91 100644 --- a/rust_compiler/libs/parser/src/sys_call.rs +++ b/rust_compiler/libs/parser/src/sys_call.rs @@ -237,6 +237,18 @@ documented! { Spanned>, Spanned>, Box>> + ), + /// Loads reagent of device's ReagentMode where a hash of the reagent type to check for + /// + /// ## IC10 + /// `lr r? device(d?|r?|id) reagentMode int` + /// ## Slang + /// `let result = loadReagent(deviceHash, "ReagentMode", reagentHash);` + /// `let result = lr(deviceHash, "ReagentMode", reagentHash);` + LoadReagent( + Spanned>, + Spanned>, + Box>> ) } } @@ -261,6 +273,7 @@ impl<'a> std::fmt::Display for System<'a> { } System::LoadSlot(a, b, c) => write!(f, "loadSlot({}, {}, {})", a, b, c), System::SetSlot(a, b, c, d) => write!(f, "setSlot({}, {}, {}, {})", a, b, c, d), + System::LoadReagent(a, b, c) => write!(f, "loadReagent({}, {}, {})", a, b, c), } } }