|
1 | 1 | //! Guarded loop versions for counted Array and Array-subclass iteration. |
2 | 2 | //! |
3 | 3 | //! A one-time runtime admission publishes scalar layout facts. The fast copy |
4 | | -//! reloads the receiver root at iteration entry, compares compact header words, |
5 | | -//! and performs the indexed load directly. Any failed proof resumes the |
6 | | -//! unchanged generic loop at the current counter. |
| 4 | +//! is entered only after its emitted blocks are proven call-free, so its |
| 5 | +//! preheader-cached receiver and storage bases stay valid for the whole copy. |
| 6 | +//! Failed admission runs the unchanged generic loop from the current counter. |
7 | 7 |
|
8 | 8 | use anyhow::Result; |
9 | 9 | use perry_hir::{CompareOp, Expr, Stmt, UpdateOp}; |
10 | 10 |
|
11 | 11 | use crate::expr::{FnCtx, StablePackedLoopFact, StablePackedNumericAccess}; |
12 | 12 | use crate::native_value::{BoundsState, BufferAccessMode, LoweredValue, MaterializationReason}; |
13 | | -use crate::types::{DOUBLE, I1, I32, I64, I8, PTR}; |
| 13 | +use crate::types::{DOUBLE, I1, I32, I64, PTR}; |
14 | 14 |
|
15 | 15 | #[derive(Clone, Copy)] |
16 | 16 | enum LoopBound { |
@@ -291,88 +291,6 @@ fn record_artifacts(ctx: &mut FnCtx<'_>, array_id: u32, receiver: &str) { |
291 | 291 | ); |
292 | 292 | } |
293 | 293 |
|
294 | | -pub(super) fn emit_iteration_guard(ctx: &mut FnCtx<'_>) -> Result<bool> { |
295 | | - let Some(fact) = ctx.stable_packed_loop_facts.last().cloned() else { |
296 | | - return Ok(false); |
297 | | - }; |
298 | | - if fact.preheader_stable { |
299 | | - return Ok(true); |
300 | | - } |
301 | | - let receiver = crate::expr::lower_expr(ctx, &Expr::LocalGet(fact.array_local_id))?; |
302 | | - let bits = ctx.block().bitcast_double_to_i64(&receiver); |
303 | | - let raw = ctx.block().and(I64, &bits, crate::nanbox::POINTER_MASK_I64); |
304 | | - let tag = ctx.block().lshr(I64, &bits, "48"); |
305 | | - let is_pointer = ctx.block().icmp_eq(I64, &tag, "32765"); |
306 | | - let floor = |
307 | | - crate::target_layout::heap_addr_lower_bound_inclusive(ctx.target_triple).to_string(); |
308 | | - let ceiling = |
309 | | - crate::target_layout::heap_addr_upper_bound_exclusive(ctx.target_triple).to_string(); |
310 | | - let above = ctx.block().icmp_uge(I64, &raw, &floor); |
311 | | - let below = ctx.block().icmp_ult(I64, &raw, &ceiling); |
312 | | - let in_heap = ctx.block().and(I1, &above, &below); |
313 | | - let safe = ctx.block().and(I1, &is_pointer, &in_heap); |
314 | | - |
315 | | - let deref_idx = ctx.new_block("stable_packed.iteration.deref"); |
316 | | - let plain_idx = ctx.new_block("stable_packed.iteration.plain"); |
317 | | - let object_idx = ctx.new_block("stable_packed.iteration.object"); |
318 | | - let fast_idx = ctx.new_block("stable_packed.iteration.fast"); |
319 | | - let deref_label = ctx.block_label(deref_idx); |
320 | | - let plain_label = ctx.block_label(plain_idx); |
321 | | - let object_label = ctx.block_label(object_idx); |
322 | | - let fast_label = ctx.block_label(fast_idx); |
323 | | - ctx.block() |
324 | | - .cond_br(&safe, &deref_label, &fact.side_exit_label); |
325 | | - |
326 | | - ctx.current_block = deref_idx; |
327 | | - let gc_addr = ctx.block().sub(I64, &raw, "8"); |
328 | | - let gc_ptr = ctx.block().inttoptr(I64, &gc_addr); |
329 | | - let live_gc = ctx.block().load_aligned(I64, &gc_ptr, 8); |
330 | | - let receiver_ptr = ctx.block().inttoptr(I64, &raw); |
331 | | - let live_header = ctx.block().load(I64, &receiver_ptr); |
332 | | - let expected_gc = descriptor_word(ctx, &fact.descriptor, 1); |
333 | | - let expected_header = descriptor_word(ctx, &fact.descriptor, 2); |
334 | | - let gc_ok = ctx.block().icmp_eq(I64, &live_gc, &expected_gc); |
335 | | - let header_ok = ctx.block().icmp_eq(I64, &live_header, &expected_header); |
336 | | - let header_words_ok = ctx.block().and(I1, &gc_ok, &header_ok); |
337 | | - let kind = descriptor_word(ctx, &fact.descriptor, 0); |
338 | | - let is_plain = ctx.block().icmp_eq(I64, &kind, "1"); |
339 | | - let header_and_plain = ctx.block().and(I1, &header_words_ok, &is_plain); |
340 | | - ctx.block() |
341 | | - .cond_br(&header_and_plain, &plain_label, &object_label); |
342 | | - |
343 | | - ctx.current_block = plain_idx; |
344 | | - let invalidated = ctx |
345 | | - .block() |
346 | | - .load_volatile(I8, "@PERRY_ARRAY_INDEX_FAST_PATH_INVALIDATED"); |
347 | | - let prototype_ok = ctx.block().icmp_eq(I8, &invalidated, "0"); |
348 | | - ctx.block() |
349 | | - .cond_br(&prototype_ok, &fast_label, &fact.side_exit_label); |
350 | | - |
351 | | - ctx.current_block = object_idx; |
352 | | - let is_object = ctx.block().icmp_eq(I64, &kind, "2"); |
353 | | - let mut object_ok = ctx.block().and(I1, &header_words_ok, &is_object); |
354 | | - let length_slot = descriptor_word(ctx, &fact.descriptor, 3); |
355 | | - let object_header_size = |
356 | | - crate::target_layout::object_header_size_bytes(ctx.target_triple).to_string(); |
357 | | - let length_bytes = ctx.block().shl(I64, &length_slot, "3"); |
358 | | - let length_offset = ctx.block().add(I64, &length_bytes, &object_header_size); |
359 | | - let length_addr = ctx.block().add(I64, &raw, &length_offset); |
360 | | - let length_ptr = ctx.block().inttoptr(I64, &length_addr); |
361 | | - let live_length = ctx.block().load(DOUBLE, &length_ptr); |
362 | | - let bound64 = descriptor_word(ctx, &fact.descriptor, 6); |
363 | | - let bound = ctx.block().uitofp(I64, &bound64, DOUBLE); |
364 | | - let length_covers_bound = ctx.block().fcmp("oge", &live_length, &bound); |
365 | | - object_ok = ctx.block().and(I1, &object_ok, &length_covers_bound); |
366 | | - ctx.block() |
367 | | - .cond_br(&object_ok, &fast_label, &fact.side_exit_label); |
368 | | - |
369 | | - ctx.current_block = fast_idx; |
370 | | - if let Some(active) = ctx.stable_packed_loop_facts.last_mut() { |
371 | | - active.live_receiver_handle = Some(raw); |
372 | | - } |
373 | | - Ok(true) |
374 | | -} |
375 | | - |
376 | 294 | pub(crate) fn try_lower_index_get( |
377 | 295 | ctx: &mut FnCtx<'_>, |
378 | 296 | object: &Expr, |
@@ -540,8 +458,7 @@ pub(crate) fn has_numeric_index_fact(ctx: &FnCtx<'_>, expr: &Expr) -> bool { |
540 | 458 | return false; |
541 | 459 | }; |
542 | 460 | ctx.stable_packed_loop_facts.iter().rev().any(|fact| { |
543 | | - fact.preheader_stable |
544 | | - && fact.numeric_elements |
| 461 | + fact.numeric_elements |
545 | 462 | && fact.array_local_id == *array_id |
546 | 463 | && fact.counter_local_id == *counter_id |
547 | 464 | }) |
@@ -708,7 +625,6 @@ pub(super) fn lower( |
708 | 625 | descriptor, |
709 | 626 | live_receiver_handle: Some(fast_raw), |
710 | 627 | numeric_elements: candidate.numeric_elements, |
711 | | - preheader_stable: true, |
712 | 628 | numeric_access, |
713 | 629 | }); |
714 | 630 | super::loops::lower_for_after_init_with_i32_bound( |
|
0 commit comments