From fccc8a928f81bdefca4ef36edb764eb84bf685c0 Mon Sep 17 00:00:00 2001 From: zees-dev <63374656+zees-dev@users.noreply.github.com> Date: Sat, 22 Feb 2025 13:47:48 +1300 Subject: [PATCH] ECAL opcode support (#6947) ## Description The ECAL opcode is supported in `fuel-vm` to extend the capabilities of the VM. Users can implement rust code to interop with the VM using the ECAL opcode. This PR introduces support for the opcode in the sway repo - such that sway assembly can be successfully compiled with the ECAL opcode. Examples of how to utilize the `ECAL` opcode can be seen here: https://github.com/FuelLabs/fuel-vm/blob/9478991db1ea8799fab47ebe1c195946a2698a7f/fuel-vm/examples/external.rs#L51 - Closes https://github.com/FuelLabs/sway/issues/5239 - Added an e2e test to validate compilation of ecall opcode in sway assembly. ## Checklist - [x] I have linked to any relevant issues. - [ ] 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) - [ ] 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. - [ ] 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). - [ ] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: z --- sway-ast/src/expr/op_code.rs | 1 + sway-core/src/asm_lang/allocated_ops.rs | 11 +++++++++ sway-core/src/asm_lang/mod.rs | 5 ++++ sway-core/src/asm_lang/virtual_ops.rs | 24 +++++++++++++++++++ sway-parse/src/expr/op_code.rs | 1 + .../should_pass/ecall/ecall_basic/Forc.lock | 8 +++++++ .../should_pass/ecall/ecall_basic/Forc.toml | 9 +++++++ .../should_pass/ecall/ecall_basic/src/main.sw | 7 ++++++ .../should_pass/ecall/ecall_basic/test.toml | 1 + 9 files changed, 67 insertions(+) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/test.toml diff --git a/sway-ast/src/expr/op_code.rs b/sway-ast/src/expr/op_code.rs index bfd13f1ed81..b8a59cb959b 100644 --- a/sway-ast/src/expr/op_code.rs +++ b/sway-ast/src/expr/op_code.rs @@ -340,6 +340,7 @@ define_op_codes!( (ECOP, ECOPOpcode, "ecop", (dst_addr: reg, curve: reg, operation: reg, src_addr: reg)), (EPAR, EPAROpcode, "epar", (ret: reg, curve: reg, groups_of_points: reg, addr: reg)), /* Other Instructions */ + (Ecal, EcalOpcode, "ecal", (reg_a: reg, reg_b: reg, reg_c: reg, reg_d: reg)), (Flag, FlagOpcode, "flag", (value: reg)), (Gm, GmOpcode, "gm", (ret: reg, op: imm)), ( diff --git a/sway-core/src/asm_lang/allocated_ops.rs b/sway-core/src/asm_lang/allocated_ops.rs index bbf00a4067b..bde88766d33 100644 --- a/sway-core/src/asm_lang/allocated_ops.rs +++ b/sway-core/src/asm_lang/allocated_ops.rs @@ -281,6 +281,12 @@ pub(crate) enum AllocatedOpcode { ), /* Other Instructions */ + ECAL( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + ), FLAG(AllocatedRegister), GM(AllocatedRegister, VirtualImmediate18), GTF(AllocatedRegister, AllocatedRegister, VirtualImmediate12), @@ -408,6 +414,7 @@ impl AllocatedOpcode { EPAR(r1, _r2, _r3, _r4) => vec![r1], /* Other Instructions */ + ECAL(_r1, _r2, _r3, _r4) => vec![], FLAG(_r1) => vec![], GM(r1, _imm) => vec![r1], GTF(r1, _r2, _i) => vec![r1], @@ -539,6 +546,7 @@ impl fmt::Display for AllocatedOpcode { EPAR(a, b, c, d) => write!(fmtr, "epar {a} {b} {c} {d}"), /* Other Instructions */ + ECAL(a, b, c, d) => write!(fmtr, "ecal {a} {b} {c} {d}"), FLAG(a) => write!(fmtr, "flag {a}"), GM(a, b) => write!(fmtr, "gm {a} {b}"), GTF(a, b, c) => write!(fmtr, "gtf {a} {b} {c}"), @@ -775,6 +783,9 @@ impl AllocatedOp { } /* Other Instructions */ + ECAL(a, b, c, d) => { + op::ECAL::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.to_reg_id()).into() + } FLAG(a) => op::FLAG::new(a.to_reg_id()).into(), GM(a, b) => op::GM::new(a.to_reg_id(), b.value().into()).into(), GTF(a, b, c) => op::GTF::new(a.to_reg_id(), b.to_reg_id(), c.value().into()).into(), diff --git a/sway-core/src/asm_lang/mod.rs b/sway-core/src/asm_lang/mod.rs index 029c59f048e..aabea4edfc8 100644 --- a/sway-core/src/asm_lang/mod.rs +++ b/sway-core/src/asm_lang/mod.rs @@ -684,6 +684,10 @@ impl Op { } /* Other Instructions */ + "ecal" => { + let (r1, r2, r3, r4) = four_regs(handler, args, immediate, whole_op_span)?; + VirtualOp::ECAL(r1, r2, r3, r4) + } "flag" => { let r1 = single_reg(handler, args, immediate, whole_op_span)?; VirtualOp::FLAG(r1) @@ -1240,6 +1244,7 @@ impl fmt::Display for VirtualOp { EPAR(a, b, c, d) => write!(fmtr, "epar {a} {b} {c} {d}"), /* Other Instructions */ + ECAL(a, b, c, d) => write!(fmtr, "ecal {a} {b} {c} {d}"), FLAG(a) => write!(fmtr, "flag {a}"), GM(a, b) => write!(fmtr, "gm {a} {b}"), GTF(a, b, c) => write!(fmtr, "gtf {a} {b} {c}"), diff --git a/sway-core/src/asm_lang/virtual_ops.rs b/sway-core/src/asm_lang/virtual_ops.rs index 1845303ebd6..137db3ac485 100644 --- a/sway-core/src/asm_lang/virtual_ops.rs +++ b/sway-core/src/asm_lang/virtual_ops.rs @@ -232,6 +232,12 @@ pub(crate) enum VirtualOp { ), /* Other Instructions */ + ECAL( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualRegister, + ), FLAG(VirtualRegister), GM(VirtualRegister, VirtualImmediate18), GTF(VirtualRegister, VirtualRegister, VirtualImmediate12), @@ -355,6 +361,7 @@ impl VirtualOp { EPAR(r1, r2, r3, r4) => vec![r1, r2, r3, r4], /* Other Instructions */ + ECAL(r1, r2, r3, r4) => vec![r1, r2, r3, r4], FLAG(r1) => vec![r1], GM(r1, _imm) => vec![r1], GTF(r1, r2, _i) => vec![r1, r2], @@ -479,6 +486,8 @@ impl VirtualOp { | K256(_, _, _) | S256(_, _, _) | ECOP(_, _, _, _) + // Other instructions + | ECAL(_, _, _, _) | FLAG(_) // Virtual OPs | BLOB(_) @@ -588,6 +597,7 @@ impl VirtualOp { | S256(_, _, _) | ECOP(_, _, _, _) | EPAR(_, _, _, _) + | ECAL(_, _, _, _) | GM(_, _) | GTF(_, _, _) | BLOB(_) @@ -709,6 +719,7 @@ impl VirtualOp { EPAR(_r1, r2, r3, r4) => vec![r2, r3, r4], /* Other Instructions */ + ECAL(r1, r2, r3, r4) => vec![r1, r2, r3, r4], FLAG(r1) => vec![r1], GM(_r1, _imm) => vec![], GTF(_r1, r2, _i) => vec![r2], @@ -833,6 +844,7 @@ impl VirtualOp { EPAR(r1, _r2, _r3, _r4) => vec![r1], /* Other Instructions */ + ECAL(_r1, _r2, _r3, _r4) => vec![], FLAG(_r1) => vec![], GM(r1, _imm) => vec![r1], GTF(r1, _r2, _i) => vec![r1], @@ -1292,6 +1304,12 @@ impl VirtualOp { ), /* Other Instructions */ + ECAL(r1, r2, r3, r4) => Self::ECAL( + 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), + ), FLAG(r1) => Self::FLAG(update_reg(reg_to_reg_map, r1)), GM(r1, i) => Self::GM(update_reg(reg_to_reg_map, r1), i.clone()), GTF(r1, r2, i) => Self::GTF( @@ -1785,6 +1803,12 @@ impl VirtualOp { ), /* Other Instructions */ + ECAL(reg1, reg2, reg3, reg4) => AllocatedOpcode::ECAL( + map_reg(&mapping, reg1), + map_reg(&mapping, reg2), + map_reg(&mapping, reg3), + map_reg(&mapping, reg4), + ), FLAG(reg) => AllocatedOpcode::FLAG(map_reg(&mapping, reg)), GM(reg, imm) => AllocatedOpcode::GM(map_reg(&mapping, reg), imm.clone()), GTF(reg1, reg2, imm) => AllocatedOpcode::GTF( diff --git a/sway-parse/src/expr/op_code.rs b/sway-parse/src/expr/op_code.rs index 29ad8918111..0be4a5b0cfc 100644 --- a/sway-parse/src/expr/op_code.rs +++ b/sway-parse/src/expr/op_code.rs @@ -137,6 +137,7 @@ define_op_codes!( (ret, curve, groups_of_points, addr) ), /* Other Instructions */ + (Ecal, EcalOpcode, "ecal", (reg_a, reg_b, reg_c, reg_d)), (Flag, FlagOpcode, "flag", (value)), (Gm, GmOpcode, "gm", (ret, op)), (Gtf, GtfOpcode, "gtf", (ret, index, tx_field_id)), diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.lock new file mode 100644 index 00000000000..cd43bbf78d8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-02C557D3012ABF18" + +[[package]] +name = "ecall_basic" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.toml new file mode 100644 index 00000000000..13c1b6e9982 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/Forc.toml @@ -0,0 +1,9 @@ +[project] +name = "ecall_basic" +authors = ["Fuel Labs "] +entry = "main.sw" +implicit-std = false +license = "Apache-2.0" + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/src/main.sw new file mode 100644 index 00000000000..8e5d877eaf5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/src/main.sw @@ -0,0 +1,7 @@ +script; + +fn main() { + asm(r1: 1u64, r2: 2u32, r3: 3u32, r4: 4u32) { + ecal r1 r2 r3 r4; + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/test.toml new file mode 100644 index 00000000000..0f3f6d7e866 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/ecall/ecall_basic/test.toml @@ -0,0 +1 @@ +category = "unit_tests_pass"