Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
87 changes: 87 additions & 0 deletions crates/codegen/src/sonatina/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,7 @@ fn lower_value_origin<'db, C: sonatina_ir::func_cursor::FuncCursor>(
AddressSpaceKind::Storage | AddressSpaceKind::TransientStorage => {
field_ptr.offset_bytes / 32
}
AddressSpaceKind::ImmutableCode => field_ptr.offset_bytes,
AddressSpaceKind::Memory => unreachable!(),
};
let offset_val = ctx.fb.make_imm_value(I256::from(offset as u64));
Expand Down Expand Up @@ -1313,6 +1314,10 @@ fn lower_intrinsic<'db, C: sonatina_ir::func_cursor::FuncCursor>(
"code region intrinsics require 1 argument".to_string(),
));
};
let mut func_item = *func_item;
while let mir::ValueOrigin::TransparentCast { value } = &ctx.body.value(func_item).origin {
func_item = *value;
}
let value_data = ctx
.body
.values
Expand Down Expand Up @@ -2209,6 +2214,12 @@ fn load_from_terminal<'db, C: sonatina_ir::func_cursor::FuncCursor>(
let loaded = apply_from_word(ctx.fb, ctx.db, raw, loaded_ty, ctx.is);
Ok(coerce_value_to_type(ctx, loaded, expected_runtime_ty))
}
AddressSpaceKind::ImmutableCode => {
let word_addr = terminal.word_addr(ctx);
let raw = load_code_word(ctx, word_addr);
let loaded = apply_from_word(ctx.fb, ctx.db, raw, loaded_ty, ctx.is);
Ok(coerce_value_to_type(ctx, loaded, expected_runtime_ty))
}
}
}

Expand Down Expand Up @@ -2805,6 +2816,55 @@ pub(super) fn lower_terminator<'db, C: sonatina_ir::func_cursor::FuncCursor>(
}
}
}
mir::TerminatingCall::DeployRuntime {
runtime_offset,
runtime_len,
immutable_payload,
} => {
let runtime_offset = lower_value(ctx, *runtime_offset)?;
let runtime_len = lower_value(ctx, *runtime_len)?;
let total_len =
if let Some((_, len)) = immutable_payload.filter(|(_, len)| *len > 0) {
let imm_len = ctx.fb.make_imm_value(I256::from(len as u64));
ctx.fb
.insert_inst(Add::new(ctx.is, runtime_len, imm_len), Type::I256)
} else {
runtime_len
};
let out = emit_evm_malloc_word_addr(ctx.fb, total_len, ctx.is);
ctx.fb.insert_inst_no_result(EvmCodeCopy::new(
ctx.is,
out,
runtime_offset,
runtime_len,
));

if let Some((immutable_ptr, immutable_len)) =
immutable_payload.filter(|(_, len)| *len > 0)
{
let immutable_ptr = lower_value(ctx, immutable_ptr)?;
let runtime_dst = ctx
.fb
.insert_inst(Add::new(ctx.is, out, runtime_len), Type::I256);
for byte_off in (0..immutable_len).step_by(32) {
let byte_off = ctx.fb.make_imm_value(I256::from(byte_off as u64));
let src = ctx
.fb
.insert_inst(Add::new(ctx.is, immutable_ptr, byte_off), Type::I256);
let dst = ctx
.fb
.insert_inst(Add::new(ctx.is, runtime_dst, byte_off), Type::I256);
let word = ctx
.fb
.insert_inst(Mload::new(ctx.is, src, Type::I256), Type::I256);
ctx.fb
.insert_inst_no_result(Mstore::new(ctx.is, dst, word, Type::I256));
}
}

ctx.fb
.insert_inst_no_result(EvmReturn::new(ctx.is, out, total_len));
}
},
Terminator::Unreachable { .. } => {
// Emit INVALID opcode (0xFE) - this consumes all gas and reverts
Expand Down Expand Up @@ -2932,6 +2992,11 @@ fn store_word_to_place<'db, C: sonatina_ir::func_cursor::FuncCursor>(
AddressSpaceKind::Calldata => {
return Err(LowerError::Unsupported("store to calldata".to_string()));
}
AddressSpaceKind::ImmutableCode => {
return Err(LowerError::Unsupported(
"store to immutable code".to_string(),
));
}
}
Ok(())
}
Expand Down Expand Up @@ -2986,6 +3051,9 @@ fn store_runtime_value_to_place<'db, C: sonatina_ir::func_cursor::FuncCursor>(
Ok(())
}
AddressSpaceKind::Calldata => Err(LowerError::Unsupported("store to calldata".to_string())),
AddressSpaceKind::ImmutableCode => Err(LowerError::Unsupported(
"store to immutable code".to_string(),
)),
}
}

