A lot of work just to do subtraction.

This commit is contained in:
Jesse Brault 2026-03-09 19:22:35 -05:00
parent e35bacb583
commit ebca87ddb9
14 changed files with 307 additions and 83 deletions

View File

@ -155,7 +155,9 @@ impl Expression {
.add_statement(IrStatement::Assign(assign)); .add_statement(IrStatement::Assign(assign));
Some(IrExpression::Variable(as_rc)) 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!(), Expression::Negative(_) => todo!(),
} }
} }

View File

@ -114,7 +114,9 @@ impl LetStatement {
Expression::Add(additive_expression) => { Expression::Add(additive_expression) => {
IrOperation::Add(additive_expression.to_ir(builder, symbol_table)) 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!(), Expression::Negative(_) => todo!(),
}; };

View File

@ -1,8 +1,17 @@
use crate::ast::expression::Expression; use crate::ast::expression::Expression;
use crate::ast::ir_builder::IrBuilder;
use crate::diagnostic::Diagnostic; 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::source_range::SourceRange;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use std::cell::RefCell;
use std::rc::Rc;
pub struct SubtractExpression { pub struct SubtractExpression {
lhs: Box<Expression>, lhs: Box<Expression>,
@ -88,4 +97,39 @@ impl SubtractExpression {
pub fn source_range(&self) -> &SourceRange { pub fn source_range(&self) -> &SourceRange {
&self.source_range &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)
}
} }

View File

@ -97,6 +97,10 @@ impl Assemble for IrAssign {
let (left, right) = ir_add.operand_pair(constants_table); let (left, right) = ir_add.operand_pair(constants_table);
builder.push(Instruction::Add(left, right, destination)); 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) => { IrOperation::Call(ir_call) => {
ir_call.assemble(builder, constants_table); ir_call.assemble(builder, constants_table);
builder.push(Instruction::Pop(Some(destination))); builder.push(Instruction::Pop(Some(destination)));

View File

@ -5,7 +5,9 @@ use crate::ir::ir_variable::{
}; };
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use crate::type_info::TypeInfo; 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::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; 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 { pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand {
match self { match self {
IrExpression::Parameter(ir_parameter) => { IrExpression::Parameter(ir_parameter) => {

View File

@ -1,6 +1,7 @@
use crate::ir::ir_add::IrAdd; use crate::ir::ir_add::IrAdd;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_subtract::IrSubtract;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{OffsetCounter, VrUser}; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -9,6 +10,7 @@ use std::fmt::{Display, Formatter};
pub enum IrOperation { pub enum IrOperation {
Load(IrExpression), Load(IrExpression),
Add(IrAdd), Add(IrAdd),
Subtract(IrSubtract),
Call(IrCall), Call(IrCall),
} }
@ -21,6 +23,9 @@ impl Display for IrOperation {
IrOperation::Add(ir_add) => { IrOperation::Add(ir_add) => {
write!(f, "{}", ir_add) write!(f, "{}", ir_add)
} }
IrOperation::Subtract(ir_subtract) => {
write!(f, "{}", ir_subtract)
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
write!(f, "{}", ir_call) write!(f, "{}", ir_call)
} }
@ -33,6 +38,7 @@ impl VrUser for IrOperation {
match self { match self {
IrOperation::Load(ir_expression) => ir_expression.vr_definitions(), IrOperation::Load(ir_expression) => ir_expression.vr_definitions(),
IrOperation::Add(ir_add) => ir_add.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(), IrOperation::Call(ir_call) => ir_call.vr_definitions(),
} }
} }
@ -41,6 +47,7 @@ impl VrUser for IrOperation {
match self { match self {
IrOperation::Load(ir_expression) => ir_expression.vr_uses(), IrOperation::Load(ir_expression) => ir_expression.vr_uses(),
IrOperation::Add(ir_add) => ir_add.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(), IrOperation::Call(ir_call) => ir_call.vr_uses(),
} }
} }
@ -53,6 +60,9 @@ impl VrUser for IrOperation {
IrOperation::Add(ir_add) => { IrOperation::Add(ir_add) => {
ir_add.propagate_spills(spills); ir_add.propagate_spills(spills);
} }
IrOperation::Subtract(ir_subtract) => {
ir_subtract.propagate_spills(spills);
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
ir_call.propagate_spills(spills); ir_call.propagate_spills(spills);
} }
@ -70,6 +80,9 @@ impl VrUser for IrOperation {
IrOperation::Add(ir_add) => { IrOperation::Add(ir_add) => {
ir_add.propagate_register_assignments(assignments); ir_add.propagate_register_assignments(assignments);
} }
IrOperation::Subtract(ir_subtract) => {
ir_subtract.propagate_register_assignments(assignments);
}
IrOperation::Call(ir_call) => { IrOperation::Call(ir_call) => {
ir_call.propagate_register_assignments(assignments); ir_call.propagate_register_assignments(assignments);
} }

View File

@ -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<IrExpression>,
right: Box<IrExpression>,
}
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<IrVrVariableDescriptor> {
[&self.left, &self.right]
.iter()
.flat_map(|expression| expression.vr_definitions())
.collect()
}
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
[&self.left, &self.right]
.iter()
.flat_map(|expression| expression.vr_uses())
.collect()
}
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
self.left.propagate_spills(spills);
self.right.propagate_spills(spills);
}
fn propagate_register_assignments(
&mut self,
assignments: &HashMap<IrVrVariableDescriptor, usize>,
) {
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)
}
}

View File

@ -9,5 +9,6 @@ pub mod ir_operation;
pub mod ir_parameter; pub mod ir_parameter;
pub mod ir_return; pub mod ir_return;
pub mod ir_statement; pub mod ir_statement;
pub mod ir_subtract;
pub mod ir_variable; pub mod ir_variable;
mod register_allocation; mod register_allocation;

View File

@ -16,6 +16,7 @@ pub enum Instruction {
InvokePlatformStatic(FunctionName, ArgCount), InvokePlatformStatic(FunctionName, ArgCount),
Add(AddOperand, AddOperand, Location), Add(AddOperand, AddOperand, Location),
Subtract(SubtractOperand, SubtractOperand, Location),
Pop(Option<Location>), Pop(Option<Location>),
@ -50,6 +51,9 @@ impl Display for Instruction {
Instruction::Add(left, right, destination) => { Instruction::Add(left, right, destination) => {
write!(f, "add {}, {}, {}", left, right, destination) write!(f, "add {}, {}, {}", left, right, destination)
} }
Instruction::Subtract(left, right, destination) => {
write!(f, "sub {}, {}, {}", left, right, destination)
}
Instruction::SetReturnValue(source) => { Instruction::SetReturnValue(source) => {
write!(f, "srv {}", 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 { pub enum ReturnOperand {
Location(Location), Location(Location),
Int(i32), Int(i32),

View File

@ -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),
}
}

View File

@ -1,17 +1,19 @@
use crate::instruction::{ use crate::instruction::{AddOperand, Instruction, MoveOperand, PushOperand, ReturnOperand};
AddOperand, ConstantName, Instruction, Location, MoveOperand, PushOperand, ReturnOperand,
};
use crate::platform_function::PlatformFunction; use crate::platform_function::PlatformFunction;
use crate::vm::constant::Constant; use crate::vm::constant::Constant;
use crate::vm::function::Function; use crate::vm::function::Function;
use crate::vm::instruction_helpers::subtract_operand_to_value;
use crate::vm::value::Value; use crate::vm::value::Value;
use crate::vm::value_util::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub mod constant; pub mod constant;
pub mod function; pub mod function;
mod instruction_helpers;
pub mod object; pub mod object;
pub mod value; pub mod value;
mod value_util;
pub struct DvmContext { pub struct DvmContext {
functions: HashMap<Rc<str>, Function>, functions: HashMap<Rc<str>, 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) => &registers[*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<Rc<str>, 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<Value>,
stack: &mut Vec<Value>,
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>( pub fn call<'a>(
context: &'a DvmContext, context: &'a DvmContext,
registers: &mut Vec<Value>, registers: &mut Vec<Value>,
@ -277,14 +230,7 @@ pub fn call<'a>(
load_constant_value(context.constants(), constant_name) load_constant_value(context.constants(), constant_name)
} }
}; };
let fp = call_stack.top().fp(); put_value(registers, call_stack.top_mut(), destination, value);
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
destination,
value,
);
call_stack.top_mut().increment_ip(); 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(left) = left_value {
if let Value::Int(right) = right_value { if let Value::Int(right) = right_value {
let result = left + right; let result = left + right;
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::Int(result), Value::Int(result),
); );
} else if let Value::String(s) = right_value { } else if let Value::String(s) = right_value {
let result = format!("{}{}", left, s); let result = format!("{}{}", left, s);
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::String(result.into()), Value::String(result.into()),
); );
@ -425,21 +367,17 @@ pub fn call<'a>(
} else if let Value::String(left) = left_value { } else if let Value::String(left) = left_value {
if let Value::Int(right) = right_value { if let Value::Int(right) = right_value {
let result = format!("{}{}", left, right); let result = format!("{}{}", left, right);
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::String(result.into()), Value::String(result.into()),
); );
} else if let Value::String(right) = right_value { } else if let Value::String(right) = right_value {
let result = format!("{}{}", left, right); let result = format!("{}{}", left, right);
let fp = call_stack.top().fp();
put_value( put_value(
registers, registers,
call_stack.top_mut().stack_mut(), call_stack.top_mut(),
fp,
destination, destination,
Value::String(result.into()), Value::String(result.into()),
); );
@ -452,19 +390,21 @@ pub fn call<'a>(
call_stack.top_mut().increment_ip(); 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 */ /* Pop instructions */
Instruction::Pop(maybe_location) => { Instruction::Pop(maybe_location) => {
let value = call_stack.top_mut().stack_mut().pop().unwrap(); let value = call_stack.top_mut().stack_mut().pop().unwrap();
if let Some(location) = maybe_location { if let Some(location) = maybe_location {
let fp = call_stack.top().fp(); put_value(registers, call_stack.top_mut(), location, value);
put_value(
registers,
call_stack.top_mut().stack_mut(),
fp,
location,
value,
);
} }
call_stack.top_mut().increment_ip(); call_stack.top_mut().increment_ip();
} }

View File

@ -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) => &registers[*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<Rc<str>, 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<Value>,
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;
}
}
}

View File

@ -131,4 +131,9 @@ mod e2e_tests {
Value::String(Rc::from("Hello. 1 plus 2 is 3")), 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))
}
} }

16
examples/subtract.dm Normal file
View File

@ -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