1
0

Add tests for multiply instruction

This commit is contained in:
Jeff 2025-02-08 11:45:03 -05:00
parent 1bb87de70f
commit 6e9d5a49d2
4 changed files with 232 additions and 2 deletions

View File

@ -46,3 +46,7 @@ path = "tests/math/add.rs"
[[test]]
name = "subtract"
path = "tests/math/subtract.rs"
[[test]]
name = "multiply"
path = "tests/math/multiply.rs"

View File

@ -346,6 +346,13 @@ impl Instruction {
Operand::Register(destination, left.as_type())
}
Operation::MULTIPLY => {
let Multiply {
destination, left, ..
} = Multiply::from(*self);
Operand::Register(destination, left.as_type())
}
unsupported => todo!("Support {unsupported}"),
}
}

View File

@ -675,7 +675,7 @@ pub fn subtract(instruction: InstructionFields, thread: &mut Thread) {
thread.set_float_register(destination, register);
}
_ => unimplemented!(),
_ => unreachable!(),
}
}
@ -689,6 +689,54 @@ pub fn multiply(instruction: InstructionFields, thread: &mut Thread) {
let right_is_constant = instruction.c_is_constant;
match (left_type, right_type) {
(TypeCode::BYTE, TypeCode::BYTE) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_byte().unwrap()
} else {
unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_byte().unwrap()
} else {
unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(right)
};
let result = left_value.saturating_mul(*right_value);
let register = Register::Value(result);
thread.set_byte_register(destination, register);
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_float().unwrap()
} else {
unsafe { thread.get_constant(left).as_float().unwrap_unchecked() }
}
} else {
thread.get_float_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_float().unwrap()
} else {
unsafe { thread.get_constant(right).as_float().unwrap_unchecked() }
}
} else {
thread.get_float_register(right)
};
let result = left_value * right_value;
let register = Register::Value(result);
thread.set_float_register(destination, register);
}
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
@ -708,7 +756,7 @@ pub fn multiply(instruction: InstructionFields, thread: &mut Thread) {
} else {
thread.get_integer_register(right)
};
let result = left_value * right_value;
let result = left_value.saturating_mul(*right_value);
let register = Register::Value(result);
thread.set_integer_register(destination as usize, register);

View File

@ -0,0 +1,171 @@
use dust_lang::{
Chunk, ConcreteValue, FunctionType, Instruction, Operand, Span, Type, Value, compile,
instruction::TypeCode, run,
};
#[test]
fn multiply_bytes() {
let source = "0x0A * 0x02";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Byte),
instructions: vec![
Instruction::load_encoded(0, 10, TypeCode::BYTE, false),
Instruction::load_encoded(1, 2, TypeCode::BYTE, false),
Instruction::multiply(
2,
Operand::Register(0, TypeCode::BYTE),
Operand::Register(1, TypeCode::BYTE),
),
Instruction::r#return(true, 2, TypeCode::BYTE),
],
positions: vec![Span(0, 4), Span(7, 11), Span(0, 11), Span(11, 11)],
..Chunk::default()
};
let return_value = Some(Value::byte(0x14));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn multiply_many_bytes() {
let source = "0x0A * 0x02 * 0x02";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Byte),
instructions: vec![
Instruction::load_encoded(0, 10, TypeCode::BYTE, false),
Instruction::load_encoded(1, 2, TypeCode::BYTE, false),
Instruction::multiply(
2,
Operand::Register(0, TypeCode::BYTE),
Operand::Register(1, TypeCode::BYTE),
),
Instruction::load_encoded(3, 2, TypeCode::BYTE, false),
Instruction::multiply(
4,
Operand::Register(2, TypeCode::BYTE),
Operand::Register(3, TypeCode::BYTE),
),
Instruction::r#return(true, 4, TypeCode::BYTE),
],
positions: vec![
Span(0, 4),
Span(7, 11),
Span(0, 11),
Span(14, 18),
Span(0, 18),
Span(18, 18),
],
..Chunk::default()
};
let return_value = Some(Value::byte(0x28));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn multiply_floats() {
let source = "0.5 * 2.0";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Float),
instructions: vec![
Instruction::multiply(
0,
Operand::Constant(0, TypeCode::FLOAT),
Operand::Constant(1, TypeCode::FLOAT),
),
Instruction::r#return(true, 0, TypeCode::FLOAT),
],
positions: vec![Span(0, 9), Span(9, 9)],
constants: vec![ConcreteValue::Float(0.5), ConcreteValue::Float(2.0)],
..Chunk::default()
};
let return_value = Some(Value::float(1.0));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn multiply_many_floats() {
let source = "0.5 * 2.0 * 0.5";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Float),
instructions: vec![
Instruction::multiply(
0,
Operand::Constant(0, TypeCode::FLOAT),
Operand::Constant(1, TypeCode::FLOAT),
),
Instruction::multiply(
1,
Operand::Register(0, TypeCode::FLOAT),
Operand::Constant(0, TypeCode::FLOAT),
),
Instruction::r#return(true, 1, TypeCode::FLOAT),
],
positions: vec![Span(0, 9), Span(0, 15), Span(15, 15)],
constants: vec![ConcreteValue::Float(0.5), ConcreteValue::Float(2.0)],
..Chunk::default()
};
let return_value = Some(Value::float(0.5));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn multiply_integers() {
let source = "10 * 5";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Integer),
instructions: vec![
Instruction::multiply(
0,
Operand::Constant(0, TypeCode::INTEGER),
Operand::Constant(1, TypeCode::INTEGER),
),
Instruction::r#return(true, 0, TypeCode::INTEGER),
],
positions: vec![Span(0, 6), Span(6, 6)],
constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(5)],
..Chunk::default()
};
let return_value = Some(Value::integer(50));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn multiply_many_integers() {
let source = "10 * 5 * 2";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Integer),
instructions: vec![
Instruction::multiply(
0,
Operand::Constant(0, TypeCode::INTEGER),
Operand::Constant(1, TypeCode::INTEGER),
),
Instruction::multiply(
1,
Operand::Register(0, TypeCode::INTEGER),
Operand::Constant(2, TypeCode::INTEGER),
),
Instruction::r#return(true, 1, TypeCode::INTEGER),
],
positions: vec![Span(0, 6), Span(0, 10), Span(10, 10)],
constants: vec![
ConcreteValue::Integer(10),
ConcreteValue::Integer(5),
ConcreteValue::Integer(2),
],
..Chunk::default()
};
let return_value = Some(Value::integer(100));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}