#![cfg(all(
feature = "compile",
feature = "verify",
not(any(
feature = "narrow-word-8",
feature = "narrow-word-16",
feature = "narrow-word-32",
feature = "narrow-address-8",
feature = "narrow-address-16",
feature = "narrow-address-32",
feature = "narrow-float-32"
))
))]
use keleusma::compiler::compile;
use keleusma::lexer::tokenize;
use keleusma::parser::parse;
use keleusma::verify::wcet_whole_chunk;
use keleusma::vm::{DEFAULT_ARENA_CAPACITY, Vm, VmState, auto_arena_capacity_for};
use keleusma::{Arena, Value};
fn run_to_int(src: &str) -> i64 {
let module = compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile");
let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
let mut vm = Vm::new(module, &arena).expect("verify");
match vm.call(&[]).expect("call") {
VmState::Finished(Value::Int(n)) => n,
other => panic!("expected a finished integer, got {:?}", other),
}
}
fn run_to_byte(src: &str) -> u8 {
let module = compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile");
let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
let mut vm = Vm::new(module, &arena).expect("verify");
match vm.call(&[]).expect("call") {
VmState::Finished(Value::Byte(b)) => b,
other => panic!("expected a finished byte, got {:?}", other),
}
}
#[test]
fn multiword_construct_and_index_digit_zero() {
assert_eq!(
run_to_int("fn main() -> Word { let m = (42, 7, 0, 0) as Multiword<4>; m[0] }"),
42
);
}
#[test]
fn multiword_index_higher_digits() {
assert_eq!(
run_to_int("fn main() -> Word { let m = (42, 7, 3, 9) as Multiword<4>; m[1] }"),
7
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (42, 7, 3, 9) as Multiword<4>; m[3] }"),
9
);
}
#[test]
fn multiword_is_a_first_class_parameter_type() {
let src = "fn first(m: Multiword<4>) -> Word { m[0] }\n\
fn main() -> Word { let m = (99, 0, 0, 0) as Multiword<4>; first(m) }";
assert_eq!(run_to_int(src), 99);
}
#[test]
fn multiword_two_word_digits_sum() {
assert_eq!(
run_to_int("fn main() -> Word { let m = (100, 200) as Multiword<2>; m[0] + m[1] }"),
300
);
}
#[test]
fn multiword_construct_from_non_literal_tuple() {
let src = "fn main() -> Word { let t = (11, 22, 33, 44); let m = t as Multiword<4>; m[2] }";
assert_eq!(run_to_int(src), 33);
}
#[test]
fn multiword_turbofish_constructor() {
assert_eq!(
run_to_int("fn main() -> Word { let m = Multiword::<4>(5, 6, 7, 8); m[1] }"),
6
);
}
#[test]
fn multiword_single_word_constructor() {
assert_eq!(
run_to_int("fn main() -> Word { let m = Multiword::<1>(77); m[0] }"),
77
);
}
#[test]
fn multiword_add_no_carry() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 200) as Multiword<2>; let b = (50, 25) as Multiword<2>; let s = a + b; s[0] }"
),
150
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 200) as Multiword<2>; let b = (50, 25) as Multiword<2>; let s = a + b; s[1] }"
),
225
);
}
#[test]
fn multiword_add_unsigned_carry_propagates() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-1, 0) as Multiword<2>; let b = (1, 0) as Multiword<2>; let s = a + b; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-1, 0) as Multiword<2>; let b = (1, 0) as Multiword<2>; let s = a + b; s[1] }"
),
1
);
}
#[test]
fn multiword_add_no_spurious_signed_carry() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (9223372036854775807, 0) as Multiword<2>; let b = (1, 0) as Multiword<2>; let s = a + b; s[1] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (9223372036854775807, 0) as Multiword<2>; let b = (1, 0) as Multiword<2>; let s = a + b; s[0] }"
),
i64::MIN
);
}
#[test]
fn multiword_sub_no_borrow() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (150, 225) as Multiword<2>; let b = (50, 25) as Multiword<2>; let d = a - b; d[0] }"
),
100
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (150, 225) as Multiword<2>; let b = (50, 25) as Multiword<2>; let d = a - b; d[1] }"
),
200
);
}
#[test]
fn multiword_sub_borrow_propagates() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 5) as Multiword<2>; let b = (1, 0) as Multiword<2>; let d = a - b; d[0] }"
),
-1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 5) as Multiword<2>; let b = (1, 0) as Multiword<2>; let d = a - b; d[1] }"
),
4
);
}
#[test]
fn multiword_four_word_add_carry_chain() {
let src = "fn main() -> Word { \
let a = (-1, -1, -1, 0) as Multiword<4>; \
let b = (1, 0, 0, 0) as Multiword<4>; \
let s = a + b; \
s[0] + s[1] + s[2] + s[3] }";
assert_eq!(run_to_int(src), 1);
}
fn compile_fails(src: &str) -> bool {
compile(&parse(&tokenize(src).expect("lex")).expect("parse")).is_err()
}
#[test]
fn multiword_cast_rejects_wrong_tuple_arity() {
assert!(compile_fails(
"fn main() -> Word { let m = (1, 2, 3) as Multiword<4>; m[0] }"
));
assert!(compile_fails(
"fn main() -> Word { let m = (1, 2, 3) as Multiword<2>; m[0] }"
));
assert!(compile_fails(
"fn main() -> Word { let m = Multiword::<4>(1, 2, 3); m[0] }"
));
}
#[test]
fn multiword_cast_rejects_non_word_element() {
assert!(compile_fails(
"fn main() -> Word { let m = (1, 2.0) as Multiword<2>; m[0] }"
));
}
fn run_traps(src: &str) -> bool {
let module = compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile");
let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
let mut vm = Vm::new(module, &arena).expect("verify");
vm.call(&[]).is_err()
}
fn run_to_bool(src: &str) -> bool {
let module = compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile");
let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
let mut vm = Vm::new(module, &arena).expect("verify");
match vm.call(&[]).expect("call") {
VmState::Finished(Value::Bool(b)) => b,
other => panic!("expected a finished bool, got {:?}", other),
}
}
#[test]
fn multiword_eq_and_ne() {
assert!(run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (5, 7) as Multiword<2>; a == b }"
));
assert!(!run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (5, 8) as Multiword<2>; a == b }"
));
assert!(run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (6, 7) as Multiword<2>; a != b }"
));
assert!(!run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (5, 7) as Multiword<2>; a != b }"
));
}
#[test]
fn multiword_ordering_decided_by_high_limb() {
assert!(run_to_bool(
"fn main() -> bool { let a = (100, 1) as Multiword<2>; let b = (0, 2) as Multiword<2>; a < b }"
));
assert!(run_to_bool(
"fn main() -> bool { let a = (0, 2) as Multiword<2>; let b = (100, 1) as Multiword<2>; a > b }"
));
}
#[test]
fn multiword_ordering_low_limb_is_unsigned() {
assert!(run_to_bool(
"fn main() -> bool { let a = (-1, 0) as Multiword<2>; let b = (1, 0) as Multiword<2>; a > b }"
));
assert!(!run_to_bool(
"fn main() -> bool { let a = (-1, 0) as Multiword<2>; let b = (1, 0) as Multiword<2>; a < b }"
));
assert!(run_to_bool(
"fn main() -> bool { let a = (5, 3) as Multiword<2>; let b = (9, 3) as Multiword<2>; a < b }"
));
}
#[test]
fn multiword_ordering_high_limb_is_signed() {
assert!(run_to_bool(
"fn main() -> bool { let a = (0, -1) as Multiword<2>; let b = (0, 0) as Multiword<2>; a < b }"
));
assert!(run_to_bool(
"fn main() -> bool { let a = (0, 0) as Multiword<2>; let b = (0, -1) as Multiword<2>; a > b }"
));
}
#[test]
fn multiword_le_and_ge_include_equality() {
assert!(run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (5, 7) as Multiword<2>; a <= b }"
));
assert!(run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (5, 7) as Multiword<2>; a >= b }"
));
assert!(!run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (4, 7) as Multiword<2>; a <= b }"
));
assert!(run_to_bool(
"fn main() -> bool { let a = (5, 7) as Multiword<2>; let b = (4, 7) as Multiword<2>; a >= b }"
));
}
#[test]
fn multiword_four_word_ordering() {
let base = "let a = (9, 9, 5, 0) as Multiword<4>; let b = (0, 0, 6, 0) as Multiword<4>;";
assert!(run_to_bool(&alloc_src(base, "a < b")));
assert!(!run_to_bool(&alloc_src(base, "a > b")));
assert!(run_to_bool(&alloc_src(base, "a != b")));
}
fn alloc_src(bindings: &str, expr: &str) -> String {
format!("fn main() -> bool {{ {} {} }}", bindings, expr)
}
#[test]
fn multiword_fixed_point_compare_same_scale() {
assert!(run_to_bool(
"fn main() -> bool { let a = (100, 0) as Multiword<2, 16>; let b = (50, 0) as Multiword<2, 16>; a > b }"
));
}
#[test]
fn multiword_fixed_point_annotation_constructs_and_indexes() {
assert_eq!(
run_to_int("fn main() -> Word { let m = Multiword::<2, 32>(7, 3); m[1] }"),
3
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (7, 3) as Multiword<2, 32>; m[0] }"),
7
);
}
#[test]
fn multiword_fixed_point_add_same_scale() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 0) as Multiword<2, 16>; let b = (50, 0) as Multiword<2, 16>; let s = a + b; s[0] }"
),
150
);
}
#[test]
fn multiword_mul_small_no_carry() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (6, 0) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a * b; s[0] }"
),
42
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (6, 0) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a * b; s[1] }"
),
0
);
}
#[test]
fn multiword_mul_cross_term_into_high_word() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (3, 5) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a * b; s[0] }"
),
21
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (3, 5) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a * b; s[1] }"
),
35
);
}
#[test]
fn multiword_mul_high_word_carry_from_digit_product() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (5000000000, 0) as Multiword<2>; let b = (5000000000, 0) as Multiword<2>; let s = a * b; s[0] }"
),
6553255926290448384
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (5000000000, 0) as Multiword<2>; let b = (5000000000, 0) as Multiword<2>; let s = a * b; s[1] }"
),
1
);
}
#[test]
fn multiword_mul_unsigned_high_correction() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-1, 0) as Multiword<2>; let b = (2, 0) as Multiword<2>; let s = a * b; s[0] }"
),
-2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-1, 0) as Multiword<2>; let b = (2, 0) as Multiword<2>; let s = a * b; s[1] }"
),
1
);
}
#[test]
fn multiword_mul_negative_value() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-1, -1) as Multiword<2>; let b = (2, 0) as Multiword<2>; let s = a * b; s[0] }"
),
-2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-1, -1) as Multiword<2>; let b = (2, 0) as Multiword<2>; let s = a * b; s[1] }"
),
-1
);
}
#[test]
fn multiword_mul_identity_and_zero() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (123, 456) as Multiword<2>; let b = (1, 0) as Multiword<2>; let s = a * b; s[0] + s[1] }"
),
579
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (123, 456) as Multiword<2>; let b = (0, 0) as Multiword<2>; let s = a * b; s[0] + s[1] }"
),
0
);
}
#[test]
fn multiword_mul_three_word_scalar() {
let base =
"let a = (2, 3, 4) as Multiword<3>; let b = (5, 0, 0) as Multiword<3>; let s = a * b;";
assert_eq!(
run_to_int(&format!("fn main() -> Word {{ {} s[0] }}", base)),
10
);
assert_eq!(
run_to_int(&format!("fn main() -> Word {{ {} s[1] }}", base)),
15
);
assert_eq!(
run_to_int(&format!("fn main() -> Word {{ {} s[2] }}", base)),
20
);
}
#[test]
fn multiword_mul_is_commutative() {
let ab = run_to_int(
"fn main() -> Word { let a = (7, 11) as Multiword<2>; let b = (13, 3) as Multiword<2>; let s = a * b; s[0] + s[1] }",
);
let ba = run_to_int(
"fn main() -> Word { let a = (7, 11) as Multiword<2>; let b = (13, 3) as Multiword<2>; let s = b * a; s[0] + s[1] }",
);
assert_eq!(ab, ba);
}
#[test]
fn multiword_fixed_point_multiply_small_scale() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (65536, 0) as Multiword<2, 16>; let b = (131072, 0) as Multiword<2, 16>; let s = a * b; s[0] }"
),
131072
);
}
#[test]
fn multiword_fixed_mul_rounds_toward_negative_infinity() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-3, -1) as Multiword<2, 1>; let b = (1, 0) as Multiword<2, 1>; let s = a * b; s[0] }"
),
-2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (3, 0) as Multiword<2, 1>; let b = (1, 0) as Multiword<2, 1>; let s = a * b; s[0] }"
),
1
);
}
#[test]
fn multiword_fixed_mul_single_word() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = Multiword::<1, 8>(256); let b = Multiword::<1, 8>(512); let s = a * b; s[0] }"
),
512
);
}
#[test]
fn multiword_fixed_mul_integer_scale() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 2) as Multiword<2, 64>; let b = (0, 3) as Multiword<2, 64>; let s = a * b; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 2) as Multiword<2, 64>; let b = (0, 3) as Multiword<2, 64>; let s = a * b; s[1] }"
),
6
);
}
#[test]
fn multiword_fixed_mul_bit_scale() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (6442450944, 0) as Multiword<2, 32>; let b = (8589934592, 0) as Multiword<2, 32>; let s = a * b; s[0] }"
),
12884901888
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (6442450944, 0) as Multiword<2, 32>; let b = (8589934592, 0) as Multiword<2, 32>; let s = a * b; s[1] }"
),
0
);
}
#[test]
fn multiword_fixed_mul_fractional_result() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (2147483648, 0) as Multiword<2, 32>; let b = (2147483648, 0) as Multiword<2, 32>; let s = a * b; s[0] }"
),
1073741824
);
}
#[test]
fn multiword_fixed_mul_negative() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, -2) as Multiword<2, 64>; let b = (0, 3) as Multiword<2, 64>; let s = a * b; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, -2) as Multiword<2, 64>; let b = (0, 3) as Multiword<2, 64>; let s = a * b; s[1] }"
),
-6
);
}
#[test]
fn multiword_fixed_mul_both_negative() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, -2) as Multiword<2, 64>; let b = (0, -3) as Multiword<2, 64>; let s = a * b; s[1] }"
),
6
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, -2) as Multiword<2, 64>; let b = (0, -3) as Multiword<2, 64>; let s = a * b; s[0] }"
),
0
);
}
#[test]
fn multiword_fixed_mul_at_bound_compiles() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 0) as Multiword<2, 128>; let b = (0, 0) as Multiword<2, 128>; let s = a * b; s[0] }"
),
0
);
}
#[test]
fn multiword_fixed_mul_over_bound_rejected() {
assert!(compile_fails(
"fn main() -> Word { let a = (1, 0) as Multiword<2, 200>; let b = (1, 0) as Multiword<2, 200>; let s = a * b; s[0] }"
));
}
#[test]
fn multiword_div_and_mod_basic() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a / b; s[0] }"
),
14
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a % b; s[0] }"
),
2
);
}
#[test]
fn multiword_div_exact_and_smaller_dividend() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (4, 0) as Multiword<2>; let s = a / b; s[0] }"
),
25
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (5, 0) as Multiword<2>; let b = (10, 0) as Multiword<2>; let s = a / b; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (5, 0) as Multiword<2>; let b = (10, 0) as Multiword<2>; let s = a % b; s[0] }"
),
5
);
}
#[test]
fn multiword_div_spans_two_words() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 1) as Multiword<2>; let b = (2, 0) as Multiword<2>; let s = a / b; s[1] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 1) as Multiword<2>; let b = (2, 0) as Multiword<2>; let s = a / b; s[0] }"
),
i64::MIN
);
}
#[test]
fn multiword_div_negative_dividend() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-100, -1) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a / b; s[0] }"
),
-14
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-100, -1) as Multiword<2>; let b = (7, 0) as Multiword<2>; let s = a % b; s[0] }"
),
-2
);
}
#[test]
fn multiword_div_negative_divisor() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (-7, -1) as Multiword<2>; let s = a / b; s[0] }"
),
-14
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (-7, -1) as Multiword<2>; let s = a % b; s[0] }"
),
2
);
}
#[test]
fn multiword_div_both_negative() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-100, -1) as Multiword<2>; let b = (-7, -1) as Multiword<2>; let s = a / b; s[0] }"
),
14
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-100, -1) as Multiword<2>; let b = (-7, -1) as Multiword<2>; let s = a % b; s[0] }"
),
-2
);
}
#[test]
fn multiword_div_three_word() {
let base =
"let a = (0, 0, 6) as Multiword<3>; let b = (3, 0, 0) as Multiword<3>; let s = a / b;";
assert_eq!(
run_to_int(&format!("fn main() -> Word {{ {} s[2] }}", base)),
2
);
assert_eq!(
run_to_int(&format!("fn main() -> Word {{ {} s[0] }}", base)),
0
);
}
#[test]
fn multiword_div_by_zero_traps() {
assert!(run_traps(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (0, 0) as Multiword<2>; let s = a / b; s[0] }"
));
assert!(run_traps(
"fn main() -> Word { let a = (100, 0) as Multiword<2>; let b = (0, 0) as Multiword<2>; let s = a % b; s[0] }"
));
}
#[test]
fn multiword_fixed_div_basic() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (25769803776, 0) as Multiword<2, 32>; let b = (8589934592, 0) as Multiword<2, 32>; let s = a / b; s[0] }"
),
12884901888
);
}
#[test]
fn multiword_fixed_div_fractional_result() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (4294967296, 0) as Multiword<2, 32>; let b = (8589934592, 0) as Multiword<2, 32>; let s = a / b; s[0] }"
),
2147483648
);
}
#[test]
fn multiword_fixed_div_negative() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (-25769803776, -1) as Multiword<2, 32>; let b = (8589934592, 0) as Multiword<2, 32>; let s = a / b; s[0] }"
),
-12884901888
);
}
#[test]
fn multiword_fixed_div_whole_word_shift() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 6) as Multiword<2, 64>; let b = (0, 2) as Multiword<2, 64>; let s = a / b; s[1] }"
),
3
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 6) as Multiword<2, 64>; let b = (0, 2) as Multiword<2, 64>; let s = a / b; s[0] }"
),
0
);
}
#[test]
fn multiword_fixed_mod_keeps_scale() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (23622320128, 0) as Multiword<2, 32>; let b = (8589934592, 0) as Multiword<2, 32>; let s = a % b; s[0] }"
),
6442450944
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (0, 5) as Multiword<2, 64>; let b = (0, 3) as Multiword<2, 64>; let s = a % b; s[1] }"
),
2
);
}
#[test]
fn multiword_fixed_div_by_zero_traps() {
assert!(run_traps(
"fn main() -> Word { let a = (4294967296, 0) as Multiword<2, 32>; let b = (0, 0) as Multiword<2, 32>; let s = a / b; s[0] }"
));
}
#[test]
fn multiword_fixed_div_truncates_toward_zero() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = Multiword::<1, 4>(0 - 16); let b = Multiword::<1, 4>(48); let s = a / b; s[0] }"
),
-5
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = Multiword::<1, 4>(16); let b = Multiword::<1, 4>(48); let s = a / b; s[0] }"
),
5
);
}
#[test]
fn scalar_word_shifts() {
assert_eq!(run_to_int("fn main() -> Word { 5 lsl 2 }"), 20);
assert_eq!(run_to_int("fn main() -> Word { 20 lsr 2 }"), 5);
assert_eq!(
run_to_int("fn main() -> Word { let x = 0 - 8; x asr 1 }"),
-4
);
assert_eq!(
run_to_int("fn main() -> Word { let x = 0 - 8; x lsr 1 }"),
9223372036854775804
);
assert_eq!(run_to_int("fn main() -> Word { 1 lsl 63 }"), i64::MIN);
assert_eq!(run_to_int("fn main() -> Word { 5 lsl 0 }"), 5);
}
#[test]
fn scalar_arithmetic_left_shift_bare_wraps_like_logical() {
assert_eq!(run_to_int("fn main() -> Word { 5 asl 2 }"), 20);
assert_eq!(run_to_int("fn main() -> Word { 1 asl 63 }"), i64::MIN);
}
#[test]
fn scalar_arithmetic_left_shift_captures_overflow() {
assert_eq!(
run_to_int("fn main() -> Word { 3 asl 2 { ok(v) => v, overflow(h, l) => 0 } }"),
12
);
assert_eq!(
run_to_int(
"fn main() -> Word { let x = 4611686018427387904; x asl 1 { ok(v) => v, overflow(h, l) => 999 } }"
),
999
);
assert_eq!(
run_to_int(
"fn main() -> Word { let x = 4611686018427387904; x asl 1 { ok(v) => v, overflow(h, l) => l } }"
),
i64::MIN
);
assert_eq!(
run_to_int(
"fn main() -> Word { let x = 4611686018427387904; x asl 1 { ok(v) => v, overflow(h, l) => saturate_max } }"
),
i64::MAX
);
}
#[test]
fn scalar_arithmetic_left_shift_captures_underflow() {
assert_eq!(
run_to_int(
"fn main() -> Word { let x = 0 - 4611686018427387904; x asl 2 { ok(v) => v, underflow(h, l) => 0 - 1 } }"
),
-1
);
}
#[test]
fn scalar_shift_precedence_below_additive() {
assert_eq!(run_to_int("fn main() -> Word { 0 - 8 asr 1 }"), -4);
}
#[test]
fn scalar_shift_rejects_out_of_range_literal_amount() {
assert!(compile_fails("fn main() -> Word { 5 lsl 64 }"));
assert!(compile_fails(
"fn main() -> Word { let x = 0 - 1; x asr 64 }"
));
}
#[test]
fn scalar_variable_shift_word() {
assert_eq!(run_to_int("fn main() -> Word { let k = 2; 5 lsl k }"), 20);
assert_eq!(run_to_int("fn main() -> Word { let k = 3; 40 lsr k }"), 5);
assert_eq!(
run_to_int("fn main() -> Word { let k = 1; let x = 0 - 8; x asr k }"),
-4
);
assert_eq!(
run_to_int("fn main() -> Word { let k = 1; let x = 0 - 8; x lsr k }"),
9223372036854775804
);
assert_eq!(run_to_int("fn main() -> Word { let k = 0; 5 lsr k }"), 5);
assert_eq!(run_to_int("fn main() -> Word { let k = 64; 5 lsl k }"), 5);
}
#[test]
fn byte_shift_constant_and_variable() {
assert_eq!(run_to_byte("fn main() -> Byte { 5Byte lsl 1 }"), 10);
assert_eq!(run_to_byte("fn main() -> Byte { 200Byte lsl 1 }"), 144);
assert_eq!(run_to_byte("fn main() -> Byte { 255Byte lsr 1 }"), 127);
assert_eq!(run_to_byte("fn main() -> Byte { 255Byte asr 1 }"), 127);
assert_eq!(
run_to_byte("fn main() -> Byte { let k = 2; 5Byte lsl k }"),
20
);
assert_eq!(
run_to_byte("fn main() -> Byte { let k = 1; 254Byte lsr k }"),
127
);
}
#[test]
fn byte_bitwise_and_complement() {
assert_eq!(run_to_byte("fn main() -> Byte { 12Byte band 10Byte }"), 8);
assert_eq!(run_to_byte("fn main() -> Byte { 12Byte bor 10Byte }"), 14);
assert_eq!(run_to_byte("fn main() -> Byte { 12Byte bxor 10Byte }"), 6);
assert_eq!(run_to_byte("fn main() -> Byte { bnot 0Byte }"), 255);
assert_eq!(run_to_byte("fn main() -> Byte { bnot 5Byte }"), 250);
}
#[test]
fn multiword_shift_left_within_and_across_words() {
assert_eq!(
run_to_int("fn main() -> Word { let m = (1, 0) as Multiword<2>; let s = m lsl 1; s[0] }"),
2
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (5, 0) as Multiword<2>; let s = m lsl 64; s[1] }"),
5
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (5, 0) as Multiword<2>; let s = m lsl 64; s[0] }"),
0
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (1, 0) as Multiword<2>; let s = m asl 1; s[0] }"),
2
);
}
#[test]
fn multiword_shift_right_arithmetic_vs_logical() {
assert_eq!(
run_to_int("fn main() -> Word { let m = (0, -1) as Multiword<2>; let s = m asr 1; s[1] }"),
-1
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (0, -1) as Multiword<2>; let s = m asr 1; s[0] }"),
i64::MIN
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (0, -1) as Multiword<2>; let s = m lsr 1; s[1] }"),
i64::MAX
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (0, -1) as Multiword<2>; let s = m lsr 1; s[0] }"),
i64::MIN
);
}
#[test]
fn multiword_shift_right_whole_word() {
assert_eq!(
run_to_int("fn main() -> Word { let m = (0, 1) as Multiword<2>; let s = m lsr 64; s[0] }"),
1
);
assert_eq!(
run_to_int("fn main() -> Word { let m = (0, 1) as Multiword<2>; let s = m lsr 64; s[1] }"),
0
);
}
#[test]
fn multiword_shift_rejects_out_of_range_literal_amount() {
assert!(compile_fails(
"fn main() -> Word { let m = (1, 0) as Multiword<2>; let s = m lsl 128; s[0] }"
));
}
#[test]
fn multiword_variable_shift() {
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1; let m = (1, 0) as Multiword<2>; let s = m lsl k; s[0] }"
),
2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 64; let m = (5, 0) as Multiword<2>; let s = m lsl k; s[1] }"
),
5
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 64; let m = (5, 0) as Multiword<2>; let s = m lsl k; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 65; let m = (1, 0) as Multiword<2>; let s = m lsl k; s[1] }"
),
2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1; let m = (1, 0) as Multiword<2>; let s = m asl k; s[0] }"
),
2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1; let m = (0, 0 - 1) as Multiword<2>; let s = m asr k; s[1] }"
),
-1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1; let m = (0, 0 - 1) as Multiword<2>; let s = m asr k; s[0] }"
),
i64::MIN
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1; let m = (0, 0 - 1) as Multiword<2>; let s = m lsr k; s[1] }"
),
i64::MAX
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 64; let m = (0, 1) as Multiword<2>; let s = m lsr k; s[0] }"
),
1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 0; let m = (5, 7) as Multiword<2>; let s = m lsl k; s[1] }"
),
7
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 200; let m = (1, 0) as Multiword<2>; let s = m lsl k; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 200; let m = (1, 0) as Multiword<2>; let s = m lsl k; s[1] }"
),
0
);
}
#[test]
fn checked_asl_still_requires_constant_amount() {
assert!(compile_fails(
"fn main() -> Word { let k = 2; 3 asl k { ok(v) => v, overflow(h, l) => 0 } }"
));
assert_eq!(run_to_int("fn main() -> Word { let k = 2; 3 asl k }"), 12);
}
#[test]
fn multiword_variable_shift_three_word() {
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 64; let m = (1, 0, 0) as Multiword<3>; let s = m lsl k; s[1] }"
),
1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 64; let m = (0, 0, 8) as Multiword<3>; let s = m lsr k; s[1] }"
),
8
);
}
#[test]
fn multiword_negative_runtime_shift_fills() {
assert_eq!(
run_to_int(
"fn main() -> Word { let c = 0 - 1; let m = (7, 3) as Multiword<2>; let s = m lsl c; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let c = 0 - 1; let m = (7, 3) as Multiword<2>; let s = m lsl c; s[1] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let c = 0 - 1; let m = (7, 3) as Multiword<2>; let s = m lsr c; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let c = 0 - 1; let m = (7, -1) as Multiword<2>; let s = m asr c; s[0] }"
),
-1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let c = 0 - 1; let m = (7, -1) as Multiword<2>; let s = m asr c; s[1] }"
),
-1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let c = 0 - 5; let m = (7, 5) as Multiword<2>; let s = m asr c; s[0] }"
),
0
);
}
#[test]
fn multiword_variable_shift_four_word() {
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 128; let m = (1, 0, 0, 0) as Multiword<4>; let s = m lsl k; s[2] }"
),
1
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 128; let m = (1, 0, 0, 0) as Multiword<4>; let s = m lsl k; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 192; let m = (0, 0, 0, 8) as Multiword<4>; let s = m lsr k; s[0] }"
),
8
);
}
#[test]
fn multiword_variable_shift_fixed_point() {
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1; let m = (2, 0) as Multiword<2, 16>; let s = m lsl k; s[0] }"
),
4
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 64; let m = (7, 0) as Multiword<2, 16>; let s = m lsl k; s[1] }"
),
7
);
}
#[test]
fn variable_shift_is_total_for_negative_and_over_large_counts() {
assert_eq!(
run_to_int("fn main() -> Word { let k = 0 - 1; 5 lsl k }"),
i64::MIN
);
assert_eq!(
run_to_int("fn main() -> Word { let k = 1000; 1 lsl k }"),
1i64 << 40
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1000000; let m = (1, 7) as Multiword<2>; let s = m lsl k; s[0] }"
),
0
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 1000000; let m = (1, 7) as Multiword<2>; let s = m lsl k; s[1] }"
),
0
);
let _neg = run_to_int(
"fn main() -> Word { let k = 0 - 1; let m = (1, 0) as Multiword<2>; let s = m lsr k; s[0] }",
);
assert_eq!(
run_to_int(
"fn main() -> Word { let k = 0 - 5; let m = (9, 3) as Multiword<2>; let s = m lsr k; s[1] }"
),
run_to_int(
"fn main() -> Word { let k = 0 - 5; let m = (9, 3) as Multiword<2>; let s = m lsr k; s[1] }"
)
);
}
fn main_wcet(src: &str) -> Result<u32, ()> {
let module = compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile");
let main = module
.chunks
.iter()
.find(|c| c.name == "main")
.expect("main chunk");
wcet_whole_chunk(main).map_err(|_| ())
}
fn main_wcmu(src: &str) -> Result<usize, ()> {
let module = compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile");
auto_arena_capacity_for(&module, &[]).map_err(|_| ())
}
#[test]
fn variable_shift_bounds_are_finite_and_account_the_unrolled_ops() {
const CONST_SHIFT: &str =
"fn main() -> Word { let m = (1, 3) as Multiword<2>; let s = m lsl 5; s[0] }";
const VAR_SHIFT: &str =
"fn main() -> Word { let k = 5; let m = (1, 3) as Multiword<2>; let s = m lsl k; s[0] }";
let const_wcet = main_wcet(CONST_SHIFT).expect("constant shift WCET is provable");
let var_wcet = main_wcet(VAR_SHIFT).expect("variable shift WCET is provable");
assert!(const_wcet > 0 && var_wcet > 0);
assert!(
var_wcet > const_wcet,
"variable shift ({var_wcet}) emits strictly more ops than constant ({const_wcet}); the cost model must reflect them"
);
assert!(main_wcmu(CONST_SHIFT).expect("constant shift WCMU is provable") > 0);
assert!(main_wcmu(VAR_SHIFT).expect("variable shift WCMU is provable") > 0);
assert!(
main_wcet(
"fn main() -> Word { let k = 130; let m = (1, 0, 0, 0) as Multiword<4>; let s = m lsr k; s[0] }"
)
.is_ok()
);
}
#[test]
fn byte_variable_shift_masks_to_word_width() {
assert_eq!(
run_to_byte("fn main() -> Byte { let k = 8; 5Byte lsl k }"),
0
);
assert_eq!(
run_to_byte("fn main() -> Byte { let k = 64; 5Byte lsl k }"),
5
);
assert_eq!(
run_to_byte("fn main() -> Byte { let k = 8; 200Byte lsr k }"),
0
);
}
#[test]
fn nested_generics_parse() {
assert_eq!(
run_to_int("fn id(x: Option<Option<Word>>) -> Word { 0 }\nfn main() -> Word { 7 }"),
7
);
}
#[test]
fn multiword_nested_operations_do_not_alias_scratch_locals() {
assert_eq!(
run_to_int(
"fn main() -> Word { \
let a = (1, 0) as Multiword<2>; \
let b = (2, 0) as Multiword<2>; \
let c = (3, 0) as Multiword<2>; \
let s = (a + b) + c; s[0] }"
),
6
);
assert!(run_to_bool(
"fn main() -> bool { \
let a = (1, 0) as Multiword<2>; \
let b = (2, 0) as Multiword<2>; \
let c = (3, 0) as Multiword<2>; \
(a + b) < (a + c) }"
));
}
#[test]
fn multiword_different_scales_do_not_mix() {
assert!(compile_fails(
"fn main() -> Word { let a = (1, 0) as Multiword<2>; let b = (1, 0) as Multiword<2, 16>; let s = a + b; s[0] }"
));
}
#[test]
fn scalar_bitwise_and_or_xor() {
assert_eq!(run_to_int("fn main() -> Word { 12 band 10 }"), 8);
assert_eq!(run_to_int("fn main() -> Word { 12 bor 10 }"), 14);
assert_eq!(run_to_int("fn main() -> Word { 12 bxor 10 }"), 6);
}
#[test]
fn scalar_bitwise_not_is_all_ones_complement() {
assert_eq!(run_to_int("fn main() -> Word { bnot 0 }"), -1);
assert_eq!(run_to_int("fn main() -> Word { bnot (0 - 1) }"), 0);
assert_eq!(run_to_int("fn main() -> Word { bnot 5 }"), -6);
}
#[test]
fn scalar_bitwise_precedence_band_below_bxor_below_bor() {
assert_eq!(run_to_int("fn main() -> Word { 1 bor 2 band 2 }"), 3);
assert_eq!(run_to_int("fn main() -> Word { 5 bxor 1 band 1 }"), 4);
}
#[test]
fn scalar_bitwise_below_comparison() {
assert!(run_to_bool("fn main() -> bool { 1 band 1 == 1 }"));
}
#[test]
fn multiword_bitwise_per_limb() {
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (12, 6) as Multiword<2>; let b = (10, 3) as Multiword<2>; let c = a band b; c[0] }"
),
8
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (12, 6) as Multiword<2>; let b = (10, 3) as Multiword<2>; let c = a band b; c[1] }"
),
2
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (12, 6) as Multiword<2>; let b = (10, 3) as Multiword<2>; let c = a bor b; c[0] }"
),
14
);
assert_eq!(
run_to_int(
"fn main() -> Word { let a = (12, 6) as Multiword<2>; let b = (10, 3) as Multiword<2>; let c = a bxor b; c[1] }"
),
5
);
}
#[test]
fn multiword_bitwise_not_per_limb() {
assert_eq!(
run_to_int("fn main() -> Word { let a = (0, 0) as Multiword<2>; let c = bnot a; c[0] }"),
-1
);
assert_eq!(
run_to_int("fn main() -> Word { let a = (5, 0) as Multiword<2>; let c = bnot a; c[1] }"),
-1
);
assert_eq!(
run_to_int("fn main() -> Word { let a = (5, 0) as Multiword<2>; let c = bnot a; c[0] }"),
-6
);
}
#[test]
fn bitwise_rejects_non_integer_operands() {
assert!(compile_fails("fn main() -> Word { true band 1 }"));
assert!(compile_fails("fn main() -> Word { bnot true }"));
}
#[test]
fn boolean_eager_and_truth_table() {
assert!(run_to_bool("fn main() -> bool { true and true }"));
assert!(!run_to_bool("fn main() -> bool { true and false }"));
assert!(!run_to_bool("fn main() -> bool { false and true }"));
assert!(!run_to_bool("fn main() -> bool { false and false }"));
}
#[test]
fn boolean_eager_or_truth_table() {
assert!(run_to_bool("fn main() -> bool { true or true }"));
assert!(run_to_bool("fn main() -> bool { true or false }"));
assert!(run_to_bool("fn main() -> bool { false or true }"));
assert!(!run_to_bool("fn main() -> bool { false or false }"));
}
#[test]
fn boolean_xor_truth_table() {
assert!(!run_to_bool("fn main() -> bool { true xor true }"));
assert!(run_to_bool("fn main() -> bool { true xor false }"));
assert!(run_to_bool("fn main() -> bool { false xor true }"));
assert!(!run_to_bool("fn main() -> bool { false xor false }"));
}
#[test]
fn boolean_not_and_double_not() {
assert!(!run_to_bool("fn main() -> bool { not true }"));
assert!(run_to_bool("fn main() -> bool { not false }"));
assert!(run_to_bool("fn main() -> bool { not not true }"));
}
#[test]
fn boolean_short_circuit_truth_table_matches_eager() {
assert!(run_to_bool("fn main() -> bool { true andalso true }"));
assert!(!run_to_bool("fn main() -> bool { true andalso false }"));
assert!(!run_to_bool("fn main() -> bool { false andalso true }"));
assert!(run_to_bool("fn main() -> bool { false orelse true }"));
assert!(!run_to_bool("fn main() -> bool { false orelse false }"));
assert!(run_to_bool("fn main() -> bool { true orelse false }"));
}
#[test]
fn boolean_precedence_and_binds_tighter_than_or() {
assert!(run_to_bool("fn main() -> bool { true or false and false }"));
}
#[test]
fn boolean_precedence_comparison_binds_tighter_than_and() {
assert!(run_to_bool("fn main() -> bool { 1 == 1 and 2 == 2 }"));
}
#[test]
fn boolean_rejects_non_bool_operands() {
assert!(compile_fails("fn main() -> bool { 1 and true }"));
assert!(compile_fails("fn main() -> bool { true xor 3 }"));
assert!(compile_fails("fn main() -> bool { 1 andalso true }"));
}