From d8557300bd4446409700240d19e9ea86d121b5b2 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 29 Nov 2024 16:22:30 +0200 Subject: [PATCH] feature: draft `LowerExportsCrossCtxStage` implementation with a lot of hardcoded shortcuts. --- .../src/stages/lower_cross_ctx/mod.rs | 122 +++++++++++++++++- .../expected/rust_sdk/cross_ctx_account.masm | 9 ++ 2 files changed, 126 insertions(+), 5 deletions(-) diff --git a/midenc-compile/src/stages/lower_cross_ctx/mod.rs b/midenc-compile/src/stages/lower_cross_ctx/mod.rs index 2e227874..77268f10 100644 --- a/midenc-compile/src/stages/lower_cross_ctx/mod.rs +++ b/midenc-compile/src/stages/lower_cross_ctx/mod.rs @@ -1,7 +1,13 @@ //! Lowering the exports for cross-context calls. -use midenc_hir::pass::AnalysisManager; -use midenc_session::Session; +use std::collections::BTreeMap; + +use midenc_hir::{ + diagnostics::Severity, pass::AnalysisManager, types::Abi::Canonical, AbiParam, CallConv, + ComponentBuilder, ComponentExport, FunctionType, InstBuilder, InterfaceFunctionIdent, Linkage, + Signature, SourceSpan, Type, +}; +use midenc_session::{DiagnosticsHandler, Session}; use super::LinkerInput; use crate::{stage::Stage, CompilerResult}; @@ -17,7 +23,9 @@ pub struct LowerExportsCrossCtxStage; // TODO: load the rodata into the memory in the lowering to ensure that the fresh context is // correctly initialized -// TODO: Don't lower the note script's entry point + +// TODO: swap `lift` and `lower` in the component import/export pretty-printing to sync with +// this stage's terminology (an export is lowered, an import is lifted) impl Stage for LowerExportsCrossCtxStage { type Input = LinkerInput; @@ -27,8 +35,112 @@ impl Stage for LowerExportsCrossCtxStage { &mut self, input: Self::Input, _analyses: &mut AnalysisManager, - _session: &Session, + session: &Session, ) -> CompilerResult { - Ok(input) + let component = if let LinkerInput::Hir(component) = input { + component + } else { + return Ok(input); + }; + + let mut component_builder = ComponentBuilder::load(*component, &session.diagnostics); + + // For each component export ensure a module for each interface that exports lowered functions + // which and the end call this Wasm core module exported function + + let mut lowered_exports: BTreeMap = + BTreeMap::new(); + let exports = component_builder.exports().clone(); + for (id, export) in exports.into_iter() { + if let Canonical = export.function_ty.abi() { + // skip exports that are already lowered + lowered_exports.insert(id, export); + continue; + } + if let Some(entrypoint) = &session.options.entrypoint { + // skip the entrypoint + if entrypoint == &id.to_string() { + lowered_exports.insert(id, export); + continue; + } + } + let new_export = generate_lowered_function( + &mut component_builder, + id, + export, + &session.diagnostics, + )?; + lowered_exports.insert(id, new_export); + } + + let component_builder = component_builder.with_exports(lowered_exports); + + let component = component_builder.build(); + // dbg!(&component.exports()); + // dbg!(&component.modules().len()); + Ok(LinkerInput::Hir(component.into())) + } +} + +fn generate_lowered_function( + component_builder: &mut ComponentBuilder, + export_id: InterfaceFunctionIdent, + export: ComponentExport, + diagnostics: &DiagnosticsHandler, +) -> CompilerResult { + // get or create the module for the interface + let module_id = export_id.interface.full_name; + let mut module_builder = component_builder.module(module_id); + // TODO: analyze the signature and speculate what cross-context Miden ABI signature we need to export. + // For now just assume passing <16 felts and returning 1 and copy the signature + let cc_export_sig = Signature { + params: vec![AbiParam::new(Type::Felt)], + results: vec![AbiParam::new(Type::Felt)], + // TODO: add CallConv::CrossCtx + cc: CallConv::SystemV, + linkage: Linkage::External, + }; + let mut builder = module_builder.function(export_id.function, cc_export_sig.clone())?; + let entry = builder.current_block(); + let params = builder.block_params(entry).to_vec(); + // TODO: lift the params from the cross-context Miden ABI to the Wasm CABI + + // TODO: put the core function signature in the export + let core_sig = Signature { + params: vec![AbiParam::new(Type::Felt)], + results: vec![AbiParam::new(Type::Felt)], + cc: CallConv::SystemV, + linkage: Linkage::Internal, + }; + let dfg = builder.data_flow_graph_mut(); + // import the Wasm core function + if dfg.get_import(&export.function).is_none() { + dfg.import_function(export.function.module, export.function.function, core_sig) + .map_err(|_e| { + let message = format!( + "Function(callee of the lowering) with name {} in module {} with signature \ + {cc_export_sig:?} is already imported (function call) with a different \ + signature", + export.function.function, export.function.module + ); + diagnostics.diagnostic(Severity::Error).with_message(message).into_report() + })?; } + // TODO: use the span from the callee + let call = builder.ins().exec(export.function, ¶ms, SourceSpan::UNKNOWN); + // dbg!(&sig); + // TODO: lower the result from the Wasm CABI to the cross-context Miden ABI + let result = builder.first_result(call); + builder.ins().ret(Some(result), SourceSpan::UNKNOWN); + let function_id = builder.build()?; + module_builder.build()?; + let component_export = ComponentExport { + function: function_id, + function_ty: FunctionType { + abi: Canonical, + ..export.function_ty + }, + ..export + }; + Ok(component_export) } diff --git a/tests/integration/expected/rust_sdk/cross_ctx_account.masm b/tests/integration/expected/rust_sdk/cross_ctx_account.masm index 4992ec4d..48e7cabf 100644 --- a/tests/integration/expected/rust_sdk/cross_ctx_account.masm +++ b/tests/integration/expected/rust_sdk/cross_ctx_account.masm @@ -1,3 +1,12 @@ +# mod #anon::miden:cross-ctx-account/foo@1.0.0 + +use.cross_ctx_account + +export."process-felt" + exec.::cross_ctx_account::"miden:cross-ctx-account/foo@1.0.0#process-felt" +end + + # mod cross_ctx_account use.intrinsics::mem