compiler: Select jumps in checked code
003268b350887c6baea48e017f5469817adf657083059f8f8f4aed711516c4cd
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: ¶ms[..], 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 ( |