Skip to content

Commit 911f093

Browse files
committed
fix: Correct segment splitting for single-block async functions with await
The split_single_block function was incorrectly assigning the original Return terminator to ALL segments. This caused segment_await_map in build_states to fail to identify await segments (it looks for Branch terminators), resulting in: - Await states being treated as Return states - Return value computation included in wrong state - Cranelift panic with invalid value reference (v4294967295) The fix ensures: - Segments BEFORE an await get a Branch terminator (signaling await point) - Only the FINAL segment gets the original Return terminator Test results: - add_to_sum(50) = 1375 ✓ (sum(1..50) + 100 = 1275 + 100) - add_to_sum(10) = 155 ✓ (sum(1..10) + 100 = 55 + 100) - All 104 embed tests pass - All 41 async tests pass
1 parent 44944c8 commit 911f093

2 files changed

Lines changed: 43 additions & 21 deletions

File tree

crates/compiler/src/async_support.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,13 @@ impl AsyncCompiler {
786786
}
787787

788788
/// Optimized path for single-block functions (most common case)
789+
///
790+
/// For single-block async functions with await points:
791+
/// - Segments BEFORE an await get a Branch terminator (to signal await point)
792+
/// - The FINAL segment gets the original terminator (usually Return)
793+
///
794+
/// This ensures that `segment_await_map` in `build_states` correctly identifies
795+
/// which segments end with await, allowing proper AsyncTerminator::Await creation.
789796
fn split_single_block(
790797
&self,
791798
func: &HirFunction,
@@ -796,33 +803,49 @@ impl AsyncCompiler {
796803
if let Some(entry_block) = func.blocks.get(&func.entry_block) {
797804
log::trace!("[ASYNC] split_single_block: entry block terminator = {:?}", entry_block.terminator);
798805
log::trace!("[ASYNC] split_single_block: entry block has {} instructions", entry_block.instructions.len());
799-
let mut current_segment = CodeSegment {
800-
instructions: Vec::new(),
801-
terminator: entry_block.terminator.clone(),
802-
resolved_targets: None, // Single block has no branches to other segments
803-
};
806+
eprintln!("[DEBUG] split_single_block: entry block has {} instructions, {} await_points",
807+
entry_block.instructions.len(), await_points.len());
808+
for ap in await_points {
809+
eprintln!("[DEBUG] await_point at instruction {}", ap.instruction_index);
810+
}
811+
812+
let mut current_instructions = Vec::new();
804813

805814
for (i, inst) in entry_block.instructions.iter().enumerate() {
806815
// Check if this is an await point
807-
if await_points.iter().any(|ap| ap.instruction_index == i) {
808-
// End current segment and start new one
809-
if !current_segment.instructions.is_empty() {
810-
segments.push(current_segment);
811-
current_segment = CodeSegment {
812-
instructions: Vec::new(),
813-
terminator: entry_block.terminator.clone(),
814-
resolved_targets: None,
815-
};
816-
}
816+
let is_await_point = await_points.iter().any(|ap| ap.instruction_index == i);
817+
818+
if is_await_point {
819+
// Push segment before await with a Branch terminator
820+
// The Branch terminator signals to build_states that this segment has an await
821+
eprintln!("[DEBUG] split_single_block: instruction {} is await point, creating segment with {} instructions",
822+
i, current_instructions.len());
823+
segments.push(CodeSegment {
824+
instructions: current_instructions,
825+
terminator: HirTerminator::Branch { target: HirId::new() }, // Dummy target
826+
resolved_targets: None,
827+
});
828+
current_instructions = Vec::new();
829+
// Skip the await instruction itself (it's handled by the state machine)
817830
} else {
818-
current_segment.instructions.push(inst.clone());
831+
current_instructions.push(inst.clone());
819832
}
820833
}
821834

822-
// Add final segment
823-
if !current_segment.instructions.is_empty() || segments.is_empty() {
824-
segments.push(current_segment);
825-
}
835+
// Add final segment with the original terminator (usually Return)
836+
eprintln!("[DEBUG] split_single_block: final segment has {} instructions with original terminator",
837+
current_instructions.len());
838+
segments.push(CodeSegment {
839+
instructions: current_instructions,
840+
terminator: entry_block.terminator.clone(),
841+
resolved_targets: None,
842+
});
843+
}
844+
845+
eprintln!("[DEBUG] split_single_block: created {} segments total", segments.len());
846+
for (i, seg) in segments.iter().enumerate() {
847+
eprintln!("[DEBUG] segment[{}]: {} instructions, terminator={:?}",
848+
i, seg.instructions.len(), seg.terminator);
826849
}
827850

828851
Ok(segments)

crates/zyntax_embed/tests/async_runtime_tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1799,7 +1799,6 @@ async fn sum_with_multiplier(start: i32, end: i32, multiplier: i32) i32 {
17991799
}
18001800

18011801
#[test]
1802-
#[ignore = "Await-then-compute pattern requires storing intermediate results to captures"]
18031802
fn test_execute_async_await_long_running_process() {
18041803
// Test an async function that awaits another async function which is
18051804
// a long-running process (computes a large sum iteratively)

0 commit comments

Comments
 (0)