compiler: Select jumps in checked code

003268b350887c6baea48e017f5469817adf657083059f8f8f4aed711516c4cd
Alexis Sellier committed ago 1 parent d266f79a
lib/std/arch/rv64/bounds.rad +40 -0
250 250
            }
251 251
        }
252 252
    }
253 253
}
254 254
255 +
/// Jumps preserve argument moves and capacity checks in both block layouts.
256 +
@test unsafe fn jumpSelectionCapacity() throws (testing::TestError) {
257 +
    for target in [1 as u32, 2] {
258 +
        for count in 0..3 {
259 +
            let params = [
260 +
                il::Param { value: il::Reg { n: 0 }, type: il::Type::W64 },
261 +
                il::Param { value: il::Reg { n: 1 }, type: il::Type::W64 },
262 +
            ];
263 +
            let targetParams = [
264 +
                il::Param { value: il::Reg { n: 2 }, type: il::Type::W64 },
265 +
                il::Param { value: il::Reg { n: 3 }, type: il::Type::W64 },
266 +
            ];
267 +
            let mut args = [il::Val::Reg(il::Reg { n: 1 }), il::Val::Reg(il::Reg { n: 0 })];
268 +
            let mut entry = [il::Instr::Jmp { target, args: &mut args[..count] }];
269 +
            let mut body = [il::Instr::Ret {
270 +
                val: il::Val::Reg(il::Reg { n: count + 1 }) if count > 0 else il::Val::Imm(7),
271 +
            }];
272 +
            let mut unused = [il::Instr::Ret { val: il::Val::Imm(0) }];
273 +
            let destination = il::Block {
274 +
                label: "destination", params: &targetParams[..count], instrs: &mut body[..],
275 +
                locs: &[], preds: &[0], loopDepth: 0,
276 +
            };
277 +
            let other = il::Block {
278 +
                label: "other", params: &[], instrs: &mut unused[..],
279 +
                locs: &[], preds: &[], loopDepth: 0,
280 +
            };
281 +
            let blocks = [
282 +
                il::Block { label: "entry", params: &[], instrs: &mut entry[..], locs: &[], preds: &[], loopDepth: 0 },
283 +
                destination if target == 1 else other,
284 +
                other if target == 1 else destination,
285 +
            ];
286 +
            let func = il::Fn {
287 +
                name: "p::jump", params: &params[..], returnType: il::Type::W64,
288 +
                isExtern: false, isLeaf: true, blocks: &blocks[..],
289 +
            };
290 +
            try checkFunctionCapacity(&func, nil);
291 +
        }
292 +
    }
293 +
}
294 +
255 295
/// Conditional edges preserve capacity checks for each comparison and layout.
256 296
@test unsafe fn branchSelectionCapacity() throws (testing::TestError) {
257 297
    for op in [il::CmpOp::Eq, il::CmpOp::Ne, il::CmpOp::Slt, il::CmpOp::Ult] {
258 298
        for typ in [il::Type::W8, il::Type::W16, il::Type::W32, il::Type::W64] {
259 299
            for layout in 0..2 {
lib/std/arch/rv64/isel.rad +19 -14
414 414
415 415
/// Select instructions for a single IL instruction.
416 416
unsafe fn selectInstr 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, instr: &il::Instr, frame: &emit::Frame, func: &il::Fn) where 'scratch: 'selection {
417 417
    match *instr {
418 418
        case il::Instr::Jmp { target, args } => {
419 -
            // Move arguments to target block's parameter registers.
420 -
            emitBlockArgs(s, func.blocks[target].params, args);
421 -
            // Skip branch if target is the next block (fallthrough).
422 -
            if target <> blockIdx + 1 {
423 -
                emit::recordBranch(s.e, target, emit::BranchKind::Jump);
424 -
            }
419 +
            selectJump(s, blockIdx, target, func.blocks[target].params, args);
425 420
        },
426 421
        case il::Instr::Br { thenTarget, thenArgs, elseTarget, elseArgs, .. } => {
427 422
            let params: *unsafe [il::Param] = func.blocks[thenTarget].params
428 423
                if thenArgs.len > 0 and elseArgs.len == 0
429 424
                else func.blocks[elseTarget].params if elseArgs.len > 0 and thenArgs.len == 0
460 455
        case il::Instr::Call { dst, func, args, .. } => selectCall(s, &func, args, dst),
461 456
        else => selectFixedInstr(s, blockIdx, instr, frame),
462 457
    }
463 458
}
464 459
460 +
/// Move edge arguments and emit a jump unless its destination follows the block.
461 +
fn selectJump 'scratch 'selection (
462 +
    s: &mut Selector 'scratch 'selection,
463 +
    blockIdx: u32,
464 +
    target: u32,
465 +
    params: &[il::Param],
466 +
    args: &[il::Val]
467 +
) where 'scratch: 'selection {
468 +
    // Move arguments to target block's parameter registers.
469 +
    emitBlockArgs(s, params, args);
470 +
    // Skip branch if target is the next block (fallthrough).
471 +
    if target <> blockIdx + 1 {
472 +
        emit::recordBranch(s.e, target, emit::BranchKind::Jump);
473 +
    }
474 +
}
475 +
465 476
/// Select a conditional branch with parameters for its argument-bearing edge.
466 477
fn selectBranch 'scratch 'selection (
467 478
    s: &mut Selector 'scratch 'selection,
468 479
    blockIdx: u32,
469 480
    instr: &il::Instr,
511 522
    // execution fall through.
512 523
    if thenArgs.len > 0 and elseArgs.len > 0 {
513 524
        panic "selectBranch: both `then` and `else` have block arguments";
514 525
    } else if thenArgs.len > 0 {
515 526
        emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
516 -
        emitBlockArgs(s, params, thenArgs);
517 527
        // Skip trailing jump if then is the next block (fallthrough).
518 -
        if thenTarget <> blockIdx + 1 {
519 -
            emit::recordBranch(s.e, thenTarget, emit::BranchKind::Jump);
520 -
        }
528 +
        selectJump(s, blockIdx, thenTarget, params, thenArgs);
521 529
    } else if thenTarget == blockIdx + 1 and elseArgs.len == 0 {
522 530
        // Then is the next block and no else args: invert the
523 531
        // condition to branch to else and fall through to then.
524 532
        emit::recordBranch(s.e, elseTarget, emit::BranchKind::InvertedCond { op, rs1, rs2 });
525 533
    } else {
526 534
        emit::recordBranch(s.e, thenTarget, emit::BranchKind::Cond { op, rs1, rs2 });
527 -
        emitBlockArgs(s, params, elseArgs);
528 535
        // Skip trailing jump if else is the next block (fallthrough).
529 -
        if elseTarget <> blockIdx + 1 {
530 -
            emit::recordBranch(s.e, elseTarget, emit::BranchKind::Jump);
531 -
        }
536 +
        selectJump(s, blockIdx, elseTarget, params, elseArgs);
532 537
    }
533 538
}
534 539
535 540
/// Select a call from its target, borrowed arguments, and optional result.
536 541
fn selectCall 'scratch 'selection (