From 9cdf7f183b42402c3ce470a48a2d8b111cc4a491 Mon Sep 17 00:00:00 2001 From: Paul Schoenfelder Date: Mon, 16 Oct 2023 22:38:12 -0400 Subject: [PATCH] feat: implement conversion of masm ir to 'real' masm --- Cargo.lock | 208 ++++++++++++++++- Cargo.toml | 5 +- codegen/masm/Cargo.toml | 1 + codegen/masm/src/masm/function.rs | 89 ++++++++ codegen/masm/src/masm/module.rs | 107 ++++++++- codegen/masm/src/masm/program.rs | 77 ++++++- hir/Cargo.toml | 1 + hir/src/asm/isa.rs | 363 +++++++++++++++++++++++++++++- 8 files changed, 832 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c3279e54..561129d48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,6 +100,18 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "atty" version = "0.2.14" @@ -144,12 +156,35 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -239,6 +274,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + [[package]] name = "cranelift-bforest" version = "0.100.0" @@ -254,12 +304,32 @@ version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f333fa641a9ad2bff0b107767dcb972c18c2bfab7969805a1d7e42449ccb0408" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -370,6 +440,16 @@ dependencies = [ "seize", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -387,6 +467,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.14.0" @@ -464,6 +550,24 @@ dependencies = [ "either", ] +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -529,6 +633,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miden-assembly" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c483ade3de7375f869a2c99de2396d0545cb6df96e0e96d693a40e298b4cfea" +dependencies = [ + "miden-core", + "num_enum", +] + [[package]] name = "miden-codegen-masm" version = "0.1.0" @@ -536,6 +650,7 @@ dependencies = [ "anyhow", "cranelift-entity", "intrusive-collections", + "miden-assembly", "miden-diagnostics", "miden-hir", "miden-hir-analysis", @@ -547,10 +662,38 @@ dependencies = [ "thiserror", ] +[[package]] +name = "miden-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3baf9ff16e3f7a6c045c8ece8d2d7ebf4521eab92de07cc9edb11f3e9c853143" +dependencies = [ + "miden-crypto", + "winter-crypto", + "winter-math", + "winter-utils", +] + +[[package]] +name = "miden-crypto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32dd571edafdd5e8947e4006a905a1c5373f2f8b08b270fea3c998db5be131cf" +dependencies = [ + "blake3", + "cc", + "glob", + "libc", + "winter-crypto", + "winter-math", + "winter-utils", +] + [[package]] name = "miden-diagnostics" version = "0.1.0" -source = "git+https://github.com/0xpolygonmiden/miden-diagnostics#f99efe05b8873723fcbb1ba22c31d15c9e4b5afe" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3a82597c2a9babcff4c9283a95130a96aaf8e339954a083bb6582fc2520cf1" dependencies = [ "atty", "codespan", @@ -565,7 +708,8 @@ dependencies = [ [[package]] name = "miden-diagnostics-macros" version = "0.1.0" -source = "git+https://github.com/0xpolygonmiden/miden-diagnostics#f99efe05b8873723fcbb1ba22c31d15c9e4b5afe" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "491d10b0eb201ba767ccdf69bf77f9d5662caf55e9ef468264cccb7129edff62" dependencies = [ "proc-macro2", "quote", @@ -579,6 +723,7 @@ dependencies = [ "anyhow", "cranelift-entity", "intrusive-collections", + "miden-assembly", "miden-diagnostics", "miden-hir-symbol", "miden-hir-type", @@ -672,6 +817,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.31", +] + [[package]] name = "object" version = "0.32.1" @@ -747,6 +913,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -908,6 +1084,16 @@ dependencies = [ "serde", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "smallvec" version = "1.11.0" @@ -1050,6 +1236,12 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.11" @@ -1211,6 +1403,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "winter-crypto" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a20b2a4499797cbaeb38c980f9f34e6e60d993e8e170a6deb354345f50cbfb" +dependencies = [ + "blake3", + "sha3", + "winter-math", + "winter-utils", +] + [[package]] name = "winter-math" version = "0.6.5" diff --git a/Cargo.toml b/Cargo.toml index a6f9b7b65..986867a27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,8 +43,9 @@ rustc-hash = "1.1" smallvec = { version = "1.9", features = ["union", "const_generics", "const_new"] } smallstr = { version = "0.3", features = ["union"] } thiserror = "1.0" -miden-diagnostics = { git = "https://github.com/0xpolygonmiden/miden-diagnostics" } -miden-parsing = { git = "https://github.com/0xpolygonmiden/miden-parsing" } +miden-assembly = "0.7" +miden-diagnostics = "0.1" +miden-parsing = "0.1" [profile.release] opt-level = 2 diff --git a/codegen/masm/Cargo.toml b/codegen/masm/Cargo.toml index 599f5548d..521940ab6 100644 --- a/codegen/masm/Cargo.toml +++ b/codegen/masm/Cargo.toml @@ -15,6 +15,7 @@ anyhow.workspace = true thiserror.workspace = true cranelift-entity.workspace = true intrusive-collections.workspace = true +miden-assembly.workspace = true miden-diagnostics.workspace = true miden-hir = { path = "../../hir" } miden-hir-analysis = { path = "../../hir-analysis" } diff --git a/codegen/masm/src/masm/function.rs b/codegen/masm/src/masm/function.rs index 4fb1abe7f..bacf90d3f 100644 --- a/codegen/masm/src/masm/function.rs +++ b/codegen/masm/src/masm/function.rs @@ -2,7 +2,9 @@ use std::fmt; use cranelift_entity::{EntityRef, PrimaryMap}; use intrusive_collections::{intrusive_adapter, LinkedListLink}; +use miden_diagnostics::Spanned; use miden_hir::{FunctionIdent, Signature, Type}; +use rustc_hash::FxHashMap; use smallvec::{smallvec, SmallVec}; use super::*; @@ -10,9 +12,11 @@ use super::*; intrusive_adapter!(pub FunctionListAdapter = Box: Function { link: LinkedListLink }); /// This represents a function in Miden Assembly +#[derive(Spanned)] pub struct Function { link: LinkedListLink, /// The name of this function + #[span] pub name: FunctionIdent, /// The type signature of this function pub signature: Signature, @@ -115,7 +119,92 @@ impl Function { imports, } } + + pub fn to_function_ast( + &self, + codemap: &miden_diagnostics::CodeMap, + imports: &miden_hir::ModuleImportInfo, + local_ids: &FxHashMap, + proc_ids: &FxHashMap, + ) -> miden_assembly::ast::ProcedureAst { + use miden_assembly::{ + self as masm, + ast::{ProcedureAst, SourceLocation}, + }; + + let name = masm::ProcedureName::try_from(self.name.function.as_str()) + .expect("invalid function name"); + let num_locals = u16::try_from(self.locals.len()).expect("too many locals"); + let start = codemap + .location(self) + .ok() + .map(|loc| { + SourceLocation::new(loc.line.to_usize() as u32, loc.column.to_usize() as u32) + }) + .unwrap_or_default(); + let body = emit_block( + self.body, + &self.blocks, + codemap, + imports, + local_ids, + proc_ids, + ); + + ProcedureAst { + name, + docs: None, + num_locals, + body, + start, + is_export: self.signature.is_public(), + } + } } + +fn emit_block( + block_id: BlockId, + blocks: &PrimaryMap, + codemap: &miden_diagnostics::CodeMap, + imports: &miden_hir::ModuleImportInfo, + local_ids: &FxHashMap, + proc_ids: &FxHashMap, +) -> miden_assembly::ast::CodeBody { + use miden_assembly::ast::{CodeBody, Node}; + + let current_block = &blocks[block_id]; + let mut ops = Vec::with_capacity(current_block.ops.len()); + for op in current_block.ops.iter() { + match op.clone() { + Op::If(then_blk, else_blk) => { + let true_case = emit_block(then_blk, blocks, codemap, imports, local_ids, proc_ids); + let false_case = + emit_block(else_blk, blocks, codemap, imports, local_ids, proc_ids); + ops.push(Node::IfElse { + true_case, + false_case, + }); + } + Op::While(blk) => { + let body = emit_block(blk, blocks, codemap, imports, local_ids, proc_ids); + ops.push(Node::While { body }); + } + Op::Repeat(n, blk) => { + let body = emit_block(blk, blocks, codemap, imports, local_ids, proc_ids); + ops.push(Node::Repeat { + times: n as u32, + body, + }); + } + op => { + ops.extend(op.into_node(codemap, imports, local_ids, proc_ids)); + } + } + } + + CodeBody::new(ops) +} + impl fmt::Debug for Function { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Function") diff --git a/codegen/masm/src/masm/module.rs b/codegen/masm/src/masm/module.rs index 709f3afde..1cfaa28e9 100644 --- a/codegen/masm/src/masm/module.rs +++ b/codegen/masm/src/masm/module.rs @@ -1,13 +1,20 @@ -use core::fmt; +use std::{collections::BTreeMap, fmt, path::Path}; use intrusive_collections::LinkedList; use miden_hir::{FunctionIdent, Ident}; +use rustc_hash::FxHashMap; use super::{Function, FunctionListAdapter, ModuleImportInfo, Op}; +/// This represents a single compiled Miden Assembly module in a form that is +/// designed to integrate well with the rest of our IR. You can think of this +/// as an intermediate representation corresponding to the Miden Assembly AST, +/// i.e. [miden_assembly::ast::ModuleAst]. pub struct Module { /// The name of this module, e.g. `std::math::u64` pub name: Ident, + /// The module-scoped documentation for this module + pub docs: Option, /// If this module contains a program entrypoint, this is the /// function identifier which should be used for that purpose. pub entry: Option, @@ -16,20 +23,104 @@ pub struct Module { /// The functions defined in this module pub functions: LinkedList, } - impl Module { + /// Create a new, empty [Module] with the given name. pub fn new(name: Ident) -> Self { Self { name, + docs: None, entry: None, imports: Default::default(), functions: Default::default(), } } + /// Convert this module into its [miden_assembly::Module] representation. + pub fn to_module_ast(&self, codemap: &miden_diagnostics::CodeMap) -> miden_assembly::Module { + use miden_assembly::{ + self as masm, + ast::{ModuleAst, ModuleImports}, + }; + + // Create module import table + let mut imported = BTreeMap::::default(); + let mut invoked = BTreeMap::::default(); + let mut proc_ids = FxHashMap::::default(); + for import in self.imports.iter() { + let path = masm::LibraryPath::new(import.name.as_str()).expect("invalid module name"); + imported.insert(import.alias.to_string(), path.clone()); + if let Some(imported_fns) = self.imports.imported(&import.alias) { + for import_fn in imported_fns.iter().copied() { + let fname = import_fn.to_string(); + let name = masm::ProcedureName::try_from(fname.as_str()) + .expect("invalid function name"); + let id = masm::ProcedureId::from_name(fname.as_str(), &path); + invoked.insert(id, (name, path.clone())); + proc_ids.insert(import_fn, id); + } + } + } + let imports = ModuleImports::new(imported, invoked); + + // Translate functions + let mut local_ids = FxHashMap::default(); + for (id, function) in self.functions.iter().enumerate() { + local_ids.insert(function.name, id as u16); + } + let mut procs = Vec::with_capacity(self.num_imported_functions()); + for function in self.functions.iter() { + procs.push(function.to_function_ast(codemap, &self.imports, &local_ids, &proc_ids)); + } + + // Construct module + let path = masm::LibraryPath::new(self.name.as_str()).expect("invalid module name"); + let ast = ModuleAst::new(procs, vec![], self.docs.clone()) + .expect("invalid module body") + .with_import_info(imports); + masm::Module { path, ast } + } + + fn num_imported_functions(&self) -> usize { + self.imports + .iter() + .map(|i| { + self.imports + .imported(&i.alias) + .map(|imported| imported.len()) + .unwrap_or(0) + }) + .sum() + } + + /// Write this module to a new file under `dir`, assuming `dir` is the root directory for a program. + /// + /// For example, if this module is named `std::math::u64`, then it will be written to `/std/math/u64.masm` + pub fn write_to_directory>( + &self, + codemap: &miden_diagnostics::CodeMap, + dir: P, + ) -> std::io::Result<()> { + use std::fs::File; + + let mut path = dir.as_ref().to_path_buf(); + assert!(path.is_dir()); + for component in self.name.as_str().split("::") { + path.push(component); + } + assert!(path.set_extension("masm")); + + let mut out = File::create(&path)?; + self.emit(codemap, &mut out) + } + /// Write this module as Miden Assembly text to `out` - pub fn emit(&self, _out: &mut dyn std::io::Write) -> std::io::Result<()> { - todo!() + pub fn emit( + &self, + codemap: &miden_diagnostics::CodeMap, + out: &mut dyn std::io::Write, + ) -> std::io::Result<()> { + let ast = self.to_module_ast(codemap); + out.write_fmt(format_args!("{}", &ast.ast)) } } impl fmt::Display for Module { @@ -46,8 +137,12 @@ impl fmt::Display for Module { writeln!(f)?; } - for function in self.functions.iter() { - writeln!(f, "{}\n", function.display(&self.imports))?; + for (i, function) in self.functions.iter().enumerate() { + if i > 0 { + writeln!(f, "\n{}", function.display(&self.imports))?; + } else { + writeln!(f, "{}", function.display(&self.imports))?; + } } if let Some(entry) = self.entry { diff --git a/codegen/masm/src/masm/program.rs b/codegen/masm/src/masm/program.rs index 92e5b5956..66cb5c6b1 100644 --- a/codegen/masm/src/masm/program.rs +++ b/codegen/masm/src/masm/program.rs @@ -1,16 +1,23 @@ -use std::path::Path; +use std::{collections::BTreeMap, path::Path}; -use miden_hir::{self as hir, DataSegmentTable, FunctionIdent}; +use miden_hir::{self as hir, DataSegmentTable, FunctionIdent, Ident}; use super::*; +/// A [Program] represents a complete set of modules which are intended to +/// be shipped together as an artifact, either as an executable, or as a library +/// to be integrated into a larger executable. #[derive(Default)] pub struct Program { + /// The set of modules which belong to this program pub modules: Vec, + /// The function identifier for the program entrypoint, if this is an executable module pub entrypoint: Option, + /// The data segment table for this program pub segments: DataSegmentTable, } impl Program { + /// Create a new, empty [Program] pub fn new() -> Self { Self::default() } @@ -22,6 +29,66 @@ impl Program { pub fn is_library(&self) -> bool { self.entrypoint.is_none() } + + /// Write this [Program] to the given output directory. + /// + /// The provided [miden_diagnostics::CodeMap] is used for computing source locations. + pub fn write_to_directory>( + &self, + codemap: &miden_diagnostics::CodeMap, + path: P, + ) -> std::io::Result<()> { + use miden_assembly as masm; + + let path = path.as_ref(); + assert!(path.is_dir()); + + let program = self.to_program_ast(); + program.write_to_file(path.join(masm::LibraryPath::EXEC_PATH))?; + + for module in self.modules.iter() { + module.write_to_directory(codemap, path)?; + } + + Ok(()) + } + + /// Convert this program to its [miden_assembly::ast::ProgramAst] representation + pub fn to_program_ast(&self) -> miden_assembly::ast::ProgramAst { + use miden_assembly::{ + self as masm, + ast::{Instruction, ModuleImports, Node, ProgramAst}, + }; + + if let Some(entry) = self.entrypoint { + let entry_import = Import::try_from(entry.module).expect("invalid module name"); + let entry_module_path = + masm::LibraryPath::new(entry_import.name.as_str()).expect("invalid module path"); + let entry_id = + masm::ProcedureId::from_name(entry.function.as_str(), &entry_module_path); + let entry_name = masm::ProcedureName::try_from( + FunctionIdent { + module: Ident::with_empty_span(entry_import.alias), + ..entry + } + .to_string(), + ) + .expect("invalid entrypoint function name"); + let imported = + BTreeMap::from([(entry_import.alias.to_string(), entry_module_path.clone())]); + let invoked = BTreeMap::from([(entry_id, (entry_name, entry_module_path))]); + let imports = ModuleImports::new(imported, invoked); + + // TODO: Write data segments, initialize function table + let body = vec![Node::Instruction(Instruction::ExecImported(entry_id))]; + + ProgramAst::new(body, vec![]) + .expect("invalid program") + .with_import_info(imports) + } else { + todo!("0xPolygonMiden/miden-vm#1108") + } + } } impl From<&hir::Program> for Program { fn from(program: &hir::Program) -> Self { @@ -35,8 +102,4 @@ impl From<&hir::Program> for Program { } } -impl Program { - pub fn write_to_directory>(&self, _path: P) -> std::io::Result<()> { - todo!() - } -} +impl Program {} diff --git a/hir/Cargo.toml b/hir/Cargo.toml index fcd26ad05..f35e22a33 100644 --- a/hir/Cargo.toml +++ b/hir/Cargo.toml @@ -14,6 +14,7 @@ edition.workspace = true anyhow.workspace = true cranelift-entity.workspace = true intrusive-collections.workspace = true +miden-assembly.workspace = true miden-diagnostics.workspace = true miden-hir-symbol = { path = "../hir-symbol" } miden-hir-type = { path = "../hir-type" } diff --git a/hir/src/asm/isa.rs b/hir/src/asm/isa.rs index fea8e6f2a..e578328d8 100644 --- a/hir/src/asm/isa.rs +++ b/hir/src/asm/isa.rs @@ -1,9 +1,10 @@ use std::fmt; use cranelift_entity::entity_impl; -use smallvec::SmallVec; +use rustc_hash::FxHashMap; +use smallvec::{smallvec, SmallVec}; -use crate::{Felt, FunctionIdent, LocalId}; +use crate::{Felt, FunctionIdent, Ident, LocalId}; /// A handle that refers to a MASM code block #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -616,6 +617,364 @@ pub enum MasmOp { /// The behavior is undefined if either `a` or `b` are >= 2^32 U32UncheckedMax, } +impl MasmOp { + pub fn into_node( + self, + _codemap: &miden_diagnostics::CodeMap, + imports: &super::ModuleImportInfo, + local_ids: &FxHashMap, + proc_ids: &FxHashMap, + ) -> SmallVec<[miden_assembly::ast::Node; 2]> { + use miden_assembly::ast::{Instruction, Node}; + let node = match self { + Self::Padw => Instruction::PadW, + Self::Push(v) => Instruction::PushFelt(v), + Self::Push2([a, b]) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(a)), + Node::Instruction(Instruction::PushFelt(b)) + ] + } + Self::Pushw(word) => Instruction::PushWord(word), + Self::PushU8(v) => Instruction::PushFelt(Felt::new(v as u64)), + Self::PushU16(v) => Instruction::PushFelt(Felt::new(v as u64)), + Self::PushU32(v) => Instruction::PushFelt(Felt::new(v as u64)), + Self::Drop => Instruction::Drop, + Self::Dropw => Instruction::DropW, + Self::Dup(0) => Instruction::Dup0, + Self::Dup(1) => Instruction::Dup1, + Self::Dup(2) => Instruction::Dup2, + Self::Dup(3) => Instruction::Dup3, + Self::Dup(4) => Instruction::Dup4, + Self::Dup(5) => Instruction::Dup5, + Self::Dup(6) => Instruction::Dup6, + Self::Dup(7) => Instruction::Dup7, + Self::Dup(8) => Instruction::Dup8, + Self::Dup(9) => Instruction::Dup9, + Self::Dup(10) => Instruction::Dup10, + Self::Dup(11) => Instruction::Dup11, + Self::Dup(12) => Instruction::Dup12, + Self::Dup(13) => Instruction::Dup13, + Self::Dup(14) => Instruction::Dup14, + Self::Dup(15) => Instruction::Dup15, + Self::Dup(n) => { + panic!("invalid dup instruction, valid index range is 0..=15, got {n}") + } + Self::Dupw(0) => Instruction::DupW0, + Self::Dupw(1) => Instruction::DupW1, + Self::Dupw(2) => Instruction::DupW2, + Self::Dupw(3) => Instruction::DupW3, + Self::Dupw(n) => { + panic!("invalid dupw instruction, valid index range is 0..=3, got {n}") + } + Self::Swap(1) => Instruction::Swap1, + Self::Swap(2) => Instruction::Swap2, + Self::Swap(3) => Instruction::Swap3, + Self::Swap(4) => Instruction::Swap4, + Self::Swap(5) => Instruction::Swap5, + Self::Swap(6) => Instruction::Swap6, + Self::Swap(7) => Instruction::Swap7, + Self::Swap(8) => Instruction::Swap8, + Self::Swap(9) => Instruction::Swap9, + Self::Swap(10) => Instruction::Swap10, + Self::Swap(11) => Instruction::Swap11, + Self::Swap(12) => Instruction::Swap12, + Self::Swap(13) => Instruction::Swap13, + Self::Swap(14) => Instruction::Swap14, + Self::Swap(15) => Instruction::Swap15, + Self::Swap(n) => { + panic!("invalid swap instruction, valid index range is 1..=15, got {n}") + } + Self::Swapw(1) => Instruction::SwapW1, + Self::Swapw(2) => Instruction::SwapW2, + Self::Swapw(3) => Instruction::SwapW3, + Self::Swapw(n) => { + panic!("invalid swapw instruction, valid index range is 1..=3, got {n}") + } + Self::Movup(2) => Instruction::MovUp2, + Self::Movup(3) => Instruction::MovUp3, + Self::Movup(4) => Instruction::MovUp4, + Self::Movup(5) => Instruction::MovUp5, + Self::Movup(6) => Instruction::MovUp6, + Self::Movup(7) => Instruction::MovUp7, + Self::Movup(8) => Instruction::MovUp8, + Self::Movup(9) => Instruction::MovUp9, + Self::Movup(10) => Instruction::MovUp10, + Self::Movup(11) => Instruction::MovUp11, + Self::Movup(12) => Instruction::MovUp12, + Self::Movup(13) => Instruction::MovUp13, + Self::Movup(14) => Instruction::MovUp14, + Self::Movup(15) => Instruction::MovUp15, + Self::Movup(n) => { + panic!("invalid movup instruction, valid index range is 2..=15, got {n}") + } + Self::Movupw(2) => Instruction::MovUpW2, + Self::Movupw(3) => Instruction::MovUpW3, + Self::Movupw(n) => { + panic!("invalid movupw instruction, valid index range is 2..=3, got {n}") + } + Self::Movdn(2) => Instruction::MovDn2, + Self::Movdn(3) => Instruction::MovDn3, + Self::Movdn(4) => Instruction::MovDn4, + Self::Movdn(5) => Instruction::MovDn5, + Self::Movdn(6) => Instruction::MovDn6, + Self::Movdn(7) => Instruction::MovDn7, + Self::Movdn(8) => Instruction::MovDn8, + Self::Movdn(9) => Instruction::MovDn9, + Self::Movdn(10) => Instruction::MovDn10, + Self::Movdn(11) => Instruction::MovDn11, + Self::Movdn(12) => Instruction::MovDn12, + Self::Movdn(13) => Instruction::MovDn13, + Self::Movdn(14) => Instruction::MovDn14, + Self::Movdn(15) => Instruction::MovDn15, + Self::Movdn(n) => { + panic!("invalid movdn instruction, valid index range is 2..=15, got {n}") + } + Self::Movdnw(2) => Instruction::MovDnW2, + Self::Movdnw(3) => Instruction::MovDnW3, + Self::Movdnw(n) => { + panic!("invalid movdnw instruction, valid index range is 2..=3, got {n}") + } + Self::Cswap => Instruction::CSwap, + Self::Cswapw => Instruction::CSwapW, + Self::Cdrop => Instruction::CDrop, + Self::Cdropw => Instruction::CDropW, + Self::Assert => Instruction::Assert, + Self::Assertz => Instruction::Assertz, + Self::AssertEq => Instruction::AssertEq, + Self::AssertEqw => Instruction::AssertEqw, + Self::LocAddr(id) => Instruction::Locaddr(id.as_usize() as u16), + Self::MemLoad => Instruction::MemLoad, + Self::MemLoadImm(addr) => Instruction::MemLoadImm(addr), + Self::MemLoadw => Instruction::MemLoadW, + Self::MemLoadwImm(addr) => Instruction::MemLoadWImm(addr), + Self::MemStore => Instruction::MemStore, + Self::MemStoreImm(addr) => Instruction::MemStoreImm(addr), + Self::MemStorew => Instruction::MemStoreW, + Self::MemStorewImm(addr) => Instruction::MemStoreWImm(addr), + Self::MemLoadOffset + | Self::MemLoadOffsetImm(_, _) + | Self::MemStoreOffset + | Self::MemStoreOffsetImm(_, _) => unimplemented!( + "this is an experimental instruction that is not supported by the Miden VM" + ), + Self::If(_, _) | Self::While(_) | Self::Repeat(_, _) => { + panic!("control flow instructions are meant to be handled specially by the caller") + } + Self::Exec(ref callee) => { + if let Some(idx) = local_ids.get(callee).copied() { + Instruction::ExecLocal(idx) + } else { + let aliased = if let Some(alias) = imports.alias(&callee.module) { + FunctionIdent { + module: alias, + function: callee.function, + } + } else { + let module_as_import = super::MasmImport::try_from(callee.module) + .expect("invalid module name"); + FunctionIdent { + module: Ident::with_empty_span(module_as_import.alias), + function: callee.function, + } + }; + let id = proc_ids + .get(&aliased) + .copied() + .unwrap_or_else(|| miden_assembly::ProcedureId::new(&aliased.to_string())); + Instruction::ExecImported(id) + } + } + Self::Syscall(ref callee) => { + let aliased = if let Some(alias) = imports.alias(&callee.module) { + FunctionIdent { + module: alias, + function: callee.function, + } + } else { + let module_as_import = + super::MasmImport::try_from(callee.module).expect("invalid module name"); + FunctionIdent { + module: Ident::with_empty_span(module_as_import.alias), + function: callee.function, + } + }; + let id = proc_ids + .get(&aliased) + .copied() + .unwrap_or_else(|| miden_assembly::ProcedureId::new(&aliased.to_string())); + Instruction::SysCall(id) + } + Self::Add => Instruction::Add, + Self::AddImm(imm) => Instruction::AddImm(imm), + Self::Sub => Instruction::Sub, + Self::SubImm(imm) => Instruction::SubImm(imm), + Self::Mul => Instruction::Mul, + Self::MulImm(imm) => Instruction::MulImm(imm), + Self::Div => Instruction::Div, + Self::DivImm(imm) => Instruction::DivImm(imm), + Self::Neg => Instruction::Neg, + Self::Inv => Instruction::Inv, + Self::Incr => Instruction::Incr, + Self::Pow2 => Instruction::Pow2, + Self::Exp => Instruction::Exp, + Self::ExpImm(imm) => Instruction::ExpBitLength(imm), + Self::Not => Instruction::Not, + Self::And => Instruction::And, + Self::AndImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(Felt::new(imm as u64))), + Node::Instruction(Instruction::And) + ] + } + Self::Or => Instruction::Or, + Self::OrImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(Felt::new(imm as u64))), + Node::Instruction(Instruction::Or) + ] + } + Self::Xor => Instruction::Xor, + Self::XorImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(Felt::new(imm as u64))), + Node::Instruction(Instruction::Xor) + ] + } + Self::Eq => Instruction::Eq, + Self::EqImm(imm) => Instruction::EqImm(imm), + Self::Neq => Instruction::Neq, + Self::NeqImm(imm) => Instruction::NeqImm(imm), + Self::Gt => Instruction::Gt, + Self::GtImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(imm)), + Node::Instruction(Instruction::Gt) + ] + } + Self::Gte => Instruction::Gte, + Self::GteImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(imm)), + Node::Instruction(Instruction::Gte) + ] + } + Self::Lt => Instruction::Lt, + Self::LtImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(imm)), + Node::Instruction(Instruction::Lt) + ] + } + Self::Lte => Instruction::Lte, + Self::LteImm(imm) => { + return smallvec![ + Node::Instruction(Instruction::PushFelt(imm)), + Node::Instruction(Instruction::Lte) + ] + } + Self::IsOdd => Instruction::IsOdd, + Self::Eqw => Instruction::Eqw, + Self::Clk => Instruction::Clk, + Self::U32Test => Instruction::U32Test, + Self::U32Testw => Instruction::U32TestW, + Self::U32Assert => Instruction::U32Assert, + Self::U32Assert2 => Instruction::U32Assert2, + Self::U32Assertw => Instruction::U32AssertW, + Self::U32Cast => Instruction::U32Cast, + Self::U32Split => Instruction::U32Split, + Self::U32CheckedAdd => Instruction::U32CheckedAdd, + Self::U32CheckedAddImm(imm) => Instruction::U32CheckedAddImm(imm), + Self::U32OverflowingAdd => Instruction::U32OverflowingAdd, + Self::U32OverflowingAddImm(imm) => Instruction::U32OverflowingAddImm(imm), + Self::U32WrappingAdd => Instruction::U32WrappingAdd, + Self::U32WrappingAddImm(imm) => Instruction::U32WrappingAddImm(imm), + Self::U32OverflowingAdd3 => Instruction::U32OverflowingAdd3, + Self::U32WrappingAdd3 => Instruction::U32WrappingAdd3, + Self::U32CheckedSub => Instruction::U32CheckedSub, + Self::U32CheckedSubImm(imm) => Instruction::U32CheckedSubImm(imm), + Self::U32OverflowingSub => Instruction::U32OverflowingSub, + Self::U32OverflowingSubImm(imm) => Instruction::U32OverflowingSubImm(imm), + Self::U32WrappingSub => Instruction::U32WrappingSub, + Self::U32WrappingSubImm(imm) => Instruction::U32WrappingSubImm(imm), + Self::U32CheckedMul => Instruction::U32CheckedMul, + Self::U32CheckedMulImm(imm) => Instruction::U32CheckedMulImm(imm), + Self::U32OverflowingMul => Instruction::U32OverflowingMul, + Self::U32OverflowingMulImm(imm) => Instruction::U32OverflowingMulImm(imm), + Self::U32WrappingMul => Instruction::U32WrappingMul, + Self::U32WrappingMulImm(imm) => Instruction::U32WrappingMulImm(imm), + Self::U32OverflowingMadd => Instruction::U32OverflowingMadd, + Self::U32WrappingMadd => Instruction::U32WrappingMadd, + Self::U32CheckedDiv => Instruction::U32CheckedDiv, + Self::U32CheckedDivImm(imm) => Instruction::U32CheckedDivImm(imm), + Self::U32UncheckedDiv => Instruction::U32UncheckedDiv, + Self::U32UncheckedDivImm(imm) => Instruction::U32UncheckedDivImm(imm), + Self::U32CheckedMod => Instruction::U32CheckedMod, + Self::U32CheckedModImm(imm) => Instruction::U32CheckedModImm(imm), + Self::U32UncheckedMod => Instruction::U32UncheckedMod, + Self::U32UncheckedModImm(imm) => Instruction::U32UncheckedModImm(imm), + Self::U32CheckedDivMod => Instruction::U32CheckedDivMod, + Self::U32CheckedDivModImm(imm) => Instruction::U32CheckedDivModImm(imm), + Self::U32UncheckedDivMod => Instruction::U32UncheckedDivMod, + Self::U32UncheckedDivModImm(imm) => Instruction::U32UncheckedDivModImm(imm), + Self::U32And => Instruction::U32CheckedAnd, + Self::U32Or => Instruction::U32CheckedOr, + Self::U32Xor => Instruction::U32CheckedXor, + Self::U32Not => Instruction::U32CheckedNot, + Self::U32CheckedShl => Instruction::U32CheckedShl, + Self::U32CheckedShlImm(imm) => { + Instruction::U32CheckedShlImm(imm.try_into().expect("invalid rotation")) + } + Self::U32UncheckedShl => Instruction::U32UncheckedShl, + Self::U32UncheckedShlImm(imm) => { + Instruction::U32UncheckedShlImm(imm.try_into().expect("invalid rotation")) + } + Self::U32CheckedShr => Instruction::U32CheckedShr, + Self::U32CheckedShrImm(imm) => { + Instruction::U32CheckedShrImm(imm.try_into().expect("invalid rotation")) + } + Self::U32UncheckedShr => Instruction::U32UncheckedShr, + Self::U32UncheckedShrImm(imm) => { + Instruction::U32UncheckedShrImm(imm.try_into().expect("invalid rotation")) + } + Self::U32CheckedRotl => Instruction::U32CheckedRotl, + Self::U32CheckedRotlImm(imm) => { + Instruction::U32CheckedRotlImm(imm.try_into().expect("invalid rotation")) + } + Self::U32UncheckedRotl => Instruction::U32UncheckedRotl, + Self::U32UncheckedRotlImm(imm) => { + Instruction::U32UncheckedRotlImm(imm.try_into().expect("invalid rotation")) + } + Self::U32CheckedRotr => Instruction::U32CheckedRotr, + Self::U32CheckedRotrImm(imm) => { + Instruction::U32CheckedRotrImm(imm.try_into().expect("invalid rotation")) + } + Self::U32UncheckedRotr => Instruction::U32UncheckedRotr, + Self::U32UncheckedRotrImm(imm) => { + Instruction::U32UncheckedRotrImm(imm.try_into().expect("invalid rotation")) + } + Self::U32CheckedPopcnt => Instruction::U32CheckedPopcnt, + Self::U32UncheckedPopcnt => Instruction::U32UncheckedPopcnt, + Self::U32Eq => Instruction::U32CheckedEq, + Self::U32EqImm(imm) => Instruction::U32CheckedEqImm(imm), + Self::U32Neq => Instruction::U32CheckedNeq, + Self::U32NeqImm(imm) => Instruction::U32CheckedNeqImm(imm), + Self::U32CheckedLt => Instruction::U32CheckedLt, + Self::U32UncheckedLt => Instruction::U32UncheckedLt, + Self::U32CheckedLte => Instruction::U32CheckedLte, + Self::U32UncheckedLte => Instruction::U32UncheckedLte, + Self::U32CheckedGt => Instruction::U32CheckedGt, + Self::U32UncheckedGt => Instruction::U32UncheckedGt, + Self::U32CheckedGte => Instruction::U32CheckedGte, + Self::U32UncheckedGte => Instruction::U32UncheckedGte, + Self::U32CheckedMin => Instruction::U32CheckedMin, + Self::U32UncheckedMin => Instruction::U32UncheckedMin, + Self::U32CheckedMax => Instruction::U32CheckedMax, + Self::U32UncheckedMax => Instruction::U32UncheckedMax, + }; + smallvec![Node::Instruction(node)] + } +} /// This implementation displays the opcode name for the given [MasmOp] impl fmt::Display for MasmOp {