1use vihaco::Instruction;
5use vihaco::value::{Type, Value};
6
7#[derive(Debug, Clone, PartialEq, Instruction, vihaco_parser::Parse)]
24#[instruction(width = 16)]
25pub enum Instruction {
26 #[delimiters(open = "", close = "", separator = " ")]
30 Span(u32, u32, u32),
31
32 Label,
34
35 #[token = "func_start"]
38 FunctionStart,
39 #[token = "func_end"]
41 FunctionEnd,
42
43 Breakpoint,
46
47 #[token = "br"]
50 #[delimiters(open = "", close = "", separator = "")]
51 Branch(#[parse_with = "crate::parse_helpers::never_u32"] u32),
52
53 #[token = "cond_br"]
55 #[delimiters(open = "", close = "", separator = ",")]
56 ConditionalBranch(
57 #[parse_with = "crate::parse_helpers::never_u32"] u32,
58 #[parse_with = "crate::parse_helpers::never_u32"] u32,
59 ),
60
61 #[token = "ret"]
64 #[delimiters(open = "", close = "", separator = "")]
65 Return(#[parse_with = "crate::parse_helpers::never_u32"] u32),
66
67 #[token = "call_indirect"]
69 IndirectCall,
70
71 #[token = "call"]
73 #[delimiters(open = "", close = "", separator = ",")]
74 Call(
75 #[parse_with = "crate::parse_helpers::never_u32"] u32,
76 #[parse_with = "crate::parse_helpers::never_u32"] u32,
77 ),
78
79 Halt,
81
82 Print,
85
86 #[delimiters(open = "", close = "", separator = " ")]
89 Load(#[parse_with = "crate::parse_helpers::cpu_type"] Type, u32),
90 #[delimiters(open = "", close = "", separator = " ")]
92 Store(#[parse_with = "crate::parse_helpers::cpu_type"] Type, u32),
93
94 Dup,
96
97 #[token = "heap_alloc"]
99 #[delimiters(open = "", close = "", separator = "")]
100 HeapAlloc(u32),
101
102 #[token = "get_item"]
104 GetItem,
105
106 #[token = "heap_dealloc"]
109 HeapDealloc,
110
111 #[token = "const"]
114 #[delimiters(open = "", close = "", separator = "")]
115 Const(#[parse_with = "crate::parse_helpers::cpu_const_value"] Value),
116
117 #[delimiters(open = "", close = "", separator = "")]
119 Add(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
120 #[delimiters(open = "", close = "", separator = "")]
121 Sub(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
122 #[delimiters(open = "", close = "", separator = "")]
123 Mul(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
124 #[delimiters(open = "", close = "", separator = "")]
125 Div(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
126 #[delimiters(open = "", close = "", separator = "")]
127 Rem(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
128 #[delimiters(open = "", close = "", separator = "")]
129 Neg(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
130
131 #[delimiters(open = "", close = "", separator = "")]
133 Shl(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
134 #[delimiters(open = "", close = "", separator = "")]
135 Shr(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
136 #[delimiters(open = "", close = "", separator = "")]
137 Rol(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
138 #[delimiters(open = "", close = "", separator = "")]
139 Ror(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
140 #[token = "bitand"]
141 #[delimiters(open = "", close = "", separator = "")]
142 BitAnd(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
143 #[token = "bitor"]
144 #[delimiters(open = "", close = "", separator = "")]
145 BitOr(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
146 #[token = "bitxor"]
147 #[delimiters(open = "", close = "", separator = "")]
148 BitXor(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
149
150 Not,
152 And,
153 Or,
154 Xor,
155
156 #[delimiters(open = "", close = "", separator = "")]
158 Eq(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
159 #[delimiters(open = "", close = "", separator = "")]
160 Ne(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
161 #[delimiters(open = "", close = "", separator = "")]
162 Lt(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
163 #[delimiters(open = "", close = "", separator = "")]
164 Gt(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
165 #[delimiters(open = "", close = "", separator = "")]
166 Le(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
167 #[delimiters(open = "", close = "", separator = "")]
168 Ge(#[parse_with = "crate::parse_helpers::cpu_type"] Type),
169}
170
171impl<T: Into<Value>> From<T> for Instruction {
172 fn from(value: T) -> Self {
173 Instruction::Const(value.into())
174 }
175}
176
177impl vihaco::CanonicalInstructionSyntax for Instruction {
178 fn variants() -> &'static [vihaco::CanonicalInstructionVariantSyntax] {
179 &[
180 vihaco::CanonicalInstructionVariantSyntax {
181 mnemonic: "cpu::const_i64",
182 operands: &[vihaco::OperandKind::I64],
183 },
184 vihaco::CanonicalInstructionVariantSyntax {
185 mnemonic: "cpu::const_f64",
186 operands: &[vihaco::OperandKind::F64],
187 },
188 vihaco::CanonicalInstructionVariantSyntax {
189 mnemonic: "cpu::const_bool",
190 operands: &[vihaco::OperandKind::Bool],
191 },
192 vihaco::CanonicalInstructionVariantSyntax {
193 mnemonic: "cpu::const_u64",
194 operands: &[vihaco::OperandKind::NonNegativeU64],
195 },
196 vihaco::CanonicalInstructionVariantSyntax {
197 mnemonic: "cpu::fn_ref",
198 operands: &[vihaco::OperandKind::Symbol],
199 },
200 vihaco::CanonicalInstructionVariantSyntax {
201 mnemonic: "cpu::call_direct",
202 operands: &[vihaco::OperandKind::Symbol],
203 },
204 ]
205 }
206}
207
208#[cfg(test)]
209#[allow(clippy::approx_constant)]
210mod parse_tests {
211 use super::Instruction;
212 use chumsky::Parser as _;
213 use vihaco::value::{Type, Value};
214 use vihaco_parser_core::Parse;
215
216 fn parse(input: &str) -> Instruction {
217 Instruction::parser()
218 .parse(input)
219 .into_result()
220 .unwrap_or_else(|e| panic!("parse({input:?}) failed: {e:?}"))
221 }
222
223 #[test]
224 fn parses_unit_variants() {
225 for (input, expected) in [
226 ("halt", Instruction::Halt),
227 ("print", Instruction::Print),
228 ("dup", Instruction::Dup),
229 ("breakpoint", Instruction::Breakpoint),
230 ("label", Instruction::Label),
231 ("func_start", Instruction::FunctionStart),
232 ("func_end", Instruction::FunctionEnd),
233 ("get_item", Instruction::GetItem),
234 ("not", Instruction::Not),
235 ("and", Instruction::And),
236 ("or", Instruction::Or),
237 ("xor", Instruction::Xor),
238 ("call_indirect", Instruction::IndirectCall),
239 ] {
240 assert_eq!(parse(input), expected, "input {input:?}");
241 }
242 }
243
244 #[test]
245 fn parses_typed_arith() {
246 assert_eq!(parse("add.i64"), Instruction::Add(Type::I64));
247 assert_eq!(parse("sub.f64"), Instruction::Sub(Type::F64));
248 assert_eq!(parse("mul.u32"), Instruction::Mul(Type::U32));
249 assert_eq!(parse("div.u64"), Instruction::Div(Type::U64));
250 assert_eq!(parse("lt.i64"), Instruction::Lt(Type::I64));
251 assert_eq!(parse("ge.f64"), Instruction::Ge(Type::F64));
252 assert_eq!(parse("bitand.i64"), Instruction::BitAnd(Type::I64));
253 assert_eq!(parse("shl.u64"), Instruction::Shl(Type::U64));
254 }
255
256 #[test]
257 fn parses_load_store() {
258 assert_eq!(parse("load.i64 7"), Instruction::Load(Type::I64, 7));
259 assert_eq!(parse("store.f64 42"), Instruction::Store(Type::F64, 42));
260 }
261
262 #[test]
263 fn parses_heap_alloc() {
264 assert_eq!(parse("heap_alloc 5"), Instruction::HeapAlloc(5));
265 }
266
267 #[test]
268 fn parses_span() {
269 assert_eq!(parse("span 0 1 2"), Instruction::Span(0, 1, 2));
270 }
271
272 #[test]
273 fn parses_const_numeric_flavors() {
274 assert_eq!(parse("const.i64 42"), Instruction::Const(Value::I64(42)));
275 assert_eq!(parse("const.u64 7"), Instruction::Const(Value::U64(7)));
276 assert_eq!(parse("const.u32 3"), Instruction::Const(Value::U32(3)));
277 assert_eq!(
278 parse("const.f64 3.14"),
279 Instruction::Const(Value::F64(3.14))
280 );
281 assert_eq!(
282 parse("const.bool true"),
283 Instruction::Const(Value::Bool(true))
284 );
285 }
286
287 #[test]
288 fn defers_symbolic_branch_to_orchestrator() {
289 assert!(Instruction::parser().parse("br @body").has_errors());
292 assert!(Instruction::parser().parse("cond_br @a, @b").has_errors());
293 assert!(Instruction::parser().parse("call @main, 0").has_errors());
294 assert!(Instruction::parser().parse("ret").has_errors());
295 }
296
297 #[test]
298 fn defers_const_string_to_orchestrator() {
299 assert!(
301 Instruction::parser()
302 .parse("const.str \"hello\"")
303 .has_errors()
304 );
305 }
306}