Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cargo extension for compiling Rust to MASM #61

Merged
merged 22 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
367bb3c
chore: draft `compile` command options using clap
greenhat Nov 20, 2023
f09a893
test: draft `compile` command using the `midenc_driver`, add test
greenhat Nov 20, 2023
e706024
test: get target dir from CARGO_TARGET_DIR env var when locating Wasm…
greenhat Nov 10, 2023
66a3d32
fix: emit Session artifact in CodegenStage, don't require Session.mat…
greenhat Nov 21, 2023
134fce3
test: check target folder exists after build, not before
greenhat Nov 10, 2023
2db01ff
chore: print the progress of the compilation
greenhat Nov 13, 2023
555051b
refactor: make `cargo_ext:compile` return `anyhow::Result`
greenhat Nov 13, 2023
5ad3645
refactor: use cargo-metadata to get Wasm artifacts
greenhat Nov 20, 2023
e108242
chore: add README.md to cargo-ext
greenhat Nov 14, 2023
58d3309
feat: add `new` command that generates a new Miden project from a tem…
greenhat Nov 20, 2023
824a8eb
fix: fix build after the rebase
greenhat Nov 20, 2023
62adf34
fix: switch to https://github.com/0xPolygonMiden/rust-templates
greenhat Nov 21, 2023
af32c71
fix build after rebase
greenhat Nov 21, 2023
5a8a6ed
refactor: switch from emiting MASM in CodegenStage, and switch to out…
greenhat Nov 21, 2023
91a8b3c
test: in cargo extension test create output folder if not exist
greenhat Nov 21, 2023
29ffb83
refactor: move cargo-ext to tools/cargo-miden
greenhat Dec 5, 2023
426096c
chore: remove miden-cargo-extension from workspace deps
greenhat Dec 5, 2023
5f0892f
remove `next_display_order` option in `Command`
greenhat Dec 5, 2023
a69711b
fix: rename `compile` cargo extension command to `build`; imports cle…
greenhat Dec 5, 2023
253d491
refactor: remove `path-absolutize` dependency
greenhat Dec 5, 2023
6e563ef
feat: revamp cargo-miden to pass the unrecognized options to cargo,
greenhat Dec 7, 2023
769d983
refactor: make `WasmTranslationConfig::module_name_fallback` non-opti…
greenhat Dec 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,359 changes: 4,509 additions & 850 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ members = [
"tests/rust-apps/*",
"tests/integration",
]
exclude = ["tests/rust-apps-wasm"]
exclude = ["tests/rust-apps-wasm", "cargo-ext/tests/data"]

[workspace.package]
version = "0.1.0"
Expand Down Expand Up @@ -78,7 +78,6 @@ midenc-driver = { path = "midenc-driver" }
midenc-session = { path = "midenc-session" }
miden-integration-tests = { path = "tests/integration" }


[profile.dev]
lto = false
# Needed for 'inventory' to work
Expand Down
15 changes: 13 additions & 2 deletions frontend-wasm/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/// Configuration for the WASM translation.
#[derive(Debug, Default)]
pub struct WasmTranslationConfig {}
#[derive(Debug)]
pub struct WasmTranslationConfig {
/// The module name to use if Wasm module doesn't have one.
pub module_name_fallback: String,
}

impl Default for WasmTranslationConfig {
fn default() -> Self {
Self {
module_name_fallback: "noname".to_string(),
}
}
}
4 changes: 2 additions & 2 deletions frontend-wasm/src/module_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ pub struct ModuleEnvironment<'a> {