Expand Down Expand Up @@ -3229,6 +3297,25 @@ fn emit_evm_malloc_word_addr<C: sonatina_ir::func_cursor::FuncCursor>(
fb.insert_inst(PtrToInt::new(is, ptr, Type::I256), Type::I256)
}

fn load_code_word<C: sonatina_ir::func_cursor::FuncCursor>(
ctx: &mut LowerCtx<'_, '_, C>,
code_offset: ValueId,
) -> ValueId {
let word_size = ctx.fb.make_imm_value(I256::from(32u64));
let ptr = if let Some(ptr) = *ctx.code_load_scratch {
ptr
} else {
let alloca_ty = ctx.fb.declare_array_type(Type::I8, 32);
let ptr = emit_alloca_word_addr(ctx.fb, alloca_ty, ctx.is);
*ctx.code_load_scratch = Some(ptr);
ptr
};
ctx.fb
.insert_inst_no_result(EvmCodeCopy::new(ctx.is, ptr, code_offset, word_size));
ctx.fb
.insert_inst(Mload::new(ctx.is, ptr, Type::I256), Type::I256)
}

/// Lower a `ConstAggregate` by registering a global data section and using CODECOPY.
///
/// Registers the constant bytes as a Sonatina global variable (data section),
Expand Down
11 changes: 7 additions & 4 deletions crates/codegen/src/sonatina/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ pub fn compile_module(
) -> Result<Module, LowerError> {
// Lower HIR to MIR
let mir_module = lower_module(db, top_mod)?;

compile_mir_module(
db,
&mir_module,
Expand Down Expand Up @@ -390,7 +389,7 @@ fn compile_mir_module_for_sonatina_output<'db>(
) -> Result<(Module, Vec<String>), LowerError> {
use mir::analysis::build_contract_graph;

let contract_graph = build_contract_graph(&mir_module.functions);
let contract_graph = build_contract_graph(db, &mir_module.functions);
let mut contract_names: Vec<String> = contract_graph.contracts.keys().cloned().collect();
contract_names.sort();

Expand Down Expand Up @@ -769,7 +768,7 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
fn identify_entry_functions(&mut self) -> Result<(), LowerError> {
use mir::analysis::build_contract_graph;

let contract_graph = build_contract_graph(&self.mir.functions);
let contract_graph = build_contract_graph(self.db, &self.mir.functions);
if contract_graph.contracts.is_empty() {
return Ok(());
}
Expand Down Expand Up @@ -815,7 +814,7 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
fn create_objects(&mut self) -> Result<(), LowerError> {
use mir::analysis::build_contract_graph;

let contract_graph = build_contract_graph(&self.mir.functions);
let contract_graph = build_contract_graph(self.db, &self.mir.functions);
if !contract_graph.contracts.is_empty() {
return self.create_contract_objects(&contract_graph);
}
Expand Down Expand Up @@ -1275,6 +1274,7 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
{
let mut const_data_globals = FxHashMap::default();
let mut overflow_revert_block = None;
let mut code_load_scratch = None;
let ptr_escape_summary = self.ptr_escape_summaries.get(&func.symbol_name);
let mut ctx = LowerCtx {
fb: &mut fb,
Expand All @@ -1298,6 +1298,7 @@ impl<'db, 'a> ModuleLowerer<'db, 'a> {
data_global_counter: &mut self.data_global_counter,
const_data_globals: &mut const_data_globals,
overflow_revert_block: &mut overflow_revert_block,
code_load_scratch: &mut code_load_scratch,
ptr_escape_summary,
};
for (idx, block) in ctx.body.blocks.iter().enumerate() {
Expand Down Expand Up @@ -1355,6 +1356,8 @@ pub(super) struct LowerCtx<'a, 'db, C: sonatina_ir::func_cursor::FuncCursor> {
pub(super) const_data_globals: &'a mut FxHashMap<Vec<u8>, GlobalVariableRef>,
/// Lazily-created shared overflow trap block for checked arithmetic in this function.
pub(super) overflow_revert_block: &'a mut Option<BlockId>,
/// Lazily-created shared scratch word used for immutable code reads in this function.
pub(super) code_load_scratch: &'a mut Option<ValueId>,
/// Escape summary for the current function.
pub(super) ptr_escape_summary: Option<&'a mir::analysis::escape::MirPtrEscapeSummary>,
}
2 changes: 1 addition & 1 deletion crates/codegen/src/sonatina/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub fn emit_test_module_sonatina(
return Ok(TestModuleOutput { tests: Vec::new() });
}

let call_graph = build_call_graph(&mir_module.functions);
let call_graph = build_call_graph(db, &mir_module.functions);
let funcs_by_symbol = build_funcs_by_symbol(&mir_module.functions);

let code_region_roots = collect_code_region_roots(&mir_module.functions);
Expand Down
57 changes: 57 additions & 0 deletions crates/codegen/src/yul/emitter/control_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ impl<'db> FunctionEmitter<'db> {
))
}
}
mir::TerminatingCall::DeployRuntime {
runtime_offset,
runtime_len,
immutable_payload,
} => self.emit_deploy_runtime_terminator(
*runtime_offset,
*runtime_len,
*immutable_payload,
ctx,
),
},
Terminator::Branch {
cond,
Expand Down Expand Up @@ -193,6 +203,53 @@ impl<'db> FunctionEmitter<'db> {
Ok(())
}

fn emit_deploy_runtime_terminator(
&mut self,
runtime_offset: ValueId,
runtime_len: ValueId,
immutable_payload: Option<(ValueId, usize)>,
ctx: &mut BlockEmitCtx<'_, '_, '_>,
) -> Result<(), YulError> {
let runtime_offset = self.lower_value(runtime_offset, ctx.state)?;
let runtime_len = self.lower_value(runtime_len, ctx.state)?;
let total_len = if let Some((_, len)) = immutable_payload.filter(|(_, len)| *len > 0) {
let total_len = ctx.state.alloc_local();
ctx.docs.push(YulDoc::line(format!(
"let {total_len} := add({runtime_len}, 0x{len:x})"
)));
total_len
} else {
runtime_len.clone()
};

let out = ctx.state.alloc_local();
ctx.docs
.push(YulDoc::line(format!("let {out} := mload(64)")));
ctx.docs.push(YulDoc::block(
format!("if iszero({out}) "),
vec![YulDoc::line(format!("{out} := 0x80"))],
));
ctx.docs
.push(YulDoc::line(format!("mstore(64, add({out}, {total_len}))")));
ctx.docs.push(YulDoc::line(format!(
"codecopy({out}, {runtime_offset}, {runtime_len})"
)));

if let Some((immutable_ptr, immutable_len)) = immutable_payload.filter(|(_, len)| *len > 0)
{
let immutable_ptr = self.lower_value(immutable_ptr, ctx.state)?;
for byte_off in (0..immutable_len).step_by(32) {
ctx.docs.push(YulDoc::line(format!(
"mstore(add({out}, add({runtime_len}, 0x{byte_off:x})), mload(add({immutable_ptr}, 0x{byte_off:x})))"
)));
}
}

ctx.docs
.push(YulDoc::line(format!("return({out}, {total_len})")));
Ok(())
}

/// Lowers an `if cond -> then else` branch terminator into Yul conditionals.
///
/// * `cond` - MIR value representing the branch predicate.
Expand Down
2 changes: 2 additions & 0 deletions crates/codegen/src/yul/emitter/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@ impl<'db> FunctionEmitter<'db> {
}
mir::ir::AddressSpaceKind::Storage
| mir::ir::AddressSpaceKind::TransientStorage => field_ptr.offset_bytes / 32,
mir::ir::AddressSpaceKind::ImmutableCode => field_ptr.offset_bytes,
};
Ok(format!("add({}, {})", base, offset))
}
Expand Down Expand Up @@ -803,6 +804,7 @@ impl<'db> FunctionEmitter<'db> {
mir::ir::AddressSpaceKind::Calldata => format!("calldataload({addr})"),
mir::ir::AddressSpaceKind::Storage => format!("sload({addr})"),
mir::ir::AddressSpaceKind::TransientStorage => format!("tload({addr})"),
mir::ir::AddressSpaceKind::ImmutableCode => format!("__fe_code_load({addr})"),
};

// Apply type-specific conversion (std::evm::word::WordRepr::from_word equivalent)
Expand Down
Loading
Loading