From ebca87ddb961ba30fd9f1d08dfe1707ed6c6f1a7 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Mon, 9 Mar 2026 19:22:35 -0500 Subject: [PATCH] A lot of work just to do subtraction. --- dmc-lib/src/ast/expression.rs | 4 +- dmc-lib/src/ast/let_statement.rs | 4 +- dmc-lib/src/ast/subtract_expression.rs | 44 +++++++++++ dmc-lib/src/ir/ir_assign.rs | 4 + dmc-lib/src/ir/ir_expression.rs | 38 +++++++++- dmc-lib/src/ir/ir_operation.rs | 13 ++++ dmc-lib/src/ir/ir_subtract.rs | 63 ++++++++++++++++ dmc-lib/src/ir/mod.rs | 1 + dvm-lib/src/instruction.rs | 22 ++++++ dvm-lib/src/vm/instruction_helpers.rs | 21 ++++++ dvm-lib/src/vm/mod.rs | 100 +++++-------------------- dvm-lib/src/vm/value_util.rs | 55 ++++++++++++++ e2e-tests/src/lib.rs | 5 ++ examples/subtract.dm | 16 ++++ 14 files changed, 307 insertions(+), 83 deletions(-) create mode 100644 dmc-lib/src/ir/ir_subtract.rs create mode 100644 dvm-lib/src/vm/instruction_helpers.rs create mode 100644 dvm-lib/src/vm/value_util.rs create mode 100644 examples/subtract.dm diff --git a/dmc-lib/src/ast/expression.rs b/dmc-lib/src/ast/expression.rs index 73b489c..ac40cf1 100644 --- a/dmc-lib/src/ast/expression.rs +++ b/dmc-lib/src/ast/expression.rs @@ -155,7 +155,9 @@ impl Expression { .add_statement(IrStatement::Assign(assign)); Some(IrExpression::Variable(as_rc)) } - Expression::Subtract(subtract_expression) => todo!(), + Expression::Subtract(subtract_expression) => { + Some(subtract_expression.to_ir_expression(builder, symbol_table)) + } Expression::Negative(_) => todo!(), } } diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index 1bd9731..fc88df5 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -114,7 +114,9 @@ impl LetStatement { Expression::Add(additive_expression) => { IrOperation::Add(additive_expression.to_ir(builder, symbol_table)) } - Expression::Subtract(subtract_expression) => todo!(), + Expression::Subtract(subtract_expression) => { + IrOperation::Subtract(subtract_expression.to_ir_subtract(builder, symbol_table)) + } Expression::Negative(_) => todo!(), }; diff --git a/dmc-lib/src/ast/subtract_expression.rs b/dmc-lib/src/ast/subtract_expression.rs index bfd6e16..cc3dd03 100644 --- a/dmc-lib/src/ast/subtract_expression.rs +++ b/dmc-lib/src/ast/subtract_expression.rs @@ -1,8 +1,17 @@ use crate::ast::expression::Expression; +use crate::ast::ir_builder::IrBuilder; use crate::diagnostic::Diagnostic; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_operation::IrOperation; +use crate::ir::ir_statement::IrStatement; +use crate::ir::ir_subtract::IrSubtract; +use crate::ir::ir_variable::IrVariable; use crate::source_range::SourceRange; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; +use std::cell::RefCell; +use std::rc::Rc; pub struct SubtractExpression { lhs: Box, @@ -88,4 +97,39 @@ impl SubtractExpression { pub fn source_range(&self) -> &SourceRange { &self.source_range } + + pub fn to_ir_subtract( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + ) -> IrSubtract { + let lhs = self + .lhs + .to_ir(builder, symbol_table) + .expect("Attempt to subtract non-expression"); + let rhs = self + .rhs + .to_ir(builder, symbol_table) + .expect("Attempt to subtract non-expression"); + IrSubtract::new(lhs, rhs) + } + + pub fn to_ir_expression( + &self, + builder: &mut IrBuilder, + symbol_table: &SymbolTable, + ) -> IrExpression { + let ir_subtract = self.to_ir_subtract(builder, symbol_table); + let t_var = IrVariable::new_vr( + builder.new_t_var().into(), + builder.current_block().id(), + self.type_info(), + ); + let as_rc = Rc::new(RefCell::new(t_var)); + let assign = IrAssign::new(as_rc.clone(), IrOperation::Subtract(ir_subtract)); + builder + .current_block_mut() + .add_statement(IrStatement::Assign(assign)); + IrExpression::Variable(as_rc) + } } diff --git a/dmc-lib/src/ir/ir_assign.rs b/dmc-lib/src/ir/ir_assign.rs index 108b548..d131fde 100644 --- a/dmc-lib/src/ir/ir_assign.rs +++ b/dmc-lib/src/ir/ir_assign.rs @@ -97,6 +97,10 @@ impl Assemble for IrAssign { let (left, right) = ir_add.operand_pair(constants_table); builder.push(Instruction::Add(left, right, destination)); } + IrOperation::Subtract(ir_subtract) => { + let (left, right) = ir_subtract.operand_pair(); + builder.push(Instruction::Subtract(left, right, destination)); + } IrOperation::Call(ir_call) => { ir_call.assemble(builder, constants_table); builder.push(Instruction::Pop(Some(destination))); diff --git a/dmc-lib/src/ir/ir_expression.rs b/dmc-lib/src/ir/ir_expression.rs index a9bf074..816c7fc 100644 --- a/dmc-lib/src/ir/ir_expression.rs +++ b/dmc-lib/src/ir/ir_expression.rs @@ -5,7 +5,9 @@ use crate::ir::ir_variable::{ }; use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::type_info::TypeInfo; -use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand}; +use dvm_lib::instruction::{ + AddOperand, Location, MoveOperand, PushOperand, ReturnOperand, SubtractOperand, +}; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; @@ -105,6 +107,40 @@ impl IrExpression { } } + pub fn subtract_operand(&self) -> SubtractOperand { + match self { + IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { + TypeInfo::Integer => SubtractOperand::Location(Location::StackFrameOffset( + ir_parameter.stack_offset(), + )), + _ => panic!( + "Attempt to subtract with non-integer type (found {})", + ir_parameter.type_info() + ), + }, + IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() { + TypeInfo::Integer => match ir_variable.borrow().descriptor() { + IrVariableDescriptor::VirtualRegister(vr_variable) => { + SubtractOperand::Location(Location::Register( + vr_variable.assigned_register(), + )) + } + IrVariableDescriptor::Stack(stack_variable) => SubtractOperand::Location( + Location::StackFrameOffset(stack_variable.offset()), + ), + }, + _ => panic!( + "Attempt to subtract with non-integer type (found {})", + ir_variable.borrow().type_info() + ), + }, + IrExpression::Int(i) => SubtractOperand::Int(*i), + IrExpression::String(_) => { + panic!("Attempt to subtract with a string"); + } + } + } + pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand { match self { IrExpression::Parameter(ir_parameter) => { diff --git a/dmc-lib/src/ir/ir_operation.rs b/dmc-lib/src/ir/ir_operation.rs index 1d562f7..58c0e01 100644 --- a/dmc-lib/src/ir/ir_operation.rs +++ b/dmc-lib/src/ir/ir_operation.rs @@ -1,6 +1,7 @@ use crate::ir::ir_add::IrAdd; use crate::ir::ir_call::IrCall; use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_subtract::IrSubtract; use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::register_allocation::{OffsetCounter, VrUser}; use std::collections::{HashMap, HashSet}; @@ -9,6 +10,7 @@ use std::fmt::{Display, Formatter}; pub enum IrOperation { Load(IrExpression), Add(IrAdd), + Subtract(IrSubtract), Call(IrCall), } @@ -21,6 +23,9 @@ impl Display for IrOperation { IrOperation::Add(ir_add) => { write!(f, "{}", ir_add) } + IrOperation::Subtract(ir_subtract) => { + write!(f, "{}", ir_subtract) + } IrOperation::Call(ir_call) => { write!(f, "{}", ir_call) } @@ -33,6 +38,7 @@ impl VrUser for IrOperation { match self { IrOperation::Load(ir_expression) => ir_expression.vr_definitions(), IrOperation::Add(ir_add) => ir_add.vr_definitions(), + IrOperation::Subtract(ir_subtract) => ir_subtract.vr_definitions(), IrOperation::Call(ir_call) => ir_call.vr_definitions(), } } @@ -41,6 +47,7 @@ impl VrUser for IrOperation { match self { IrOperation::Load(ir_expression) => ir_expression.vr_uses(), IrOperation::Add(ir_add) => ir_add.vr_uses(), + IrOperation::Subtract(ir_subtract) => ir_subtract.vr_uses(), IrOperation::Call(ir_call) => ir_call.vr_uses(), } } @@ -53,6 +60,9 @@ impl VrUser for IrOperation { IrOperation::Add(ir_add) => { ir_add.propagate_spills(spills); } + IrOperation::Subtract(ir_subtract) => { + ir_subtract.propagate_spills(spills); + } IrOperation::Call(ir_call) => { ir_call.propagate_spills(spills); } @@ -70,6 +80,9 @@ impl VrUser for IrOperation { IrOperation::Add(ir_add) => { ir_add.propagate_register_assignments(assignments); } + IrOperation::Subtract(ir_subtract) => { + ir_subtract.propagate_register_assignments(assignments); + } IrOperation::Call(ir_call) => { ir_call.propagate_register_assignments(assignments); } diff --git a/dmc-lib/src/ir/ir_subtract.rs b/dmc-lib/src/ir/ir_subtract.rs new file mode 100644 index 0000000..e505c11 --- /dev/null +++ b/dmc-lib/src/ir/ir_subtract.rs @@ -0,0 +1,63 @@ +use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::register_allocation::{OffsetCounter, VrUser}; +use dvm_lib::instruction::SubtractOperand; +use std::collections::{HashMap, HashSet}; +use std::fmt::{Display, Formatter}; + +pub struct IrSubtract { + left: Box, + right: Box, +} + +impl IrSubtract { + pub fn new(left: IrExpression, right: IrExpression) -> Self { + Self { + left: left.into(), + right: right.into(), + } + } + + pub fn operand_pair(&self) -> (SubtractOperand, SubtractOperand) { + (self.left.subtract_operand(), self.right.subtract_operand()) + } +} + +impl VrUser for IrSubtract { + fn vr_definitions(&self) -> HashSet { + [&self.left, &self.right] + .iter() + .flat_map(|expression| expression.vr_definitions()) + .collect() + } + + fn vr_uses(&self) -> HashSet { + [&self.left, &self.right] + .iter() + .flat_map(|expression| expression.vr_uses()) + .collect() + } + + fn propagate_spills(&mut self, spills: &HashSet) { + self.left.propagate_spills(spills); + self.right.propagate_spills(spills); + } + + fn propagate_register_assignments( + &mut self, + assignments: &HashMap, + ) { + self.left.propagate_register_assignments(assignments); + self.right.propagate_register_assignments(assignments); + } + + fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) { + // no-op + } +} + +impl Display for IrSubtract { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{} - {}", self.left, self.right) + } +} diff --git a/dmc-lib/src/ir/mod.rs b/dmc-lib/src/ir/mod.rs index 006ba16..ab76595 100644 --- a/dmc-lib/src/ir/mod.rs +++ b/dmc-lib/src/ir/mod.rs @@ -9,5 +9,6 @@ pub mod ir_operation; pub mod ir_parameter; pub mod ir_return; pub mod ir_statement; +pub mod ir_subtract; pub mod ir_variable; mod register_allocation; diff --git a/dvm-lib/src/instruction.rs b/dvm-lib/src/instruction.rs index 012aff4..47fa44d 100644 --- a/dvm-lib/src/instruction.rs +++ b/dvm-lib/src/instruction.rs @@ -16,6 +16,7 @@ pub enum Instruction { InvokePlatformStatic(FunctionName, ArgCount), Add(AddOperand, AddOperand, Location), + Subtract(SubtractOperand, SubtractOperand, Location), Pop(Option), @@ -50,6 +51,9 @@ impl Display for Instruction { Instruction::Add(left, right, destination) => { write!(f, "add {}, {}, {}", left, right, destination) } + Instruction::Subtract(left, right, destination) => { + write!(f, "sub {}, {}, {}", left, right, destination) + } Instruction::SetReturnValue(source) => { write!(f, "srv {}", source) } @@ -148,6 +152,24 @@ impl Display for AddOperand { } } +pub enum SubtractOperand { + Location(Location), + Int(i32), +} + +impl Display for SubtractOperand { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + SubtractOperand::Location(location) => { + write!(f, "{}", location) + } + SubtractOperand::Int(i) => { + write!(f, "{}", i) + } + } + } +} + pub enum ReturnOperand { Location(Location), Int(i32), diff --git a/dvm-lib/src/vm/instruction_helpers.rs b/dvm-lib/src/vm/instruction_helpers.rs new file mode 100644 index 0000000..ce1acab --- /dev/null +++ b/dvm-lib/src/vm/instruction_helpers.rs @@ -0,0 +1,21 @@ +use crate::instruction::SubtractOperand; +use crate::vm::CallFrame; +use crate::vm::value::Value; +use crate::vm::value_util::load_value; + +pub fn subtract_operand_to_value( + subtract_operand: &SubtractOperand, + registers: &[Value], + current_frame: &CallFrame, +) -> Value { + match subtract_operand { + SubtractOperand::Location(location) => load_value( + registers, + current_frame.stack(), + current_frame.fp(), + location, + ) + .clone(), + SubtractOperand::Int(i) => Value::Int(*i), + } +} diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index 6b2dace..a353bca 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -1,17 +1,19 @@ -use crate::instruction::{ - AddOperand, ConstantName, Instruction, Location, MoveOperand, PushOperand, ReturnOperand, -}; +use crate::instruction::{AddOperand, Instruction, MoveOperand, PushOperand, ReturnOperand}; use crate::platform_function::PlatformFunction; use crate::vm::constant::Constant; use crate::vm::function::Function; +use crate::vm::instruction_helpers::subtract_operand_to_value; use crate::vm::value::Value; +use crate::vm::value_util::*; use std::collections::HashMap; use std::rc::Rc; pub mod constant; pub mod function; +mod instruction_helpers; pub mod object; pub mod value; +mod value_util; pub struct DvmContext { functions: HashMap, Function>, @@ -165,55 +167,6 @@ impl<'a> CallStack<'a> { } } -fn load_value<'a>( - registers: &'a [Value], - stack: &'a [Value], - fp: usize, - location: &Location, -) -> &'a Value { - match location { - Location::Register(register) => ®isters[*register], - Location::StackFrameOffset(offset) => { - let value_index = fp.checked_add_signed(*offset).expect(&format!( - "Overflow when adding offset {} to fp {}", - *offset, fp - )); - &stack[value_index] - } - } -} - -fn load_constant_value( - constants: &HashMap, Constant>, - constant_name: &ConstantName, -) -> Value { - let constant = &constants[constant_name]; - match constant { - Constant::String(string_constant) => Value::String(string_constant.content_owned()), - } -} - -fn put_value( - registers: &mut Vec, - stack: &mut Vec, - fp: usize, - destination: &Location, - value: Value, -) { - match destination { - Location::Register(register) => { - registers[*register] = value; - } - Location::StackFrameOffset(offset) => { - let target_index = fp.checked_add_signed(*offset).expect(&format!( - "Failed to calculate target index from fp + offset ({} + {})", - fp, offset - )); - stack[target_index] = value; - } - } -} - pub fn call<'a>( context: &'a DvmContext, registers: &mut Vec, @@ -277,14 +230,7 @@ pub fn call<'a>( load_constant_value(context.constants(), constant_name) } }; - let fp = call_stack.top().fp(); - put_value( - registers, - call_stack.top_mut().stack_mut(), - fp, - destination, - value, - ); + put_value(registers, call_stack.top_mut(), destination, value); call_stack.top_mut().increment_ip(); } @@ -401,21 +347,17 @@ pub fn call<'a>( if let Value::Int(left) = left_value { if let Value::Int(right) = right_value { let result = left + right; - let fp = call_stack.top().fp(); put_value( registers, - call_stack.top_mut().stack_mut(), - fp, + call_stack.top_mut(), destination, Value::Int(result), ); } else if let Value::String(s) = right_value { let result = format!("{}{}", left, s); - let fp = call_stack.top().fp(); put_value( registers, - call_stack.top_mut().stack_mut(), - fp, + call_stack.top_mut(), destination, Value::String(result.into()), ); @@ -425,21 +367,17 @@ pub fn call<'a>( } else if let Value::String(left) = left_value { if let Value::Int(right) = right_value { let result = format!("{}{}", left, right); - let fp = call_stack.top().fp(); put_value( registers, - call_stack.top_mut().stack_mut(), - fp, + call_stack.top_mut(), destination, Value::String(result.into()), ); } else if let Value::String(right) = right_value { let result = format!("{}{}", left, right); - let fp = call_stack.top().fp(); put_value( registers, - call_stack.top_mut().stack_mut(), - fp, + call_stack.top_mut(), destination, Value::String(result.into()), ); @@ -452,19 +390,21 @@ pub fn call<'a>( call_stack.top_mut().increment_ip(); } + Instruction::Subtract(left_operand, right_operand, destination) => { + let left = subtract_operand_to_value(left_operand, registers, call_stack.top()) + .unwrap_int(); + let right = subtract_operand_to_value(right_operand, registers, call_stack.top()) + .unwrap_int(); + let result = Value::Int(left - right); + put_value(registers, call_stack.top_mut(), destination, result); + call_stack.top_mut().increment_ip(); + } /* Pop instructions */ Instruction::Pop(maybe_location) => { let value = call_stack.top_mut().stack_mut().pop().unwrap(); if let Some(location) = maybe_location { - let fp = call_stack.top().fp(); - put_value( - registers, - call_stack.top_mut().stack_mut(), - fp, - location, - value, - ); + put_value(registers, call_stack.top_mut(), location, value); } call_stack.top_mut().increment_ip(); } diff --git a/dvm-lib/src/vm/value_util.rs b/dvm-lib/src/vm/value_util.rs new file mode 100644 index 0000000..752e5bd --- /dev/null +++ b/dvm-lib/src/vm/value_util.rs @@ -0,0 +1,55 @@ +use crate::instruction::{ConstantName, Location}; +use crate::vm::CallFrame; +use crate::vm::constant::Constant; +use crate::vm::value::Value; +use std::collections::HashMap; +use std::rc::Rc; + +pub fn load_value<'a>( + registers: &'a [Value], + stack: &'a [Value], + fp: usize, + location: &Location, +) -> &'a Value { + match location { + Location::Register(register) => ®isters[*register], + Location::StackFrameOffset(offset) => { + let value_index = fp.checked_add_signed(*offset).expect(&format!( + "Overflow when adding offset {} to fp {}", + *offset, fp + )); + &stack[value_index] + } + } +} + +pub fn load_constant_value( + constants: &HashMap, Constant>, + constant_name: &ConstantName, +) -> Value { + let constant = &constants[constant_name]; + match constant { + Constant::String(string_constant) => Value::String(string_constant.content_owned()), + } +} + +pub fn put_value( + registers: &mut Vec, + current_frame: &mut CallFrame, + destination: &Location, + value: Value, +) { + match destination { + Location::Register(register) => { + registers[*register] = value; + } + Location::StackFrameOffset(offset) => { + let fp = current_frame.fp(); + let target_index = fp.checked_add_signed(*offset).expect(&format!( + "Failed to calculate target index from fp + offset ({} + {})", + fp, offset + )); + current_frame.stack_mut()[target_index] = value; + } + } +} diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index 69fc199..ba3cec5 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -131,4 +131,9 @@ mod e2e_tests { Value::String(Rc::from("Hello. 1 plus 2 is 3")), ); } + + #[test] + fn simple_subtract() { + assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1)) + } } diff --git a/examples/subtract.dm b/examples/subtract.dm new file mode 100644 index 0000000..9f924a6 --- /dev/null +++ b/examples/subtract.dm @@ -0,0 +1,16 @@ +extern fn println(message: Any) -> Void + +fn add(a: Int, b: Int) -> Int + a + b +end + +fn subtract(a: Int, b: Int) -> Int + a - b +end + +fn main() + let a = 42 + let b = 42 + println(add(a, b)) + println(subtract(a, b)) +end