compiler: Scan stack reservations through checked instruction slices

f28b07519331e1ca6603e85eda11d3eb6165183277d052eb3d8d42ffb6e9b389
Alexis Sellier committed ago 1 parent 80edc705
lib/std/arch/rv64/isel.rad +28 -28
270 270
    size: i32,
271 271
    /// Whether any dynamic-sized reserves exist.
272 272
    isDynamic: bool,
273 273
}
274 274
275 -
/// Pre-scan all blocks for constant-sized reserve instructions.
275 +
/// Add a block's reserve instructions to the accumulated frame requirements.
276 276
/// Returns the total size needed for all static reserves, respecting alignment.
277 -
unsafe fn computeReserveInfo(func: &il::Fn) -> ?ReserveInfo {
278 -
    let mut offset: i32 = 0;
279 -
    let mut isDynamic = false;
280 -
281 -
    for b in 0..func.blocks.len {
282 -
        let block = &func.blocks[b];
283 -
        for instr in block.instrs {
284 -
            match instr {
285 -
                case il::Instr::Reserve { size, alignment, .. } => {
286 -
                    if let case il::Val::Imm(sz) = size {
287 -
                        if alignment == 0 or (alignment & (alignment - 1)) <> 0 or sz < 0 {
288 -
                            return nil;
289 -
                        }
290 -
                        let aligned = (offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1);
291 -
                        if aligned > 0x7fff0000 or sz as u64 > 0x7fff0000 - aligned {
292 -
                            return nil;
293 -
                        }
294 -
                        set offset = (aligned + sz as u64) as i32;
295 -
                    } else {
296 -
                        set isDynamic = true;
277 +
fn computeReserveInfo(instrs: &[il::Instr], previous: ReserveInfo) -> ?ReserveInfo {
278 +
    let mut offset = previous.size;
279 +
    let mut isDynamic = previous.isDynamic;
280 +
    for instr in instrs {
281 +
        match instr {
282 +
            case il::Instr::Reserve { size, alignment, .. } => {
283 +
                if let case il::Val::Imm(sz) = size {
284 +
                    if alignment == 0 or (alignment & (alignment - 1)) <> 0 or sz < 0 {
285 +
                        return nil;
297 286
                    }
298 -
                },
299 -
                else => {},
300 -
            }
287 +
                    let aligned = (offset as u64 + alignment as u64 - 1) & ~(alignment as u64 - 1);
288 +
                    if aligned > 0x7fff0000 or sz as u64 > 0x7fff0000 - aligned {
289 +
                        return nil;
290 +
                    }
291 +
                    set offset = (aligned + sz as u64) as i32;
292 +
                } else {
293 +
                    set isDynamic = true;
294 +
                }
295 +
            },
296 +
            else => {},
301 297
        }
302 298
    }
303 299
    return ReserveInfo { size: offset, isDynamic };
304 300
}
305 301
313 309
        return;
314 310
    }
315 311
    // Reset block offsets for this function.
316 312
    labels::resetBlocks(&mut e.labels);
317 313
    // Pre-scan for constant-sized reserves to promote to fixed frame slots.
318 -
    let reserveInfo = computeReserveInfo(func) else {
319 -
        set e.error = super::Error::Capacity;
320 -
        return;
321 -
    };
314 +
    let mut reserveInfo = ReserveInfo { size: 0, isDynamic: false };
315 +
    for block in func.blocks {
316 +
        let next = computeReserveInfo(&block.instrs[..], reserveInfo) else {
317 +
            set e.error = super::Error::Capacity;
318 +
            return;
319 +
        };
320 +
        set reserveInfo = next;
321 +
    }
322 322
    if reserveInfo.size as u64 + ralloc.spill.frameSize as u64 > 0x7fffff00 {
323 323
        set e.error = super::Error::Capacity; return;
324 324
    }
325 325
    let isLeaf = func.isLeaf;
326 326
    // Compute frame layout from spill slots, reserve slots, and used callee-saved registers.