From 1bda0e8c9862d04e230abee09f17607c11d9d152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Ron=C4=8Devi=C4=87?= Date: Fri, 30 Aug 2024 14:22:52 +0200 Subject: [PATCH] Support FuelVM `wqxx` instructions in ASM blocks (#6484) ## Description This PR adds support for FuelVM `wqxx` instructions in ASM blocks. Some of those instructions were up to now partially supported in IR and ASM generation but not in ASM blocks. The immediate motivation for adding support for these instructions is the need for `wqml` instruction in the new implementation of `u256::pow()`. Additionally, the PR adds expressive diagnostics for `UnrecognizedOpCode` parsing error. ## Checklist - [ ] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [ ] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --- Cargo.lock | 1 + sway-ast/src/expr/op_code.rs | 7 + sway-core/src/asm_lang/allocated_ops.rs | 22 +++ sway-core/src/asm_lang/mod.rs | 30 ++++ sway-core/src/asm_lang/virtual_ops.rs | 49 +++++++ sway-error/Cargo.toml | 1 + sway-error/src/error.rs | 25 ++++ sway-error/src/formatting.rs | 80 +++++++++- sway-error/src/parser_error.rs | 6 +- sway-parse/src/expr/op_code.rs | 15 +- .../asm_unrecognized_opcode/Forc.lock | 3 + .../Forc.toml | 2 +- .../asm_unrecognized_opcode/src/main.sw | 15 ++ .../asm_unrecognized_opcode/test.toml | 28 ++++ .../should_fail/unrecognized_opcode/Forc.lock | 3 - .../unrecognized_opcode/src/main.sw | 8 - .../should_fail/unrecognized_opcode/test.toml | 4 - .../language/asm_wqxx_instructions/Forc.lock | 13 ++ .../language/asm_wqxx_instructions/Forc.toml | 8 + .../asm_wqxx_instructions/src/main.sw | 137 ++++++++++++++++++ .../language/asm_wqxx_instructions/test.toml | 1 + 21 files changed, 435 insertions(+), 23 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.lock rename test/src/e2e_vm_tests/test_programs/should_fail/{unrecognized_opcode => asm_unrecognized_opcode}/Forc.toml (79%) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/test.toml delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.lock delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/src/main.sw delete mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/test.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/test.toml diff --git a/Cargo.lock b/Cargo.lock index d4d8ea1951c..d394f5c5c2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6911,6 +6911,7 @@ dependencies = [ "in_definite", "num-traits", "smallvec", + "strsim 0.11.1", "sway-types", "thiserror", "uwuify", diff --git a/sway-ast/src/expr/op_code.rs b/sway-ast/src/expr/op_code.rs index 3bb40b2b91c..acfc54411eb 100644 --- a/sway-ast/src/expr/op_code.rs +++ b/sway-ast/src/expr/op_code.rs @@ -200,6 +200,13 @@ define_op_codes!( (Srli, SrliOpcode, "srli", (ret: reg, lhs: reg, rhs: imm)), (Sub, SubOpcode, "sub", (ret: reg, lhs: reg, rhs: reg)), (Subi, SubiOpcode, "subi", (ret: reg, lhs: reg, rhs: imm)), + (Wqcm, WqcmOpcode, "wqcm", (ret: reg, lhs: reg, rhs: reg, op_mode: imm)), + (Wqop, WqopOpcode, "wqop", (ret: reg, lhs: reg, rhs: reg, op_mode: imm)), + (Wqml, WqmlOpcode, "wqml", (ret: reg, lhs: reg, rhs: reg, indirect: imm)), + (Wqdv, WqdvOpcode, "wqdv", (ret: reg, lhs: reg, rhs: reg, indirect: imm)), + (Wqmd, WqmdOpcode, "wqmd", (ret: reg, lhs_a: reg, lhs_b: reg, rhs: reg)), + (Wqam, WqamOpcode, "wqam", (ret: reg, lhs_a: reg, lhs_b: reg, rhs: reg)), + (Wqmm, WqmmOpcode, "wqmm", (ret: reg, lhs_a: reg, lhs_b: reg, rhs: reg)), (Xor, XorOpcode, "xor", (ret: reg, lhs: reg, rhs: reg)), (Xori, XoriOpcode, "xori", (ret: reg, lhs: reg, rhs: imm)), /* Control Flow Instructions */ diff --git a/sway-core/src/asm_lang/allocated_ops.rs b/sway-core/src/asm_lang/allocated_ops.rs index 23ff1c11acd..5590279cde7 100644 --- a/sway-core/src/asm_lang/allocated_ops.rs +++ b/sway-core/src/asm_lang/allocated_ops.rs @@ -110,6 +110,12 @@ pub(crate) enum AllocatedOpcode { AllocatedRegister, VirtualImmediate06, ), + WQMD( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), WQCM( AllocatedRegister, AllocatedRegister, @@ -122,6 +128,12 @@ pub(crate) enum AllocatedOpcode { AllocatedRegister, AllocatedRegister, ), + WQMM( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), /* Control Flow Instructions */ JMP(AllocatedRegister), @@ -303,8 +315,10 @@ impl AllocatedOpcode { WQOP(_, _, _, _) => vec![], WQML(_, _, _, _) => vec![], WQDV(_, _, _, _) => vec![], + WQMD(_, _, _, _) => vec![], WQCM(r1, _, _, _) => vec![r1], WQAM(_, _, _, _) => vec![], + WQMM(_, _, _, _) => vec![], /* Control Flow Instructions */ JMP(_r1) => vec![], @@ -428,8 +442,10 @@ impl fmt::Display for AllocatedOpcode { WQOP(a, b, c, d) => write!(fmtr, "wqop {a} {b} {c} {d}"), WQML(a, b, c, d) => write!(fmtr, "wqml {a} {b} {c} {d}"), WQDV(a, b, c, d) => write!(fmtr, "wqdv {a} {b} {c} {d}"), + WQMD(a, b, c, d) => write!(fmtr, "wqmd {a} {b} {c} {d}"), WQCM(a, b, c, d) => write!(fmtr, "wqcm {a} {b} {c} {d}"), WQAM(a, b, c, d) => write!(fmtr, "wqam {a} {b} {c} {d}"), + WQMM(a, b, c, d) => write!(fmtr, "wqmm {a} {b} {c} {d}"), /* Control Flow Instructions */ JMP(a) => write!(fmtr, "jmp {a}"), @@ -594,12 +610,18 @@ impl AllocatedOp { WQDV(a, b, c, d) => { op::WQDV::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.value.into()).into() } + WQMD(a, b, c, d) => { + op::WQMD::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.to_reg_id()).into() + } WQCM(a, b, c, d) => { op::WQCM::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.value.into()).into() } WQAM(a, b, c, d) => { op::WQAM::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.to_reg_id()).into() } + WQMM(a, b, c, d) => { + op::WQMM::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.to_reg_id()).into() + } /* Control Flow Instructions */ JMP(a) => op::JMP::new(a.to_reg_id()).into(), diff --git a/sway-core/src/asm_lang/mod.rs b/sway-core/src/asm_lang/mod.rs index 641fbfff9f6..5e1bcaded52 100644 --- a/sway-core/src/asm_lang/mod.rs +++ b/sway-core/src/asm_lang/mod.rs @@ -415,6 +415,34 @@ impl Op { let (r1, r2, imm) = two_regs_imm_12(handler, args, immediate, whole_op_span)?; VirtualOp::SUBI(r1, r2, imm) } + "wqcm" => { + let (r1, r2, r3, imm) = three_regs_imm_06(handler, args, immediate, whole_op_span)?; + VirtualOp::WQCM(r1, r2, r3, imm) + } + "wqop" => { + let (r1, r2, r3, imm) = three_regs_imm_06(handler, args, immediate, whole_op_span)?; + VirtualOp::WQOP(r1, r2, r3, imm) + } + "wqml" => { + let (r1, r2, r3, imm) = three_regs_imm_06(handler, args, immediate, whole_op_span)?; + VirtualOp::WQML(r1, r2, r3, imm) + } + "wqdv" => { + let (r1, r2, r3, imm) = three_regs_imm_06(handler, args, immediate, whole_op_span)?; + VirtualOp::WQDV(r1, r2, r3, imm) + } + "wqmd" => { + let (r1, r2, r3, r4) = four_regs(handler, args, immediate, whole_op_span)?; + VirtualOp::WQMD(r1, r2, r3, r4) + } + "wqam" => { + let (r1, r2, r3, r4) = four_regs(handler, args, immediate, whole_op_span)?; + VirtualOp::WQAM(r1, r2, r3, r4) + } + "wqmm" => { + let (r1, r2, r3, r4) = four_regs(handler, args, immediate, whole_op_span)?; + VirtualOp::WQMM(r1, r2, r3, r4) + } "xor" => { let (r1, r2, r3) = three_regs(handler, args, immediate, whole_op_span)?; VirtualOp::XOR(r1, r2, r3) @@ -1148,8 +1176,10 @@ impl fmt::Display for VirtualOp { WQOP(a, b, c, d) => write!(fmtr, "wqop {a} {b} {c} {d}"), WQML(a, b, c, d) => write!(fmtr, "wqml {a} {b} {c} {d}"), WQDV(a, b, c, d) => write!(fmtr, "wqdv {a} {b} {c} {d}"), + WQMD(a, b, c, d) => write!(fmtr, "wqmd {a} {b} {c} {d}"), WQCM(a, b, c, d) => write!(fmtr, "wqcm {a} {b} {c} {d}"), WQAM(a, b, c, d) => write!(fmtr, "wqam {a} {b} {c} {d}"), + WQMM(a, b, c, d) => write!(fmtr, "wqmm {a} {b} {c} {d}"), /* Control Flow Instructions */ JMP(a) => write!(fmtr, "jmp {a}"), diff --git a/sway-core/src/asm_lang/virtual_ops.rs b/sway-core/src/asm_lang/virtual_ops.rs index 29b7da0030e..ffe3509b7cd 100644 --- a/sway-core/src/asm_lang/virtual_ops.rs +++ b/sway-core/src/asm_lang/virtual_ops.rs @@ -75,6 +75,12 @@ pub(crate) enum VirtualOp { VirtualRegister, VirtualImmediate06, ), + WQMD( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), WQCM( VirtualRegister, VirtualRegister, @@ -87,6 +93,12 @@ pub(crate) enum VirtualOp { VirtualRegister, VirtualRegister, ), + WQMM( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), /* Control Flow Instructions */ JMP(VirtualRegister), @@ -263,8 +275,10 @@ impl VirtualOp { WQOP(r1, r2, r3, _) => vec![r1, r2, r3], WQML(r1, r2, r3, _) => vec![r1, r2, r3], WQDV(r1, r2, r3, _) => vec![r1, r2, r3], + WQMD(r1, r2, r3, r4) => vec![r1, r2, r3, r4], WQCM(r1, r2, r3, _) => vec![r1, r2, r3], WQAM(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + WQMM(r1, r2, r3, r4) => vec![r1, r2, r3, r4], /* Control Flow Instructions */ JMP(r1) => vec![r1], @@ -399,8 +413,10 @@ impl VirtualOp { WQOP(_, _, _, _) | WQML(_, _, _, _) | WQDV(_, _, _, _) + | WQMD(_, _, _, _) | WQCM(_, _, _, _) | WQAM(_, _, _, _) + | WQMM(_, _, _, _) | JMP(_) | JI(_) | JNE(_, _, _) @@ -493,8 +509,10 @@ impl VirtualOp { | WQOP(_, _, _, _) | WQML(_, _, _, _) | WQDV(_, _, _, _) + | WQMD(_, _, _, _) | WQCM(_, _, _, _) | WQAM(_, _, _, _) + | WQMM(_, _, _, _) // Cryptographic | ECK1(_, _, _) | ECR1(_, _, _) @@ -596,11 +614,16 @@ impl VirtualOp { SUBI(_r1, r2, _i) => vec![r2], XOR(_r1, r2, r3) => vec![r2, r3], XORI(_r1, r2, _i) => vec![r2], + // Note that most of the `WQ..` instructions *read* from the `r1` result register, + // because the register itself does not contain the result, but provides the + // memory address at which the result will be stored. WQOP(r1, r2, r3, _) => vec![r1, r2, r3], WQML(r1, r2, r3, _) => vec![r1, r2, r3], WQDV(r1, r2, r3, _) => vec![r1, r2, r3], + WQMD(r1, r2, r3, r4) => vec![r1, r2, r3, r4], WQCM(_, r2, r3, _) => vec![r2, r3], WQAM(r1, r2, r3, r4) => vec![r1, r2, r3, r4], + WQMM(r1, r2, r3, r4) => vec![r1, r2, r3, r4], /* Control Flow Instructions */ JMP(r1) => vec![r1], @@ -718,8 +741,10 @@ impl VirtualOp { WQOP(_, _, _, _) => vec![], WQML(_, _, _, _) => vec![], WQDV(_, _, _, _) => vec![], + WQMD(_, _, _, _) => vec![], WQCM(r1, _, _, _) => vec![r1], WQAM(_, _, _, _) => vec![], + WQMM(_, _, _, _) => vec![], /* Control Flow Instructions */ JMP(_r1) => vec![], @@ -987,6 +1012,12 @@ impl VirtualOp { update_reg(reg_to_reg_map, r3), i.clone(), ), + WQMD(r1, r2, r3, r4) => Self::WQMD( + update_reg(reg_to_reg_map, r1), + update_reg(reg_to_reg_map, r2), + update_reg(reg_to_reg_map, r3), + update_reg(reg_to_reg_map, r4), + ), WQCM(r1, r2, r3, i) => Self::WQCM( update_reg(reg_to_reg_map, r1), update_reg(reg_to_reg_map, r2), @@ -999,6 +1030,12 @@ impl VirtualOp { update_reg(reg_to_reg_map, r3), update_reg(reg_to_reg_map, r4), ), + WQMM(r1, r2, r3, r4) => Self::WQMM( + update_reg(reg_to_reg_map, r1), + update_reg(reg_to_reg_map, r2), + update_reg(reg_to_reg_map, r3), + update_reg(reg_to_reg_map, r4), + ), /* Control Flow Instructions */ JMP(r1) => Self::JMP(update_reg(reg_to_reg_map, r1)), @@ -1464,6 +1501,12 @@ impl VirtualOp { map_reg(&mapping, reg3), imm.clone(), ), + WQMD(reg1, reg2, reg3, reg4) => AllocatedOpcode::WQMD( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), WQCM(reg1, reg2, reg3, imm) => AllocatedOpcode::WQCM( map_reg(&mapping, reg1), map_reg(&mapping, reg2), @@ -1476,6 +1519,12 @@ impl VirtualOp { map_reg(&mapping, reg3), map_reg(&mapping, reg4), ), + WQMM(reg1, reg2, reg3, reg4) => AllocatedOpcode::WQMM( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), /* Control Flow Instructions */ JMP(reg1) => AllocatedOpcode::JMP(map_reg(&mapping, reg1)), diff --git a/sway-error/Cargo.toml b/sway-error/Cargo.toml index 506e1badc6c..8cc56791be2 100644 --- a/sway-error/Cargo.toml +++ b/sway-error/Cargo.toml @@ -13,6 +13,7 @@ either = "1.9.0" in_definite = "1.0.0" num-traits = "0.2.14" smallvec = "1.7" +strsim = "0.11.1" sway-types = { version = "0.63.3", path = "../sway-types" } thiserror = "1.0" uwuify = { version = "^0.2", optional = true } diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 2131c63f238..ec85cf3298b 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -2646,6 +2646,31 @@ impl ToDiagnostic for CompileError { format!("{} E.g., `*ref_to_mutable_value` or `*max_mut(&mut x, &mut y)`.", Indent::Single), ] }, + ParseErrorKind::UnrecognizedOpCode { known_op_codes } => Diagnostic { + reason: Some(Reason::new(code(1), "Assembly instruction is unknown".to_string())), + issue: Issue::error( + source_engine, + error.span.clone(), + format!("\"{}\" is not a known assembly instruction.", + error.span.as_str() + ) + ), + hints: { + let suggestions = &did_you_mean(error.span.as_str(), known_op_codes.iter(), 2); + if suggestions.is_empty() { + vec![] + } else { + vec![ + Hint::help( + source_engine, + error.span.clone(), + format!("Did you mean {}?", sequence_to_str_or(suggestions, Enclosing::DoubleQuote, 2)) + ), + ] + } + }, + help: vec![] + }, _ => Diagnostic { // TODO: Temporary we use self here to achieve backward compatibility. // In general, self must not be used and will not be used once we diff --git a/sway-error/src/formatting.rs b/sway-error/src/formatting.rs index a81f289df4b..b4483424d6d 100644 --- a/sway-error/src/formatting.rs +++ b/sway-error/src/formatting.rs @@ -3,7 +3,7 @@ use std::{ borrow::Cow, - cmp, + cmp::{self, Ordering}, fmt::{self, Display}, }; @@ -95,6 +95,42 @@ impl Display for Indent { /// /// Panics if the `sequence` is empty, or `max_items` is zero. pub(crate) fn sequence_to_str(sequence: &[T], enclosing: Enclosing, max_items: usize) -> String +where + T: Display, +{ + sequence_to_str_impl(sequence, enclosing, max_items, "and") +} + +/// Returns reading-friendly textual representation of the `sequence`, with comma-separated +/// items and each item optionally enclosed in the specified `enclosing`. +/// If the sequence has more than `max_items` the remaining items are replaced +/// with the text "or more". +/// +/// E.g.: +/// [a] => "a" +/// [a, b] => "a" or "b" +/// [a, b, c] => "a", "b" or "c" +/// [a, b, c, d] => "a", "b", "c" or one more +/// [a, b, c, d, e] => "a", "b", "c" or two more +/// +/// Panics if the `sequence` is empty, or `max_items` is zero. +pub(crate) fn sequence_to_str_or( + sequence: &[T], + enclosing: Enclosing, + max_items: usize, +) -> String +where + T: Display, +{ + sequence_to_str_impl(sequence, enclosing, max_items, "or") +} + +fn sequence_to_str_impl( + sequence: &[T], + enclosing: Enclosing, + max_items: usize, + and_or: &str, +) -> String where T: Display, { @@ -115,12 +151,13 @@ where if !remaining.is_empty() { format!( - "{}, and {} more", + "{}, {} {} more", to_display .iter() .map(fmt_item) .collect::>() .join(", "), + and_or, number_to_str(remaining.len()) ) } else { @@ -128,10 +165,15 @@ where [] => unreachable!("There must be at least one item in the sequence."), [item] => fmt_item(item), [first_item, second_item] => { - format!("{} and {}", fmt_item(first_item), fmt_item(second_item)) + format!( + "{} {} {}", + fmt_item(first_item), + and_or, + fmt_item(second_item) + ) } _ => format!( - "{}, and {}", + "{}, {} {}", to_display .split_last() .unwrap() @@ -140,6 +182,7 @@ where .map(fmt_item) .collect::>() .join(", "), + and_or, fmt_item(to_display.last().unwrap()) ), } @@ -290,3 +333,32 @@ pub(crate) fn first_line(text: &str, with_ellipses: bool) -> Cow { Cow::Owned(text[..index_of_new_line].to_string() + if with_ellipses { "..." } else { "" }) } } + +/// Finds strings from an iterable of `possible_values` similar to a given value `v`. +/// Returns a vector of all possible values that exceed a similarity threshold, +/// sorted by similarity (most similar comes first). The returned vector will have +/// at most `max_num_of_suggestions` elements. +/// +/// The implementation is taken and adapted from the [Clap project](https://github.com/clap-rs/clap/blob/50f7646cf72dd7d4e76d9284d76bdcdaceb7c049/clap_builder/src/parser/features/suggestions.rs#L11). +pub(crate) fn did_you_mean( + v: &str, + possible_values: I, + max_num_of_suggestions: usize, +) -> Vec +where + T: AsRef, + I: IntoIterator, +{ + let mut candidates: Vec<_> = possible_values + .into_iter() + .map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned())) + // Confidence of 0.7 so that bar -> baz is suggested. + .filter(|(confidence, _)| *confidence > 0.7) + .collect(); + candidates.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal)); + candidates + .into_iter() + .take(max_num_of_suggestions) + .map(|(_, pv)| pv) + .collect() +} diff --git a/sway-error/src/parser_error.rs b/sway-error/src/parser_error.rs index 9f7d4e364f7..21e417801cf 100644 --- a/sway-error/src/parser_error.rs +++ b/sway-error/src/parser_error.rs @@ -12,8 +12,10 @@ pub enum ParseErrorKind { ExpectedAnItemAfterDocComment, #[error("Expected a comma or closing parenthesis in function arguments.")] ExpectedCommaOrCloseParenInFnArgs, - #[error("Unrecognized op code.")] - UnrecognizedOpCode, + #[error("Unknown assembly instruction.")] + UnrecognizedOpCode { + known_op_codes: &'static [&'static str], + }, #[error("Unexpected token in statement.")] UnexpectedTokenInStatement, #[error("This expression cannot be assigned to.")] diff --git a/sway-parse/src/expr/op_code.rs b/sway-parse/src/expr/op_code.rs index f1819e6223b..1281ca4bae3 100644 --- a/sway-parse/src/expr/op_code.rs +++ b/sway-parse/src/expr/op_code.rs @@ -6,6 +6,10 @@ use sway_types::{Ident, Spanned}; macro_rules! define_op_codes ( ( $(($op_name:ident, $ty_name:ident, $s:literal, ($($arg_name:ident),*)),)* ) => { + pub const OP_CODES: &'static [&'static str] = &[ + $($s),* + ]; + pub fn parse_instruction(ident: Ident, parser: &mut Parser) -> ParseResult { match ident.as_str() { $($s => { @@ -16,7 +20,9 @@ macro_rules! define_op_codes ( },)* _ => { let span = ident.span().clone(); - Err(parser.emit_error_with_span(ParseErrorKind::UnrecognizedOpCode, span)) + Err(parser.emit_error_with_span(ParseErrorKind::UnrecognizedOpCode { + known_op_codes: OP_CODES, + }, span)) }, } } @@ -54,6 +60,13 @@ define_op_codes!( (Srli, SrliOpcode, "srli", (ret, lhs, rhs)), (Sub, SubOpcode, "sub", (ret, lhs, rhs)), (Subi, SubiOpcode, "subi", (ret, lhs, rhs)), + (Wqcm, WqcmOpcode, "wqcm", (ret, lhs, rhs, op_mode)), + (Wqop, WqopOpcode, "wqop", (ret, lhs, rhs, op_mode)), + (Wqml, WqmlOpcode, "wqml", (ret, lhs, rhs, indirect)), + (Wqdv, WqdvOpcode, "wqdv", (ret, lhs, rhs, indirect)), + (Wqmd, WqmdOpcode, "wqmd", (ret, lhs_a, lhs_b, rhs)), + (Wqam, WqamOpcode, "wqam", (ret, lhs_a, lhs_b, rhs)), + (Wqmm, WqmmOpcode, "wqmm", (ret, lhs_a, lhs_b, rhs)), (Xor, XorOpcode, "xor", (ret, lhs, rhs)), (Xori, XoriOpcode, "xori", (ret, lhs, rhs)), /* Control Flow Instructions */ diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.lock new file mode 100644 index 00000000000..ce1b03b30fa --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "asm_unrecognized_opcode" +source = "member" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.toml similarity index 79% rename from test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.toml rename to test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.toml index c6b847f7ff8..5667453e56f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/Forc.toml @@ -2,7 +2,7 @@ authors = ["Fuel Labs "] license = "Apache-2.0" implicit-std = false -name = "unrecognized_opcode" +name = "asm_unrecognized_opcode" entry = "main.sw" [dependencies] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/src/main.sw new file mode 100644 index 00000000000..c524244eb9e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/src/main.sw @@ -0,0 +1,15 @@ +library; + +pub fn test() { + asm (r1: 0, r2: 0, r3: 0) { + blobbb r1 r2 r3; + }; + + asm (r1: 0, r2: 0, r3: 0) { + modd r1 r2 r3; + }; + + asm (r1: 0, r2: 0, r3: 0) { + ttllyunknwn r1 r2 r3; + }; +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/test.toml new file mode 100644 index 00000000000..963dd58630e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/asm_unrecognized_opcode/test.toml @@ -0,0 +1,28 @@ +category = "fail" + +#check: $()error +#sameln: $()Assembly instruction is unknown +#check: $()blobbb r1 r2 r3; +#nextln: $()"blobbb" is not a known assembly instruction. +#nextln: $()Did you mean "blob"? + +#check: $()Expected an expression + +#check: $()error +#sameln: $()Assembly instruction is unknown +#check: $()modd r1 r2 r3; +#nextln: $()"modd" is not a known assembly instruction. +#nextln: $()Did you mean "mod" or "modi"? + +#check: $()Expected an expression + +#check: $()error +#sameln: $()Assembly instruction is unknown +#check: $()ttllyunknwn r1 r2 r3; +#nextln: $()"ttllyunknwn" is not a known assembly instruction. + +#check: $()Expected an expression + +#not: $()Did you mean + +#check: $()6 errors. \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.lock deleted file mode 100644 index 1b37fb7e1f1..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/Forc.lock +++ /dev/null @@ -1,3 +0,0 @@ -[[package]] -name = 'unrecognized_opcode' -source = 'member' diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/src/main.sw deleted file mode 100644 index 1fc663e5516..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/src/main.sw +++ /dev/null @@ -1,8 +0,0 @@ -script; - -fn main() { - let _x = asm (r1, r2: 0, r3: 0) { - modd r1 r2 r3; - r1: u64 - }; -} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/test.toml deleted file mode 100644 index 5354716275a..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/unrecognized_opcode/test.toml +++ /dev/null @@ -1,4 +0,0 @@ -category = "fail" - - # check: $()modd r1 r2 r3; - # check: $()Unrecognized op code. diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.lock new file mode 100644 index 00000000000..3b576dee330 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "asm_wqxx_instructions" +source = "member" +dependencies = ["std"] + +[[package]] +name = "core" +source = "path+from-root-DEA77F133437397D" + +[[package]] +name = "std" +source = "path+from-root-DEA77F133437397D" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.toml new file mode 100644 index 00000000000..c44f1f66715 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "asm_wqxx_instructions" + +[dependencies] +std = { path = "../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/src/main.sw new file mode 100644 index 00000000000..3203aa3b494 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/src/main.sw @@ -0,0 +1,137 @@ +library; + +const ZERO: b256 = b256::zero(); +const ONE: b256 = 0x0000000000000000000000000000000000000000000000000000000000000001; +const TWO: b256 = 0x0000000000000000000000000000000000000000000000000000000000000002; +const THREE: b256 = 0x0000000000000000000000000000000000000000000000000000000000000003; +const FOUR: b256 = 0x0000000000000000000000000000000000000000000000000000000000000004; +const FIVE: b256 = 0x0000000000000000000000000000000000000000000000000000000000000005; +const NINE: b256 = 0x0000000000000000000000000000000000000000000000000000000000000009; +const TEN: b256 = 0x000000000000000000000000000000000000000000000000000000000000000A; + +#[test] +fn wqml() { + let mut res = ZERO; + + let a: b256 = TWO; + let b: b256 = TWO; + + asm(res: res, a: a, b: b) { + wqml res a b i48; + } + + assert_eq(res, FOUR); + + let a: u64 = 5; + let b: u64 = 2; + + asm(res: res, a: a, b: b) { + wqml res a b i0; + } + + assert_eq(res, TEN); + + asm(res: res, a: ZERO, b: ZERO) { + wqml res a b i48; + } + + assert_eq(res, ZERO); + + let a: b256 = THREE; + let b: u64 = 3; + + asm(res: res, a: a, b: b) { + wqml res a b i16; + } + + assert_eq(res, NINE); +} + +#[test] +fn all_in_one() { + // WQCM + let a: b256 = TWO; + let b: b256 = TWO; + + let bool_res = asm(b_res, a: a, b: b) { + wqcm b_res a b i32; // a == b + b_res: bool + }; + + assert_eq(bool_res, true); + + // WQOP + let mut res = TEN; + + let a: b256 = TEN; + let b: b256 = NINE; + + asm(res: res, a: a, b: b) { + wqop res a b i33; // a - b + }; + + assert_eq(res, ONE); + + // WQML + let mut res = ZERO; + + let a: b256 = TWO; + let b: b256 = TWO; + + asm(res: res, a: a, b: b) { + wqml res a b i48; // 2 * 2 + } + + assert_eq(res, FOUR); + + // WQDV + let mut res = ZERO; + + let a: b256 = TEN; + let b: b256 = FIVE; + + asm(res: res, a: a, b: b) { + wqdv res a b i32; // 10 / 5 + } + + assert_eq(res, TWO); + + // WQMD + let mut res = ZERO; + + let a: b256 = TEN; + let b: b256 = TWO; + let c: b256 = FIVE; + + asm(res: res, a: a, b: b, c: c) { + wqmd res a b c; // (10 * 2) / 5 + } + + assert_eq(res, FOUR); + + // WQAM + let mut res = ZERO; + + let a: b256 = TEN; + let b: b256 = TWO; + let c: b256 = FIVE; + + asm(res: res, a: a, b: b, c: c) { + wqam res a b c; // (10 + 2) % 5 + } + + assert_eq(res, TWO); + + // WQMM + let mut res = ZERO; + + let a: b256 = TEN; + let b: b256 = TWO; + let c: b256 = THREE; + + asm(res: res, a: a, b: b, c: c) { + wqmm res a b c; // (10 * 2) % 3 + } + + assert_eq(res, TWO); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/test.toml new file mode 100644 index 00000000000..f1958c1b086 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_wqxx_instructions/test.toml @@ -0,0 +1 @@ +category = "unit_tests_pass" \ No newline at end of file