Bitwise folding with hash() as well
This commit is contained in:
@@ -67,6 +67,9 @@ pub enum Error<'a> {
|
|||||||
#[error("Expected a {0}-tuple, but you're trying to destructure into {1} variables")]
|
#[error("Expected a {0}-tuple, but you're trying to destructure into {1} variables")]
|
||||||
TupleSizeMismatch(usize, usize, Span),
|
TupleSizeMismatch(usize, usize, Span),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
OperationNotSupported(String, Span),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Unknown(String, Option<Span>),
|
Unknown(String, Option<Span>),
|
||||||
}
|
}
|
||||||
@@ -89,7 +92,8 @@ impl<'a> From<Error<'a>> for lsp_types::Diagnostic {
|
|||||||
| ConstAssignment(_, span)
|
| ConstAssignment(_, span)
|
||||||
| DeviceAssignment(_, span)
|
| DeviceAssignment(_, span)
|
||||||
| AgrumentMismatch(_, span)
|
| AgrumentMismatch(_, span)
|
||||||
| TupleSizeMismatch(_, _, span) => Diagnostic {
|
| TupleSizeMismatch(_, _, span)
|
||||||
|
| OperationNotSupported(_, span) => Diagnostic {
|
||||||
range: span.into(),
|
range: span.into(),
|
||||||
message: value.to_string(),
|
message: value.to_string(),
|
||||||
severity: Some(DiagnosticSeverity::ERROR),
|
severity: Some(DiagnosticSeverity::ERROR),
|
||||||
@@ -497,9 +501,9 @@ impl<'a> Compiler<'a> {
|
|||||||
// Check if device is "db" (not allowed)
|
// Check if device is "db" (not allowed)
|
||||||
if let Operand::Device(ref dev_str) = device {
|
if let Operand::Device(ref dev_str) = device {
|
||||||
if dev_str.as_ref() == "db" {
|
if dev_str.as_ref() == "db" {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::OperationNotSupported(
|
||||||
"Direct stack access on 'db' is not yet supported".to_string(),
|
"Direct stack access on 'db' is not yet supported".to_string(),
|
||||||
Some(expr.span),
|
expr.span,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1152,9 +1156,9 @@ impl<'a> Compiler<'a> {
|
|||||||
// Check if device is "db" (not allowed)
|
// Check if device is "db" (not allowed)
|
||||||
if let Operand::Device(ref dev_str) = device {
|
if let Operand::Device(ref dev_str) = device {
|
||||||
if dev_str.as_ref() == "db" {
|
if dev_str.as_ref() == "db" {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::OperationNotSupported(
|
||||||
"Direct stack access on 'db' is not yet supported".to_string(),
|
"Direct stack access on 'db' is not yet supported".to_string(),
|
||||||
Some(assignee.span),
|
assignee.span,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2355,7 +2359,22 @@ impl<'a> Compiler<'a> {
|
|||||||
// 4. Handle Binary Ops: Recurse BOTH sides, then combine
|
// 4. Handle Binary Ops: Recurse BOTH sides, then combine
|
||||||
Expression::Binary(bin) => fold_binary_expression(&bin.node),
|
Expression::Binary(bin) => fold_binary_expression(&bin.node),
|
||||||
|
|
||||||
// 5. Anything else (Variables, Function Calls) cannot be compile-time folded
|
// 5. Handle hash() macro - evaluates to a constant at compile time
|
||||||
|
Expression::Invocation(inv) => {
|
||||||
|
if inv.node.name.node == "hash" && inv.node.arguments.len() == 1 {
|
||||||
|
if let Expression::Literal(Spanned {
|
||||||
|
node: Literal::String(str_to_hash),
|
||||||
|
..
|
||||||
|
}) = &inv.node.arguments[0].node
|
||||||
|
{
|
||||||
|
// hash() takes a string literal and returns a signed integer
|
||||||
|
return Some(Number::Integer(crc_hash_signed(str_to_hash), Unit::None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Anything else cannot be compile-time folded
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod device_indexing_tests {
|
||||||
|
use crate::common::compile_with_and_without_optimization;
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_indexing_with_hash_and_binary_literals() {
|
||||||
|
let source = indoc! {"
|
||||||
|
device printer = \"d0\";
|
||||||
|
|
||||||
|
let item_type = hash(\"ItemIronIngot\");
|
||||||
|
let quality = 16;
|
||||||
|
let quantity = 7;
|
||||||
|
|
||||||
|
// Pack into a single value using bitwise operations
|
||||||
|
// Format: (itemHash << 16) | (quality << 8) | quantity
|
||||||
|
let packed = (item_type << 16) | (quality << 8) | quantity;
|
||||||
|
|
||||||
|
// Write to device stack at address 255
|
||||||
|
let addr = 255;
|
||||||
|
printer[addr] = packed;
|
||||||
|
|
||||||
|
// Read it back
|
||||||
|
let result = printer[addr];
|
||||||
|
"};
|
||||||
|
let output = compile_with_and_without_optimization(source);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_indexing_with_computed_index() {
|
||||||
|
let source = indoc! {"
|
||||||
|
device storage = \"d1\";
|
||||||
|
|
||||||
|
let base_addr = 10;
|
||||||
|
let offset = 5;
|
||||||
|
let index = base_addr + offset;
|
||||||
|
|
||||||
|
let value = 42;
|
||||||
|
storage[index] = value;
|
||||||
|
|
||||||
|
let retrieved = storage[index];
|
||||||
|
"};
|
||||||
|
let output = compile_with_and_without_optimization(source);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_indexing_with_binary_literals() {
|
||||||
|
let source = indoc! {"
|
||||||
|
device mem = \"d0\";
|
||||||
|
|
||||||
|
// Binary literals for bitwise operations
|
||||||
|
let flags = 0b1010_0101;
|
||||||
|
let mask = 0b1111_0000;
|
||||||
|
let masked = flags & mask;
|
||||||
|
|
||||||
|
// Write to device
|
||||||
|
mem[0] = masked;
|
||||||
|
|
||||||
|
// Read back
|
||||||
|
let read_value = mem[0];
|
||||||
|
"};
|
||||||
|
let output = compile_with_and_without_optimization(source);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_indexing_complex_expression() {
|
||||||
|
let source = indoc! {"
|
||||||
|
device db = \"d0\";
|
||||||
|
|
||||||
|
let item = hash(\"ItemCopper\");
|
||||||
|
let quality = 5;
|
||||||
|
let quantity = 100;
|
||||||
|
|
||||||
|
// Complex bitwise expression
|
||||||
|
let packed = (item << 16) | ((quality & 0xFF) << 8) | (quantity & 0xFF);
|
||||||
|
|
||||||
|
// Index with computed address
|
||||||
|
let slot = 1;
|
||||||
|
let address = slot * 256 + 100;
|
||||||
|
db[address] = packed;
|
||||||
|
|
||||||
|
// Read back with different computation
|
||||||
|
let read_addr = (slot + 0) * 256 + 100;
|
||||||
|
let stored_value = db[read_addr];
|
||||||
|
"};
|
||||||
|
let output = compile_with_and_without_optimization(source);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_device_indexing_optimization_folds_constants() {
|
||||||
|
let source = indoc! {"
|
||||||
|
device cache = \"d0\";
|
||||||
|
|
||||||
|
// This should optimize to a single constant
|
||||||
|
let packed_constant = (hash(\"ItemSilver\") << 16) | (10 << 8) | 50;
|
||||||
|
|
||||||
|
cache[255] = packed_constant;
|
||||||
|
let result = cache[255];
|
||||||
|
"};
|
||||||
|
let output = compile_with_and_without_optimization(source);
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ mod bitwise_tests;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod common;
|
mod common;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
mod device_indexing_tests;
|
||||||
|
#[cfg(test)]
|
||||||
mod function_tests;
|
mod function_tests;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod number_literal_tests;
|
mod number_literal_tests;
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ move r13 r4
|
|||||||
|
|
||||||
move r8 5
|
move r8 5
|
||||||
move r9 3
|
move r9 3
|
||||||
and r10 r8 r9
|
move r1 1
|
||||||
or r11 r8 r9
|
move r10 1
|
||||||
xor r12 r8 r9
|
move r2 7
|
||||||
|
move r11 7
|
||||||
|
move r3 6
|
||||||
|
move r12 6
|
||||||
not r4 r8
|
not r4 r8
|
||||||
move r13 r4
|
move r13 r4
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ move r11 r3
|
|||||||
## Optimized Output
|
## Optimized Output
|
||||||
|
|
||||||
move r8 8
|
move r8 8
|
||||||
sll r1 r8 2
|
move r1 32
|
||||||
move r9 r1
|
move r9 32
|
||||||
sra r2 r8 1
|
move r2 4
|
||||||
move r10 r2
|
move r10 4
|
||||||
srl r3 r8 1
|
move r3 4
|
||||||
move r11 r3
|
move r11 4
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: libs/integration_tests/src/device_indexing_tests.rs
|
||||||
|
assertion_line: 90
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
## Unoptimized Output
|
||||||
|
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 r15
|
||||||
|
move r9 5
|
||||||
|
move r10 100
|
||||||
|
sll r1 r8 16
|
||||||
|
and r2 r9 255
|
||||||
|
sll r3 r2 8
|
||||||
|
or r4 r1 r3
|
||||||
|
and r5 r10 255
|
||||||
|
or r6 r4 r5
|
||||||
|
move r11 r6
|
||||||
|
move r12 1
|
||||||
|
mul r7 r12 256
|
||||||
|
add r2 r7 100
|
||||||
|
move r13 r2
|
||||||
|
put d0 r13 r11
|
||||||
|
add r1 r12 0
|
||||||
|
mul r3 r1 256
|
||||||
|
add r4 r3 100
|
||||||
|
move r14 r4
|
||||||
|
get r5 d0 r14
|
||||||
|
push r5
|
||||||
|
sub sp sp 1
|
||||||
|
|
||||||
|
## Optimized Output
|
||||||
|
|
||||||
|
move r8 r15
|
||||||
|
move r9 5
|
||||||
|
move r10 100
|
||||||
|
sll r1 r8 16
|
||||||
|
move r3 1280
|
||||||
|
or r4 r1 r3
|
||||||
|
move r5 100
|
||||||
|
or r11 r4 r5
|
||||||
|
move r12 1
|
||||||
|
move r7 256
|
||||||
|
move r2 356
|
||||||
|
move r13 356
|
||||||
|
put d0 r13 r11
|
||||||
|
move r1 1
|
||||||
|
move r3 256
|
||||||
|
move r4 356
|
||||||
|
move r14 356
|
||||||
|
get r5 d0 r14
|
||||||
|
push r5
|
||||||
|
sub sp sp 1
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
source: libs/integration_tests/src/device_indexing_tests.rs
|
||||||
|
assertion_line: 105
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
## Unoptimized Output
|
||||||
|
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
sll r1 -952768015 16
|
||||||
|
or r2 r1 2560
|
||||||
|
or r3 r2 50
|
||||||
|
move r8 r3
|
||||||
|
put d0 255 r8
|
||||||
|
get r4 d0 255
|
||||||
|
move r9 r4
|
||||||
|
|
||||||
|
## Optimized Output
|
||||||
|
|
||||||
|
move r1 -62440604631040
|
||||||
|
move r2 -62440604628480
|
||||||
|
move r3 -62440604628430
|
||||||
|
move r8 -62440604628430
|
||||||
|
put d0 255 r8
|
||||||
|
get r9 d0 255
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
source: libs/integration_tests/src/device_indexing_tests.rs
|
||||||
|
assertion_line: 65
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
## Unoptimized Output
|
||||||
|
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 165
|
||||||
|
move r9 240
|
||||||
|
and r1 r8 r9
|
||||||
|
move r10 r1
|
||||||
|
put d0 0 r10
|
||||||
|
get r2 d0 0
|
||||||
|
move r11 r2
|
||||||
|
|
||||||
|
## Optimized Output
|
||||||
|
|
||||||
|
move r8 165
|
||||||
|
move r9 240
|
||||||
|
move r1 160
|
||||||
|
move r10 160
|
||||||
|
put d0 0 r10
|
||||||
|
get r11 d0 0
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
source: libs/integration_tests/src/device_indexing_tests.rs
|
||||||
|
assertion_line: 45
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
## Unoptimized Output
|
||||||
|
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 10
|
||||||
|
move r9 5
|
||||||
|
add r1 r8 r9
|
||||||
|
move r10 r1
|
||||||
|
move r11 42
|
||||||
|
put d1 r10 r11
|
||||||
|
get r2 d1 r10
|
||||||
|
move r12 r2
|
||||||
|
|
||||||
|
## Optimized Output
|
||||||
|
|
||||||
|
move r8 10
|
||||||
|
move r9 5
|
||||||
|
move r1 15
|
||||||
|
move r10 15
|
||||||
|
move r11 42
|
||||||
|
put d1 r10 r11
|
||||||
|
get r12 d1 r10
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
source: libs/integration_tests/src/device_indexing_tests.rs
|
||||||
|
assertion_line: 27
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
## Unoptimized Output
|
||||||
|
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 r15
|
||||||
|
move r9 16
|
||||||
|
move r10 7
|
||||||
|
sll r1 r8 16
|
||||||
|
sll r2 r9 8
|
||||||
|
or r3 r1 r2
|
||||||
|
or r4 r3 r10
|
||||||
|
move r11 r4
|
||||||
|
move r12 255
|
||||||
|
put d0 r12 r11
|
||||||
|
get r5 d0 r12
|
||||||
|
move r13 r5
|
||||||
|
|
||||||
|
## Optimized Output
|
||||||
|
|
||||||
|
move r8 r15
|
||||||
|
move r9 16
|
||||||
|
move r10 7
|
||||||
|
sll r1 r8 16
|
||||||
|
move r2 4096
|
||||||
|
or r3 r1 r2
|
||||||
|
or r11 r3 r10
|
||||||
|
move r12 255
|
||||||
|
put d0 r12 r11
|
||||||
|
get r13 d0 r12
|
||||||
@@ -31,6 +31,26 @@ pub fn constant_propagation<'a>(
|
|||||||
Instruction::Mod(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| {
|
Instruction::Mod(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| {
|
||||||
if y.is_zero() { Decimal::ZERO } else { x % y }
|
if y.is_zero() { Decimal::ZERO } else { x % y }
|
||||||
}),
|
}),
|
||||||
|
Instruction::And(dst, a, b) => try_fold_bitwise(dst, a, b, ®isters, |x, y| x & y),
|
||||||
|
Instruction::Or(dst, a, b) => try_fold_bitwise(dst, a, b, ®isters, |x, y| x | y),
|
||||||
|
Instruction::Xor(dst, a, b) => try_fold_bitwise(dst, a, b, ®isters, |x, y| x ^ y),
|
||||||
|
Instruction::Sll(dst, a, b) => try_fold_bitwise(dst, a, b, ®isters, |x, y| {
|
||||||
|
if y >= 64 { 0 } else { x << y as u32 }
|
||||||
|
}),
|
||||||
|
Instruction::Sra(dst, a, b) => try_fold_bitwise(dst, a, b, ®isters, |x, y| {
|
||||||
|
if y >= 64 {
|
||||||
|
(if x < 0 { -1 } else { 0 })
|
||||||
|
} else {
|
||||||
|
x >> y as u32
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Instruction::Srl(dst, a, b) => try_fold_bitwise(dst, a, b, ®isters, |x, y| {
|
||||||
|
if y >= 64 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(x as u64 >> y as u32) as i64
|
||||||
|
}
|
||||||
|
}),
|
||||||
Instruction::BranchEq(a, b, l) => {
|
Instruction::BranchEq(a, b, l) => {
|
||||||
try_resolve_branch(a, b, l, ®isters, |x, y| x == y)
|
try_resolve_branch(a, b, l, ®isters, |x, y| x == y)
|
||||||
}
|
}
|
||||||
@@ -110,6 +130,43 @@ where
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decimal_to_i64(d: Decimal) -> i64 {
|
||||||
|
// Convert decimal to i64, truncating if needed
|
||||||
|
if let Ok(int_val) = d.try_into() {
|
||||||
|
int_val
|
||||||
|
} else {
|
||||||
|
// For very large or very small values, use a default
|
||||||
|
if d.is_sign_negative() {
|
||||||
|
i64::MIN
|
||||||
|
} else {
|
||||||
|
i64::MAX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn i64_to_decimal(i: i64) -> Decimal {
|
||||||
|
Decimal::from(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_fold_bitwise<'a, F>(
|
||||||
|
dst: &Operand<'a>,
|
||||||
|
a: &Operand<'a>,
|
||||||
|
b: &Operand<'a>,
|
||||||
|
regs: &[Option<Decimal>; 16],
|
||||||
|
op: F,
|
||||||
|
) -> Option<Instruction<'a>>
|
||||||
|
where
|
||||||
|
F: Fn(i64, i64) -> i64,
|
||||||
|
{
|
||||||
|
let val_a = resolve_value(a, regs)?;
|
||||||
|
let val_b = resolve_value(b, regs)?;
|
||||||
|
let result = op(decimal_to_i64(val_a), decimal_to_i64(val_b));
|
||||||
|
Some(Instruction::Move(
|
||||||
|
dst.clone(),
|
||||||
|
Operand::Number(i64_to_decimal(result)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn try_resolve_branch<'a, F>(
|
fn try_resolve_branch<'a, F>(
|
||||||
a: &Operand<'a>,
|
a: &Operand<'a>,
|
||||||
b: &Operand<'a>,
|
b: &Operand<'a>,
|
||||||
|
|||||||
Reference in New Issue
Block a user