@@ -1416,10 +1416,16 @@ impl CodeGenerator {
14161416 self.emit("add t0, sp, t6");
14171417 self.emit(format!("addi t0, t0, {}", ENTRY_WITNESS_BUFFER_OFFSET));
14181418 if abi_index < 8 {
1419- self.emit_entry_witness_scalar_load_from_reg(&format!("a{}", abi_index), "t0", width, param.ty == IrType::I32);
1419+ self.emit_entry_witness_scalar_load_from_reg(
1420+ &format!("a{}", abi_index),
1421+ "t0",
1422+ "t1",
1423+ width,
1424+ param.ty == IrType::I32,
1425+ );
14201426 } else {
14211427 let caller_stack_offset = (abi_index - 8) * 8;
1422- self.emit_entry_witness_scalar_load_from_reg("t3", "t0", width, param.ty == IrType::I32);
1428+ self.emit_entry_witness_scalar_load_from_reg("t3", "t0", "t1", width, param.ty == IrType::I32);
14231429 self.emit(format!(
14241430 "# cellscript entry abi: scalar param {} stored to caller stack +{}",
14251431 param.name, caller_stack_offset
@@ -1628,14 +1634,24 @@ impl CodeGenerator {
16281634 }
16291635 }
16301636
1631- fn emit_entry_witness_scalar_load_from_reg(&mut self, dest_reg: &str, base_reg: &str, width: usize, signed_i32: bool) {
1637+ fn emit_entry_witness_scalar_load_from_reg(
1638+ &mut self,
1639+ dest_reg: &str,
1640+ base_reg: &str,
1641+ byte_reg: &str,
1642+ width: usize,
1643+ signed_i32: bool,
1644+ ) {
1645+ debug_assert_ne!(dest_reg, base_reg, "entry scalar decoder destination must not alias its base");
1646+ debug_assert_ne!(byte_reg, base_reg, "entry scalar decoder scratch must not alias its base");
1647+ debug_assert_ne!(byte_reg, dest_reg, "entry scalar decoder scratch must not alias its destination");
16321648 self.emit(format!("li {}, 0", dest_reg));
16331649 for byte_index in 0..width {
1634- self.emit(format!("lbu t0 , {}({})", byte_index, base_reg));
1650+ self.emit(format!("lbu {} , {}({})", byte_reg , byte_index, base_reg));
16351651 if byte_index != 0 {
1636- self.emit(format!("slli t0, t0 , {}", byte_index * 8));
1652+ self.emit(format!("slli {}, {} , {}", byte_reg, byte_reg , byte_index * 8));
16371653 }
1638- self.emit(format!("or {}, {}, t0 ", dest_reg, dest_reg));
1654+ self.emit(format!("or {}, {}, {} ", dest_reg, dest_reg, byte_reg ));
16391655 }
16401656 if signed_i32 {
16411657 self.emit_sign_extend_i32(dest_reg);
@@ -1653,14 +1669,17 @@ impl CodeGenerator {
16531669 }
16541670 }
16551671
1656- fn emit_entry_load_u32_from_reg(&mut self, dest_reg: &str, base_reg: &str) {
1672+ fn emit_entry_load_u32_from_reg(&mut self, dest_reg: &str, base_reg: &str, byte_reg: &str) {
1673+ debug_assert_ne!(dest_reg, base_reg, "entry u32 decoder destination must not alias its base");
1674+ debug_assert_ne!(byte_reg, base_reg, "entry u32 decoder scratch must not alias its base");
1675+ debug_assert_ne!(byte_reg, dest_reg, "entry u32 decoder scratch must not alias its destination");
16571676 self.emit(format!("li {}, 0", dest_reg));
16581677 for byte_index in 0..4 {
1659- self.emit(format!("lbu t0 , {}({})", byte_index, base_reg));
1678+ self.emit(format!("lbu {} , {}({})", byte_reg , byte_index, base_reg));
16601679 if byte_index != 0 {
1661- self.emit(format!("slli t0, t0 , {}", byte_index * 8));
1680+ self.emit(format!("slli {}, {} , {}", byte_reg, byte_reg , byte_index * 8));
16621681 }
1663- self.emit(format!("or {}, {}, t0 ", dest_reg, dest_reg));
1682+ self.emit(format!("or {}, {}, {} ", dest_reg, dest_reg, byte_reg ));
16641683 }
16651684 }
16661685
@@ -1718,14 +1737,14 @@ impl CodeGenerator {
17181737
17191738 self.emit_sp_addi("t0", ENTRY_SCRIPT_BUFFER_OFFSET);
17201739 self.emit("add t0, t0, t4");
1721- self.emit_entry_load_u32_from_reg("t5", "t0");
1740+ self.emit_entry_load_u32_from_reg("t5", "t0", "t1" );
17221741 self.emit("addi t6, t4, 4");
17231742 self.emit("add t1, t6, t5");
17241743 self.emit("sltu t2, t3, t1");
17251744 self.emit(format!("beqz t2, {}", args_span_ok_label));
17261745 self.emit(format!("j {}", fail_label));
17271746 self.emit_label(&args_span_ok_label);
1728- self.emit_stack_store ("t6", ENTRY_SCRIPT_ARGS_START_OFFSET);
1747+ self.emit_stack_store_with_avoid ("t6", ENTRY_SCRIPT_ARGS_START_OFFSET, &["t5"] );
17291748 self.emit_stack_store("t5", ENTRY_SCRIPT_ARGS_LEN_OFFSET);
17301749 self.emit("li t0, 0");
17311750 self.emit_stack_store("t0", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
@@ -1749,34 +1768,36 @@ impl CodeGenerator {
17491768 };
17501769 let bytes_ok_label = self.fresh_label("entry_lock_args_bytes_ok");
17511770 self.emit(format!("# cellscript entry abi: lock_args param {} consumes {} script arg byte(s)", param.name, width));
1752- self.emit_stack_load("t6", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
1753- self.emit_stack_load("t5", ENTRY_SCRIPT_ARGS_LEN_OFFSET);
1754- self.emit(format!("addi t1, t6, {}", width));
1755- self.emit("sltu t2, t5, t1");
1771+ let witness_cursor_live = ["t5", "t6"];
1772+ let witness_and_script_cursor_live = ["t3", "t5", "t6"];
1773+ self.emit_stack_load_with_avoid("t3", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET, &witness_cursor_live);
1774+ self.emit_stack_load_with_avoid("t4", ENTRY_SCRIPT_ARGS_LEN_OFFSET, &witness_and_script_cursor_live);
1775+ self.emit(format!("addi t1, t3, {}", width));
1776+ self.emit("sltu t2, t4, t1");
17561777 self.emit(format!("beqz t2, {}", bytes_ok_label));
17571778 self.emit(format!("j {}", fail_label));
17581779 self.emit_label(&bytes_ok_label);
1759- self.emit_stack_load("t3 ", ENTRY_SCRIPT_ARGS_START_OFFSET);
1760- self.emit("add t3, t3, t6 ");
1780+ self.emit_stack_load_with_avoid("t4 ", ENTRY_SCRIPT_ARGS_START_OFFSET, &witness_and_script_cursor_live );
1781+ self.emit("add t4, t4, t3 ");
17611782 self.emit_sp_addi("t0", ENTRY_SCRIPT_BUFFER_OFFSET);
1762- self.emit("add t0, t0, t3 ");
1783+ self.emit("add t0, t0, t4 ");
17631784
17641785 if fixed_byte_width.is_some() {
17651786 self.emit_entry_abi_reg_arg(*abi_index, "t0", outgoing_stack_arg_bytes);
17661787 self.emit_entry_abi_immediate_arg(*abi_index + 1, width as u64, outgoing_stack_arg_bytes);
17671788 *abi_index += 2;
17681789 } else if *abi_index < 8 {
1769- self.emit_entry_witness_scalar_load_from_reg(&format!("a{}", *abi_index), "t0", width, param.ty == IrType::I32);
1790+ self.emit_entry_witness_scalar_load_from_reg(&format!("a{}", *abi_index), "t0", "t1", width, param.ty == IrType::I32);
17701791 *abi_index += 1;
17711792 } else {
1772- self.emit_entry_witness_scalar_load_from_reg("t4", "t0", width, param.ty == IrType::I32);
1793+ self.emit_entry_witness_scalar_load_from_reg("t4", "t0", "t1", width, param.ty == IrType::I32);
17731794 self.emit_entry_abi_reg_arg(*abi_index, "t4", outgoing_stack_arg_bytes);
17741795 *abi_index += 1;
17751796 }
17761797
1777- self.emit_stack_load("t6 ", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
1778- self.emit(format!("addi t6, t6 , {}", width));
1779- self.emit_stack_store("t6 ", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET);
1798+ self.emit_stack_load_with_avoid("t3 ", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET, &witness_cursor_live );
1799+ self.emit(format!("addi t3, t3 , {}", width));
1800+ self.emit_stack_store_with_avoid("t3 ", ENTRY_SCRIPT_ARGS_CURSOR_OFFSET, &witness_cursor_live );
17801801 }
17811802
17821803 fn emit_entry_lock_args_exact_size_check(&mut self, fail_label: &str) {
@@ -3436,7 +3457,12 @@ impl CodeGenerator {
34363457
34373458 /// Emit `ld rd, offset(sp)` through the centralized stack-offset gate.
34383459 fn emit_stack_load(&mut self, rd: &str, offset: usize) {
3439- self.emit_stack_access("ld", rd, offset);
3460+ self.emit_stack_access_with_avoid("ld", rd, offset, &[]);
3461+ }
3462+
3463+ /// Emit `ld rd, offset(sp)` without clobbering explicitly live registers.
3464+ fn emit_stack_load_with_avoid(&mut self, rd: &str, offset: usize, avoid: &[&str]) {
3465+ self.emit_stack_access_with_avoid("ld", rd, offset, avoid);
34403466 }
34413467
34423468 /// Emit `lbu rd, offset(sp)` through the centralized stack-offset gate.
@@ -3446,7 +3472,12 @@ impl CodeGenerator {
34463472
34473473 /// Emit `sd rs2, offset(sp)` through the centralized stack-offset gate.
34483474 fn emit_stack_store(&mut self, rs2: &str, offset: usize) {
3449- self.emit_stack_access("sd", rs2, offset);
3475+ self.emit_stack_access_with_avoid("sd", rs2, offset, &[]);
3476+ }
3477+
3478+ /// Emit `sd rs2, offset(sp)` without clobbering explicitly live registers.
3479+ fn emit_stack_store_with_avoid(&mut self, rs2: &str, offset: usize, avoid: &[&str]) {
3480+ self.emit_stack_access_with_avoid("sd", rs2, offset, avoid);
34503481 }
34513482
34523483 /// Emit `sb rs2, offset(sp)` through the centralized stack-offset gate.
@@ -3455,11 +3486,18 @@ impl CodeGenerator {
34553486 }
34563487
34573488 fn emit_stack_access(&mut self, opcode: &str, register: &str, offset: usize) {
3489+ self.emit_stack_access_with_avoid(opcode, register, offset, &[]);
3490+ }
3491+
3492+ fn emit_stack_access_with_avoid(&mut self, opcode: &str, register: &str, offset: usize, avoid: &[&str]) {
34583493 let offset = i64::try_from(offset).expect("stack offset should fit in i64");
34593494 if small_signed_immediate(offset) {
34603495 self.emit(format!("{} {}, {}(sp)", opcode, register, offset));
34613496 } else {
3462- let scratch = scratch_register_avoiding(&[register]);
3497+ let mut live_registers = Vec::with_capacity(1 + avoid.len());
3498+ live_registers.push(register);
3499+ live_registers.extend_from_slice(avoid);
3500+ let scratch = scratch_register_avoiding(&live_registers);
34633501 self.emit(format!("li {}, {}", scratch, offset));
34643502 self.emit(format!("add {}, sp, {}", scratch, scratch));
34653503 self.emit(format!("{} {}, 0({})", opcode, register, scratch));
@@ -3587,7 +3625,7 @@ impl CodeGenerator {
35873625 body.read_refs.iter().enumerate().map(|(index, pattern)| (pattern.binding.as_str(), index)).collect::<HashMap<_, _>>();
35883626 let mut read_ref_param_index = 0usize;
35893627 for param in params {
3590- if param.source == ParamSource::Output {
3628+ if matches!( param.source, ParamSource::Output | ParamSource::LockArgs) {
35913629 continue;
35923630 }
35933631 if !self.param_is_runtime_bound(param) {
@@ -5646,7 +5684,7 @@ impl CodeGenerator {
56465684 self.emit_loaded_field_equals_expected(size_offset, buffer_offset, layout, expected, context);
56475685 return true;
56485686 }
5649- let Some(width) = layout_fixed_byte_width(layout) else {
5687+ let Some(width) = layout_fixed_byte_width(layout).or_else(|| self.fixed_named_type_width(&layout.ty)) else {
56505688 return false;
56515689 };
56525690 let Some(source) = self.expected_fixed_byte_source(expected, width) else {
@@ -6356,7 +6394,7 @@ impl CodeGenerator {
63566394 }
63576395 pattern.fields.iter().all(|(field, value)| {
63586396 self.type_layouts.get(&pattern.ty).and_then(|layouts| layouts.get(field)).is_some_and(|layout| {
6359- if let Some(width) = layout_fixed_byte_width(layout) {
6397+ if let Some(width) = layout_fixed_byte_width(layout).or_else(|| self.fixed_named_type_width(&layout.ty)) {
63606398 self.is_prelude_available_fixed_value(value, width)
63616399 } else {
63626400 self.can_verify_dynamic_create_output_field_value(value, layout)
@@ -6405,7 +6443,7 @@ impl CodeGenerator {
64056443 let Some(layout) = self.type_layouts.get(&pattern.ty).and_then(|fields| fields.get(field)).cloned() else {
64066444 continue;
64076445 };
6408- if layout_fixed_byte_width(&layout).is_some() {
6446+ if layout_fixed_byte_width(&layout).or_else(|| self.fixed_named_type_width(&layout.ty)). is_some() {
64096447 if is_fixed_type {
64106448 self.emit_loaded_field_bytes_equals_expected(
64116449 size_offset,
@@ -6466,7 +6504,7 @@ impl CodeGenerator {
64666504 field_count: usize,
64676505 expected: &IrOperand,
64686506 ) -> bool {
6469- let Some(width) = layout_fixed_byte_width(layout) else {
6507+ let Some(width) = layout_fixed_byte_width(layout).or_else(|| self.fixed_named_type_width(&layout.ty)) else {
64706508 return false;
64716509 };
64726510 let output_start_offset = self.runtime_expr_temp_offset(0).expect("runtime temp slot 0");
@@ -9730,7 +9768,6 @@ impl CodeGenerator {
97309768 return Ok(());
97319769 };
97329770 let ipc_buffer_offset = self.runtime_scratch_buffer_offset();
9733- let ipc_size_offset = self.runtime_scratch_size_offset();
97349771 self.emit("# cellscript abi: NovaSeal BIP340 verifier IPC envelope via VM2 pipe/spawn/wait");
97359772 let pipe_ok = self.fresh_label("novaseal_bip340_pipe_ok");
97369773 self.emit("call __ckb_pipe");
@@ -9778,18 +9815,17 @@ impl CodeGenerator {
97789815 self.emit_fail(CellScriptRuntimeError::Bip340SpawnFailed);
97799816 self.emit_label(&spawn_ok);
97809817 self.emit_stack_store("a1", child_pid_offset);
9781- self.emit("# cellscript abi: BIP340 IPC write canonical 144-byte envelope");
9782- self.emit("li t0, 144");
9783- self.emit_stack_store("t0", ipc_size_offset);
9784- self.emit_stack_load("a0", write_fd_offset);
9785- self.emit_sp_addi("a1", ipc_buffer_offset);
9786- self.emit_sp_addi("a2", ipc_size_offset);
9787- self.emit(format!("li a7, {}", ckb_abi::syscall::WRITE));
9788- self.emit("ecall");
9789- let write_ok = self.fresh_label("novaseal_bip340_write_ok");
9790- self.emit(format!("beqz a0, {}", write_ok));
9791- self.emit_fail(CellScriptRuntimeError::Bip340MessageWriteFailed);
9792- self.emit_label(&write_ok);
9818+ self.emit("# cellscript abi: BIP340 IPC write canonical 18-word little-endian envelope");
9819+ for word_index in 0..18 {
9820+ self.emit(format!("# cellscript abi: novaseal bip340 ipc word {}", word_index));
9821+ self.emit_stack_load("a0", write_fd_offset);
9822+ self.emit_stack_load("a1", ipc_buffer_offset + word_index * 8);
9823+ self.emit("call __ckb_pipe_write");
9824+ let write_ok = self.fresh_label("novaseal_bip340_write_ok");
9825+ self.emit(format!("beqz a0, {}", write_ok));
9826+ self.emit_fail(CellScriptRuntimeError::Bip340MessageWriteFailed);
9827+ self.emit_label(&write_ok);
9828+ }
97939829 self.emit_stack_load("a0", write_fd_offset);
97949830 self.emit("call __ckb_close");
97959831 let close_ok = self.fresh_label("novaseal_bip340_close_ok");
0 commit comments