use crate::*;
use std::sync::Arc;
#[test]
fn qbe_value() {
let val = Value::Temporary("temp42".into());
assert_eq!(format!("{val}"), "%temp42");
let val = Value::Global("main".into());
assert_eq!(format!("{val}"), "$main");
let val = Value::Const(1337);
assert_eq!(format!("{val}"), "1337");
}
#[test]
fn block() {
let blk = Block {
label: "start".into(),
items: vec![BlockItem::Statement(Statement::Volatile(Instr::Ret(None)))],
};
let formatted = format!("{blk}");
let mut lines = formatted.lines();
assert_eq!(lines.next().unwrap(), "@start");
assert_eq!(lines.next().unwrap(), "\tret");
let blk = Block {
label: "start".into(),
items: vec![
BlockItem::Comment("Comment".into()),
BlockItem::Statement(Statement::Assign(
Value::Temporary("foo".into()),
Type::Word,
Instr::Add(Value::Const(2), Value::Const(2)),
)),
BlockItem::Statement(Statement::Volatile(Instr::Ret(Some(Value::Temporary(
"foo".into(),
))))),
],
};
let formatted = format!("{blk}");
let mut lines = formatted.lines();
assert_eq!(lines.next().unwrap(), "@start");
assert_eq!(lines.next().unwrap(), "\t# Comment");
assert_eq!(lines.next().unwrap(), "\t%foo =w add 2, 2");
assert_eq!(lines.next().unwrap(), "\tret %foo");
}
#[test]
fn instr_blit() {
let blk = Block {
label: "start".into(),
items: vec![BlockItem::Statement(Statement::Volatile(Instr::Blit(
Value::Temporary("src".into()),
Value::Temporary("dst".into()),
4,
)))],
};
let formatted = format!("{blk}");
let mut lines = formatted.lines();
assert_eq!(lines.next().unwrap(), "@start");
assert_eq!(lines.next().unwrap(), "\tblit %src, %dst, 4");
}
#[test]
fn function() {
let func = Function {
linkage: Linkage::public(),
return_ty: None,
name: "main".into(),
arguments: Vec::new(),
blocks: vec![Block {
label: "start".into(),
items: vec![BlockItem::Statement(Statement::Volatile(Instr::Ret(None)))],
}],
};
let formatted = format!("{func}");
let mut lines = formatted.lines();
assert_eq!(lines.next().unwrap(), "export function $main() {");
assert_eq!(lines.next().unwrap(), "@start");
assert_eq!(lines.next().unwrap(), "\tret");
assert_eq!(lines.next().unwrap(), "}");
}
#[test]
fn function_new_equivalence() {
let func1 = Function {
linkage: Linkage::public(),
return_ty: None,
name: "main".into(),
arguments: Vec::new(),
blocks: Vec::new(),
};
let func2 = Function::new(Linkage::public(), "main", Vec::new(), None);
assert_eq!(func1, func2);
}
#[test]
fn datadef() {
let datadef = DataDef {
linkage: Linkage::public(),
name: "hello".into(),
align: None,
items: vec![
(Type::Byte, DataItem::Str("Hello, World!".into())),
(Type::Byte, DataItem::Const(0)),
],
};
let formatted = format!("{datadef}");
assert_eq!(
formatted,
"export data $hello = { b \"Hello, World!\", b 0 }"
);
}
#[test]
fn datadef_new_equivalence() {
let datadef1 = DataDef {
linkage: Linkage::public(),
name: "hello".into(),
align: None,
items: vec![],
};
let datadef2 = DataDef::new(Linkage::public(), "hello", None, vec![]);
assert_eq!(datadef1, datadef2);
}
#[test]
fn typedef_regular() {
let typedef = TypeDef::Regular {
ident: "person".into(),
align: None,
items: vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
};
let formatted = format!("{typedef}");
assert_eq!(formatted, "type :person = { l, w 2, b }");
let typedef_with_align = TypeDef::Regular {
ident: "person".into(),
align: Some(8),
items: vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
};
let formatted = format!("{typedef_with_align}");
assert_eq!(formatted, "type :person = align 8 { l, w 2, b }");
let ty = Type::aggregate(&Arc::new(typedef));
let formatted = format!("{ty}");
assert_eq!(formatted, ":person");
}
#[test]
fn typedef_union() {
let typedef = TypeDef::Union {
ident: "data".into(),
align: None,
variations: vec![
vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
vec![(Type::Long, 2)],
],
};
let formatted = format!("{typedef}");
assert_eq!(formatted, "type :data = { { l, w 2, b } { l 2 } }");
let typedef_with_align = TypeDef::Union {
ident: "data".into(),
align: Some(8),
variations: vec![
vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
vec![(Type::Long, 2)],
],
};
let formatted = format!("{typedef_with_align}");
assert_eq!(formatted, "type :data = align 8 { { l, w 2, b } { l 2 } }");
let ty = Type::aggregate(&Arc::new(typedef));
let formatted = format!("{ty}");
assert_eq!(formatted, ":data");
}
#[test]
fn typedef_opaque() {
let typedef = TypeDef::Opaque {
ident: "data".into(),
align: 8,
size: 64,
};
let formatted = format!("{typedef}");
assert_eq!(formatted, "type :data = align 8 { 64 }");
let ty = Type::aggregate(&Arc::new(typedef));
let formatted = format!("{ty}");
assert_eq!(formatted, ":data");
}
#[test]
fn type_size() {
assert!(Type::Byte.size() == 1);
assert!(Type::SignedByte.size() == 1);
assert!(Type::UnsignedByte.size() == 1);
assert!(Type::Halfword.size() == 2);
assert!(Type::SignedHalfword.size() == 2);
assert!(Type::UnsignedHalfword.size() == 2);
assert!(Type::Word.size() == 4);
assert!(Type::Single.size() == 4);
assert!(Type::Long.size() == 8);
assert!(Type::Double.size() == 8);
let typedef_regular = TypeDef::Regular {
ident: "person".into(),
align: None,
items: vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
};
let aggregate = Type::aggregate(&Arc::new(typedef_regular));
assert_eq!(aggregate.size(), 24);
let typedef_union = Arc::new(TypeDef::Union {
ident: "data".into(),
align: None,
variations: vec![
vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
vec![(Type::Long, 5)],
],
});
let aggregate = Type::aggregate(&typedef_union);
assert_eq!(aggregate.size(), 40);
let typedef_opaque = Arc::new(TypeDef::Opaque {
ident: "data".into(),
align: 8,
size: 64,
});
let aggregate = Type::aggregate(&typedef_opaque);
assert_eq!(aggregate.size(), 64);
}
#[test]
fn type_align() {
assert!(Type::Byte.align() == 1);
assert!(Type::SignedByte.align() == 1);
assert!(Type::UnsignedByte.align() == 1);
assert!(Type::Halfword.align() == 2);
assert!(Type::SignedHalfword.align() == 2);
assert!(Type::UnsignedHalfword.align() == 2);
assert!(Type::Word.align() == 4);
assert!(Type::Single.align() == 4);
assert!(Type::Long.align() == 8);
assert!(Type::Double.align() == 8);
let typedef_regular = TypeDef::Regular {
ident: "person".into(),
align: None,
items: vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
};
let aggregate = Type::aggregate(&Arc::new(typedef_regular));
assert_eq!(aggregate.align(), 8);
let typedef_union = Arc::new(TypeDef::Union {
ident: "data".into(),
align: None,
variations: vec![
vec![(Type::Word, 1), (Type::Word, 2), (Type::Byte, 1)],
vec![(Type::Long, 5)],
],
});
let aggregate = Type::aggregate(&typedef_union);
assert_eq!(aggregate.align(), 8);
let typedef_opaque = Arc::new(TypeDef::Opaque {
ident: "data".into(),
align: 8,
size: 64,
});
let aggregate = Type::aggregate(&typedef_opaque);
assert_eq!(aggregate.align(), 8);
}
#[test]
fn type_size_nested_aggregate() {
let inner = Arc::new(TypeDef::Regular {
ident: "dog".into(),
align: None,
items: vec![(Type::Long, 2)],
});
let inner_aggregate = Type::aggregate(&inner);
assert!(inner_aggregate.size() == 16);
let typedef = Arc::new(TypeDef::Regular {
ident: "person".into(),
align: None,
items: vec![
(Type::Long, 1),
(Type::Word, 2),
(Type::Byte, 1),
(Type::aggregate(&inner), 1),
],
});
let aggregate = Type::aggregate(&typedef);
assert_eq!(aggregate.size(), 40);
}
#[test]
fn type_into_abi() {
let unchanged = |ty: Type| assert_eq!(ty.clone().into_abi(), ty);
unchanged(Type::Word);
unchanged(Type::Long);
unchanged(Type::Single);
unchanged(Type::Double);
let typedef = Arc::new(TypeDef::Regular {
ident: "foo".into(),
align: None,
items: Vec::new(),
});
unchanged(Type::aggregate(&typedef));
assert_eq!(Type::Byte.into_abi(), Type::Word);
assert_eq!(Type::UnsignedByte.into_abi(), Type::Word);
assert_eq!(Type::SignedByte.into_abi(), Type::Word);
assert_eq!(Type::Halfword.into_abi(), Type::Word);
assert_eq!(Type::UnsignedHalfword.into_abi(), Type::Word);
assert_eq!(Type::SignedHalfword.into_abi(), Type::Word);
}
#[test]
fn type_into_base() {
let unchanged = |ty: Type| assert_eq!(ty.clone().into_base(), ty);
unchanged(Type::Word);
unchanged(Type::Long);
unchanged(Type::Single);
unchanged(Type::Double);
assert_eq!(Type::Byte.into_base(), Type::Word);
assert_eq!(Type::UnsignedByte.into_base(), Type::Word);
assert_eq!(Type::SignedHalfword.into_base(), Type::Word);
assert_eq!(Type::Halfword.into_base(), Type::Word);
assert_eq!(Type::UnsignedHalfword.into_base(), Type::Word);
assert_eq!(Type::SignedHalfword.into_base(), Type::Word);
let typedef = Arc::new(TypeDef::Regular {
ident: "foo".into(),
align: None,
items: Vec::new(),
});
assert_eq!(Type::aggregate(&typedef).into_base(), Type::Long);
}
#[test]
fn add_function_to_module() {
let mut module = Module::new();
let function = Function {
linkage: Linkage::public(),
name: "foo".into(),
arguments: Vec::new(),
blocks: Vec::new(),
return_ty: None,
};
module.add_function(function.clone());
assert_eq!(module.functions.into_iter().next().unwrap(), function);
}
#[test]
fn variadic_call() {
let instr = Instr::Call(
"printf".into(),
vec![
(Type::Long, Value::Global("fmt".into())),
(Type::Word, Value::Const(0)),
],
Some(1),
);
assert_eq!(instr.to_string(), "call $printf(l $fmt, ..., w 0)");
}
#[test]
fn module_fmt_order() {
let mut module = Module::new();
let typedef = Arc::new(TypeDef::Regular {
ident: "test_type".into(),
align: None,
items: vec![(Type::Long, 1)],
});
module.add_type(typedef);
let mut func = Function::new(Linkage::public(), "test_func", Vec::new(), None);
let block = func.add_block("entry");
block.add_instr(Instr::Ret(None));
module.add_function(func);
let data = DataDef::new(
Linkage::private(),
"test_data",
None,
vec![(Type::Word, DataItem::Const(42))],
);
module.add_data(data);
let formatted = format!("{module}");
let type_pos = formatted
.find("type :test_type")
.expect("Type definition not found");
let func_pos = formatted
.find("export function $test_func")
.expect("Function not found");
let data_pos = formatted
.find("data $test_data")
.expect("Data definition not found");
assert!(
type_pos < func_pos,
"Type definition should appear before function"
);
assert!(
func_pos < data_pos,
"Function should appear before data definition"
);
}
#[test]
fn comparison_types() {
let ordered_cmp = Statement::Volatile(Instr::Cmp(
Type::Double,
Cmp::O,
Value::Temporary("a".into()),
Value::Temporary("b".into()),
));
assert_eq!(format!("{ordered_cmp}"), "cod %a, %b");
let unordered_cmp = Statement::Volatile(Instr::Cmp(
Type::Single,
Cmp::Uo,
Value::Temporary("a".into()),
Value::Temporary("b".into()),
));
assert_eq!(format!("{unordered_cmp}"), "cuos %a, %b");
let unsigned_lt = Statement::Volatile(Instr::Cmp(
Type::Word,
Cmp::Ult,
Value::Temporary("a".into()),
Value::Temporary("b".into()),
));
assert_eq!(format!("{unsigned_lt}"), "cultw %a, %b");
let unsigned_le = Statement::Volatile(Instr::Cmp(
Type::Long,
Cmp::Ule,
Value::Temporary("a".into()),
Value::Temporary("b".into()),
));
assert_eq!(format!("{unsigned_le}"), "culel %a, %b");
let unsigned_gt = Statement::Volatile(Instr::Cmp(
Type::Word,
Cmp::Ugt,
Value::Temporary("a".into()),
Value::Temporary("b".into()),
));
assert_eq!(format!("{unsigned_gt}"), "cugtw %a, %b");
let unsigned_ge = Statement::Volatile(Instr::Cmp(
Type::Long,
Cmp::Uge,
Value::Temporary("a".into()),
Value::Temporary("b".into()),
));
assert_eq!(format!("{unsigned_ge}"), "cugel %a, %b");
}
#[test]
fn unsigned_arithmetic_instructions() {
let udiv = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Udiv(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
assert_eq!(format!("{udiv}"), "%result =w udiv %a, %b");
let urem = Statement::Assign(
Value::Temporary("result".into()),
Type::Long,
Instr::Urem(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
assert_eq!(format!("{urem}"), "%result =l urem %a, %b");
}
#[test]
fn shift_instructions() {
let sar = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Sar(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
assert_eq!(format!("{sar}"), "%result =w sar %a, %b");
let shr = Statement::Assign(
Value::Temporary("result".into()),
Type::Long,
Instr::Shr(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
assert_eq!(format!("{shr}"), "%result =l shr %a, %b");
let shl = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Shl(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
assert_eq!(format!("{shl}"), "%result =w shl %a, %b");
}
#[test]
fn cast_instruction() {
let cast_int_to_float = Statement::Assign(
Value::Temporary("result".into()),
Type::Single,
Instr::Cast(Value::Temporary("a".into())),
);
assert_eq!(format!("{cast_int_to_float}"), "%result =s cast %a");
let cast_float_to_int = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Cast(Value::Temporary("f".into())),
);
assert_eq!(format!("{cast_float_to_int}"), "%result =w cast %f");
}
#[test]
fn extension_operations() {
let extsw = Statement::Assign(
Value::Temporary("result".into()),
Type::Long,
Instr::Extsw(Value::Temporary("a".into())),
);
assert_eq!(format!("{extsw}"), "%result =l extsw %a");
let extuw = Statement::Assign(
Value::Temporary("result".into()),
Type::Long,
Instr::Extuw(Value::Temporary("a".into())),
);
assert_eq!(format!("{extuw}"), "%result =l extuw %a");
let extsh = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Extsh(Value::Temporary("a".into())),
);
assert_eq!(format!("{extsh}"), "%result =w extsh %a");
let extuh = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Extuh(Value::Temporary("a".into())),
);
assert_eq!(format!("{extuh}"), "%result =w extuh %a");
let extsb = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Extsb(Value::Temporary("a".into())),
);
assert_eq!(format!("{extsb}"), "%result =w extsb %a");
let extub = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Extub(Value::Temporary("a".into())),
);
assert_eq!(format!("{extub}"), "%result =w extub %a");
}
#[test]
fn float_precision_conversion() {
let exts = Statement::Assign(
Value::Temporary("result".into()),
Type::Double,
Instr::Exts(Value::Temporary("a".into())),
);
assert_eq!(format!("{exts}"), "%result =d exts %a");
let truncd = Statement::Assign(
Value::Temporary("result".into()),
Type::Single,
Instr::Truncd(Value::Temporary("a".into())),
);
assert_eq!(format!("{truncd}"), "%result =s truncd %a");
}
#[test]
fn float_integer_conversions() {
let stosi = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Stosi(Value::Temporary("a".into())),
);
assert_eq!(format!("{stosi}"), "%result =w stosi %a");
let stoui = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Stoui(Value::Temporary("a".into())),
);
assert_eq!(format!("{stoui}"), "%result =w stoui %a");
let dtosi = Statement::Assign(
Value::Temporary("result".into()),
Type::Long,
Instr::Dtosi(Value::Temporary("a".into())),
);
assert_eq!(format!("{dtosi}"), "%result =l dtosi %a");
let dtoui = Statement::Assign(
Value::Temporary("result".into()),
Type::Long,
Instr::Dtoui(Value::Temporary("a".into())),
);
assert_eq!(format!("{dtoui}"), "%result =l dtoui %a");
let swtof = Statement::Assign(
Value::Temporary("result".into()),
Type::Single,
Instr::Swtof(Value::Temporary("a".into())),
);
assert_eq!(format!("{swtof}"), "%result =s swtof %a");
let uwtof = Statement::Assign(
Value::Temporary("result".into()),
Type::Single,
Instr::Uwtof(Value::Temporary("a".into())),
);
assert_eq!(format!("{uwtof}"), "%result =s uwtof %a");
let sltof = Statement::Assign(
Value::Temporary("result".into()),
Type::Double,
Instr::Sltof(Value::Temporary("a".into())),
);
assert_eq!(format!("{sltof}"), "%result =d sltof %a");
let ultof = Statement::Assign(
Value::Temporary("result".into()),
Type::Double,
Instr::Ultof(Value::Temporary("a".into())),
);
assert_eq!(format!("{ultof}"), "%result =d ultof %a");
}
#[test]
fn variadic_instructions() {
let vastart = Statement::Volatile(Instr::Vastart(Value::Temporary("ap".into())));
assert_eq!(format!("{vastart}"), "vastart %ap");
let vaarg = Statement::Assign(
Value::Temporary("arg".into()),
Type::Word,
Instr::Vaarg(Type::Word, Value::Temporary("ap".into())),
);
assert_eq!(format!("{vaarg}"), "%arg =w vaargw %ap");
}
#[test]
fn phi_instruction() {
let phi = Instr::Phi(vec![
("ift".into(), Value::Const(2)),
("iff".into(), Value::Temporary("3".into())),
]);
assert_eq!(format!("{phi}"), "phi @ift 2, @iff %3");
let phi = Statement::Assign(
Value::Temporary("result".into()),
Type::Word,
Instr::Phi(vec![
("start".into(), Value::Temporary("1".into())),
("loop".into(), Value::Global("tmp".into())),
]),
);
assert_eq!(format!("{phi}"), "%result =w phi @start %1, @loop $tmp");
let phi = Instr::Phi(vec![
("case1".into(), Value::Const(10)),
("case2".into(), Value::Const(20)),
("case3".into(), Value::Const(30)),
]);
assert_eq!(format!("{phi}"), "phi @case1 10, @case2 20, @case3 30");
let phi = Statement::Assign(
Value::Temporary("merged".into()),
Type::Long,
Instr::Phi(vec![
("path_a".into(), Value::Temporary("x".into())),
("path_b".into(), Value::Temporary("y".into())),
("path_c".into(), Value::Global("global_val".into())),
("path_d".into(), Value::Const(42)),
]),
);
assert_eq!(
format!("{phi}"),
"%merged =l phi @path_a %x, @path_b %y, @path_c $global_val, @path_d 42"
);
}
#[test]
fn halt_instruction() {
let hlt = Statement::Volatile(Instr::Hlt);
assert_eq!(format!("{hlt}"), "hlt");
}
#[test]
fn thread_local_linkage() {
let thread_local = Linkage::thread_local();
assert_eq!(format!("{thread_local}"), "thread ");
let exported_thread_local = Linkage::exported_thread_local();
assert_eq!(format!("{exported_thread_local}"), "export thread ");
let thread_local_with_section = Linkage::thread_local_with_section("data");
assert_eq!(
format!("{thread_local_with_section}"),
"thread section \"data\" "
);
let data_def = DataDef {
linkage: Linkage::thread_local(),
name: "thread_var".into(),
align: None,
items: vec![(Type::Word, DataItem::Const(42))],
};
assert_eq!(format!("{data_def}"), "thread data $thread_var = { w 42 }");
}
#[test]
fn zero_initialized_data() {
let zero_data = DataItem::Zero(1000);
assert_eq!(format!("{zero_data}"), "z 1000");
let data_def = DataDef {
linkage: Linkage::private(),
name: "zero_array".into(),
align: None,
items: vec![(Type::Byte, DataItem::Zero(1000))],
};
assert_eq!(format!("{data_def}"), "data $zero_array = { b z 1000 }");
let data_def = DataDef {
linkage: Linkage::private(),
name: "mixed_data".into(),
align: None,
items: vec![
(Type::Word, DataItem::Const(1)),
(Type::Byte, DataItem::Zero(10)),
(Type::Word, DataItem::Const(2)),
],
};
assert_eq!(
format!("{data_def}"),
"data $mixed_data = { w 1, b z 10, w 2 }"
);
}
#[test]
fn complex_block_with_multiple_instructions() {
let mut block = Block {
label: "test_block".into(),
items: Vec::new(),
};
block.assign_instr(
Value::Temporary("udiv_result".into()),
Type::Word,
Instr::Udiv(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
block.assign_instr(
Value::Temporary("shift_result".into()),
Type::Word,
Instr::Shl(Value::Temporary("a".into()), Value::Temporary("b".into())),
);
block.assign_instr(
Value::Temporary("cast_result".into()),
Type::Single,
Instr::Cast(Value::Temporary("shift_result".into())),
);
block.assign_instr(
Value::Temporary("cmp_result".into()),
Type::Word,
Instr::Cmp(
Type::Single,
Cmp::Uo,
Value::Temporary("cast_result".into()),
Value::Temporary("x".into()),
),
);
block.add_instr(Instr::Hlt);
let formatted = format!("{block}");
let lines: Vec<&str> = formatted.lines().collect();
assert_eq!(lines[0], "@test_block");
assert_eq!(lines[1], "\t%udiv_result =w udiv %a, %b");
assert_eq!(lines[2], "\t%shift_result =w shl %a, %b");
assert_eq!(lines[3], "\t%cast_result =s cast %shift_result");
assert_eq!(lines[4], "\t%cmp_result =w cuos %cast_result, %x");
assert_eq!(lines[5], "\thlt");
}
#[test]
fn assign_instr_aggregate_type_coercion() {
let mut block = Block {
label: "test_block".into(),
items: Vec::new(),
};
let typedef = Arc::new(TypeDef::Regular {
ident: "person".into(),
align: None,
items: vec![(Type::Long, 1), (Type::Word, 2), (Type::Byte, 1)],
});
block.assign_instr(
Value::Temporary("human".into()),
Type::aggregate(&typedef),
Instr::Alloc8(Type::aggregate(&typedef).size()),
);
block.assign_instr(
Value::Temporary("result".into()),
Type::aggregate(&typedef),
Instr::Call("new_person".into(), vec![], None),
);
let formatted = format!("{block}");
let lines: Vec<&str> = formatted.lines().collect();
assert_eq!(lines[0], "@test_block");
assert_eq!(lines[1], "\t%human =l alloc8 24");
assert_eq!(lines[2], "\t%result =:person call $new_person()");
}
#[test]
fn load_valid_types() {
let src = Value::Temporary("addr".into());
assert_eq!(
format!("{}", Instr::Load(Type::SignedByte, src.clone())),
"loadsb %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::UnsignedByte, src.clone())),
"loadub %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::SignedHalfword, src.clone())),
"loadsh %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::UnsignedHalfword, src.clone())),
"loaduh %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::Word, src.clone())),
"loadw %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::Long, src.clone())),
"loadl %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::Single, src.clone())),
"loads %addr"
);
assert_eq!(
format!("{}", Instr::Load(Type::Double, src.clone())),
"loadd %addr"
);
}
#[test]
#[should_panic(expected = "ambiguous sub-word load")]
fn load_byte_panics() {
let src = Value::Temporary("addr".into());
let _ = format!("{}", Instr::Load(Type::Byte, src));
}
#[test]
#[should_panic(expected = "ambiguous sub-word load")]
fn load_halfword_panics() {
let src = Value::Temporary("addr".into());
let _ = format!("{}", Instr::Load(Type::Halfword, src));
}
#[test]
fn store_valid_types() {
let dest = Value::Temporary("addr".into());
let val = Value::Temporary("val".into());
assert_eq!(
format!("{}", Instr::Store(Type::Byte, dest.clone(), val.clone())),
"storeb %val, %addr"
);
assert_eq!(
format!(
"{}",
Instr::Store(Type::SignedByte, dest.clone(), val.clone())
),
"storeb %val, %addr"
);
assert_eq!(
format!(
"{}",
Instr::Store(Type::UnsignedByte, dest.clone(), val.clone())
),
"storeb %val, %addr"
);
assert_eq!(
format!(
"{}",
Instr::Store(Type::Halfword, dest.clone(), val.clone())
),
"storeh %val, %addr"
);
assert_eq!(
format!(
"{}",
Instr::Store(Type::SignedHalfword, dest.clone(), val.clone())
),
"storeh %val, %addr"
);
assert_eq!(
format!(
"{}",
Instr::Store(Type::UnsignedHalfword, dest.clone(), val.clone())
),
"storeh %val, %addr"
);
assert_eq!(
format!("{}", Instr::Store(Type::Word, dest.clone(), val.clone())),
"storew %val, %addr"
);
assert_eq!(
format!("{}", Instr::Store(Type::Long, dest.clone(), val.clone())),
"storel %val, %addr"
);
assert_eq!(
format!("{}", Instr::Store(Type::Single, dest.clone(), val.clone())),
"stores %val, %addr"
);
assert_eq!(
format!("{}", Instr::Store(Type::Double, dest.clone(), val.clone())),
"stored %val, %addr"
);
}