impl<'a> ModuleEnvironment<'a> {
/// Creates a new `ModuleEnvironment` instance.
pub fn new() -> Self {
pub fn new(name: String) -> Self {
Self {
info: ModuleInfo::new(Ident::with_empty_span(Symbol::intern("noname"))),
info: ModuleInfo::new(Ident::with_empty_span(Symbol::intern(name))),
trans: FuncTranslator::new(),
function_bodies: PrimaryMap::new(),
data_segments: PrimaryMap::new(),
Expand Down
4 changes: 2 additions & 2 deletions frontend-wasm/src/module_translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ struct WasmTranslationResult {
/// Translate a sequence of bytes forming a valid Wasm binary into Miden IR
fn translate_module_inner(
wasm: &[u8],
_config: &WasmTranslationConfig,
config: &WasmTranslationConfig,
diagnostics: &DiagnosticsHandler,
) -> WasmResult<WasmTranslationResult> {
let mut module_env = ModuleEnvironment::new();
let mut module_env = ModuleEnvironment::new(config.module_name_fallback.clone());
let env = &mut module_env;
let wasm_features = WasmFeatures::default();
let mut validator = Validator::new_with_features(wasm_features);
Expand Down
5 changes: 2 additions & 3 deletions midenc-compile/src/stages/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ impl Stage for CodegenStage {
match input {
MaybeLinked::Linked(program) => {
let mut convert_to_masm = masm::ConvertHirToMasm::<hir::Program>::default();
Ok(convert_to_masm
.convert(program, analyses, session)
.map(Compiled::Program)?)
let program = convert_to_masm.convert(program, analyses, session)?;
Ok(Compiled::Program(program))
}
MaybeLinked::Unlinked(modules) => {
let mut convert_to_masm = masm::ConvertHirToMasm::<hir::Module>::default();
Expand Down
2 changes: 1 addition & 1 deletion midenc-compile/src/stages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{CompilerError, CompilerResult};
use miden_codegen_masm as masm;
use miden_frontend_wasm as wasm;
use miden_hir::pass::{AnalysisManager, ConversionPass, RewritePass};
use miden_hir::{self as hir, parser::ast, PassInfo};
use miden_hir::{self as hir, parser::ast};
use midenc_session::Session;

mod codegen;
Expand Down
21 changes: 16 additions & 5 deletions midenc-compile/src/stages/parse.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use midenc_session::InputFile;
use std::path::Path;
use wasm::WasmTranslationConfig;

use super::*;

Expand Down Expand Up @@ -35,9 +36,15 @@ impl Stage for ParseStage {
FileType::Wasm => self.parse_hir_from_wasm_file(path.as_ref(), &session),
unsupported => unreachable!("unsupported file type: {unsupported}"),
},
InputType::Stdin { ref input, .. } => match file_type {
InputType::Stdin { name, ref input } => match file_type {
FileType::Hir => self.parse_ast_from_bytes(&input, &session),
FileType::Wasm => self.parse_hir_from_wasm_bytes(&input, &session),
FileType::Wasm => self.parse_hir_from_wasm_bytes(
bitwalker marked this conversation as resolved.
Show resolved Hide resolved
&input,
&session,
&WasmTranslationConfig {
module_name_fallback: name.to_string().clone(),
},
),
unsupported => unreachable!("unsupported file type: {unsupported}"),
},
}
Expand Down Expand Up @@ -80,16 +87,20 @@ impl ParseStage {
let mut file = std::fs::File::open(path)?;
let mut bytes = Vec::with_capacity(1024);
file.read_to_end(&mut bytes)?;
self.parse_hir_from_wasm_bytes(&bytes, session)
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
let config = wasm::WasmTranslationConfig {
module_name_fallback: file_name,
};
self.parse_hir_from_wasm_bytes(&bytes, session, &config)
}

fn parse_hir_from_wasm_bytes(
&self,
bytes: &[u8],
session: &Session,
config: &WasmTranslationConfig,
) -> CompilerResult<ParseOutput> {
let config = wasm::WasmTranslationConfig::default();
let module = wasm::translate_module(bytes, &config, &session.diagnostics)?;
let module = wasm::translate_module(bytes, config, &session.diagnostics)?;

Ok(ParseOutput::Hir(Box::new(module)))
}
Expand Down
15 changes: 7 additions & 8 deletions midenc-compile/src/stages/rewrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ impl Stage for ApplyRewritesStage {
let matches = session.matches();
for rewrite in inventory::iter::<RewritePassRegistration<hir::Module>> {
let flag = rewrite.name();
if let Some(index) = matches.index_of(flag) {
let is_enabled = matches.get_flag(flag);
if is_enabled {
registered.push((index, rewrite.get()));
if matches.try_contains_id(flag).is_ok() {
if let Some(index) = matches.index_of(flag) {
let is_enabled = matches.get_flag(flag);
if is_enabled {
registered.push((index, rewrite.get()));
}
}
}
}
Expand All @@ -41,10 +43,7 @@ impl Stage for ApplyRewritesStage {
// Otherwise, assume that the intent was to skip those rewrites and do not add them
let mut rewrites = RewriteSet::default();
if registered.is_empty() {
let convert_to_masm_flag = <masm::ConvertHirToMasm<hir::Module> as PassInfo>::FLAG;
let convert_to_masm = matches.get_flag(convert_to_masm_flag);

if convert_to_masm {
if session.should_codegen() {
rewrites.push(ModuleRewritePassAdapter::new(
transforms::SplitCriticalEdges,
));
Expand Down
10 changes: 7 additions & 3 deletions midenc-session/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,17 @@ impl Session {
if self.should_emit(output_type) {
match self.output_files.path(output_type) {
OutputFile::Real(path) => {
let path = if let Some(name) = item.name() {
path.with_file_name(name.as_str())
let file_path = if path.is_dir() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output file path is guaranteed to always be a file path, not a directory, so this check is unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my case, it's a folder since I'm passing a target folder as output_dir in Session.

Copy link
Contributor

@bitwalker bitwalker Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the OutputFile API is designed to always return a file path, so if it's returning a directory there's a bug somewhere. I'll take a closer look

let item_name = item
.name()
.map(|s| s.to_string())
.unwrap_or("noname".to_string());
path.join(item_name.as_str())
.with_extension(output_type.extension())
} else {
path
};
item.write_to_file(&path)?;
item.write_to_file(&file_path)?;
}
OutputFile::Stdout => {
item.write_to_stdout()?;
Expand Down
41 changes: 41 additions & 0 deletions tools/cargo-miden/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[package]
name = "cargo-miden"
version.workspace = true
rust-version.workspace = true
authors.workspace = true
description.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
categories.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
edition.workspace = true
publish.workspace = true
autotests = false # disable autodiscovery of tests

[[bin]]
name = "cargo-miden"

[[test]]
name = "integration"
path = "tests/mod.rs"

[dependencies]
midenc-compile.workspace = true
midenc-session.workspace = true
miden-diagnostics.workspace = true
env_logger.workspace = true
log.workspace = true
clap.workspace = true
anyhow.workspace = true
cargo-component = "0.5"
cargo-component-core = "0.5"
cargo_metadata = "0.18"
cargo-generate = "0.18"
semver = "1.0.20"
parse_arg = "0.1.4"
path-absolutize = "3.1.1"

[dev-dependencies]
42 changes: 42 additions & 0 deletions tools/cargo-miden/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Miden Cargo Extension

This crate provides a cargo extension that allows you to compile Rust code to Miden VM MASM.

## Installation

To install the extension, run:

```bash
cargo install cargo-miden
```

## Usage

### Getting help
To get help with the extension, run:

```bash
cargo miden
```

Or for help with a specific command:

```bash
cargo miden <command> --help
```

### Creating a new project
To create a new Miden VM project, run:

```bash
cargo miden new <project-name>
```

### Compiling a project
To compile a Rust crate to Miden VM MASM, run:

```bash
cargo miden build
```

Without any additional arguments, this will compile the library target in the target directory in the `miden` folder.
64 changes: 64 additions & 0 deletions tools/cargo-miden/src/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::{
path::{Path, PathBuf},
sync::Arc,
};

use anyhow::{bail, Context};
use miden_diagnostics::Verbosity;
use midenc_session::{
InputFile, OutputFile, OutputType, OutputTypeSpec, OutputTypes, ProjectType, Session, TargetEnv,
};

pub fn build_masm(
wasm_file_path: &Path,
output_folder: &Path,
is_bin: bool,
) -> anyhow::Result<PathBuf> {
let project_type = if is_bin {
ProjectType::Program
} else {
ProjectType::Library
};

if !output_folder.exists() {
bail!(
"MASM output folder '{}' does not exist.",
output_folder.to_str().unwrap()
);
}
log::debug!(
"Compiling '{}' Wasm to '{}' directory with midenc ...",
wasm_file_path.to_str().unwrap(),
&output_folder.to_str().unwrap()
);
let input = InputFile::from_path(wasm_file_path).context("Invalid input file")?;
let output_file_folder = OutputFile::Real(output_folder.to_path_buf());
let output_type = OutputType::Masm;
let output_types = OutputTypes::new(vec![OutputTypeSpec {
output_type,
path: Some(output_file_folder.clone()),
}]);
let cwd = std::env::current_dir().context("Failed to get current working directory")?;
let options = midenc_session::Options::new(cwd)
// .with_color(color)
.with_verbosity(Verbosity::Debug)
// .with_warnings(self.warn)
.with_output_types(output_types);
let target = TargetEnv::default();
let session = Arc::new(
Session::new(
target,
input,
Some(output_folder.to_path_buf()),
None,
None,
options,
None,
)
.with_project_type(project_type),
);
midenc_compile::compile(session.clone()).context("Wasm to MASM compilation failed!")?;
let mut output_path = output_folder.join(wasm_file_path.file_stem().unwrap());
output_path.set_extension(output_type.extension());
Ok(output_path)
}
Loading
Loading