rv64: separate IL and allocation lifetimes
1fe3802cc7eeba9385d3a982e550c04978eb105fcc2fd119731538ce3655cd79
1 parent
68871bf5
lib/std/arch/rv64/bounds.rad
+73 -27
| 50 | 50 | il::Block { label: "target", params: &targetParams[..], instrs: &mut target[..], locs: &[], preds: &[], loopDepth: 0 }, |
|
| 51 | 51 | il::Block { label: "other", params: &[], instrs: &mut other[..], locs: &[], preds: &[], loopDepth: 0 }, |
|
| 52 | 52 | ]; |
|
| 53 | 53 | let func = il::Fn { name: "p::parameters", params: ¶ms[..], returnType: il::Type::W64, |
|
| 54 | 54 | isExtern: false, isLeaf: true, blocks: &blocks[..] }; |
|
| 55 | - | let mut arena = alloc::new(&mut SCRATCH[..]); |
|
| 55 | + | let mut functionArena = alloc::new(&mut FUNCTION_STORAGE[..]); |
|
| 56 | + | let mut scratchArena = alloc::new(&mut SCRATCH[..]); |
|
| 56 | 57 | let mut code = alloc::new(&mut MEMORY[..]); |
|
| 57 | 58 | let mut gen = generator(&mut code); |
|
| 58 | - | use arena as storage in { |
|
| 59 | - | let view = try! il::published::publish(&func, &storage); |
|
| 59 | + | use functionArena as functionStorage in { |
|
| 60 | + | let view = try! il::published::publish(&func, &functionStorage); |
|
| 60 | 61 | let config = super::targetConfig(); |
|
| 61 | - | let allocation = try! regalloc::allocate(&view, &config, &storage); |
|
| 62 | - | isel::selectFn(&mut gen.e, &allocation, &view); |
|
| 63 | - | assert gen.e.error == nil; |
|
| 64 | - | let expected = try! storage.copy(&gen.e.code[..gen.e.codeLen]); |
|
| 65 | - | set params[0].value.n = 0xffffffff; |
|
| 66 | - | set targetParams[0].value.n = 0xffffffff; |
|
| 67 | - | set args[0] = il::Val::Imm(42); |
|
| 68 | - | set cases[0].value = 99; |
|
| 69 | - | set entry[0] = il::Instr::Unreachable; |
|
| 70 | - | set target[0] = il::Instr::Ret { val: il::Val::Imm(99) }; |
|
| 71 | - | alloc::reset(&mut code); |
|
| 72 | - | set gen = generator(&mut code); |
|
| 73 | - | isel::selectFn(&mut gen.e, &allocation, &view); |
|
| 74 | - | assert gen.e.error == nil and gen.e.codeLen == expected.len; |
|
| 75 | - | for word, i in expected { |
|
| 76 | - | assert gen.e.code[i] == word; |
|
| 62 | + | use scratchArena as scratchStorage in { |
|
| 63 | + | let allocation = try! regalloc::allocate(&view, &config, &scratchStorage); |
|
| 64 | + | isel::selectFn(&mut gen.e, &allocation, &view); |
|
| 65 | + | assert gen.e.error == nil; |
|
| 66 | + | let expected = try! functionStorage.copy(&gen.e.code[..gen.e.codeLen]); |
|
| 67 | + | set params[0].value.n = 0xffffffff; |
|
| 68 | + | set targetParams[0].value.n = 0xffffffff; |
|
| 69 | + | set args[0] = il::Val::Imm(42); |
|
| 70 | + | set cases[0].value = 99; |
|
| 71 | + | set entry[0] = il::Instr::Unreachable; |
|
| 72 | + | set target[0] = il::Instr::Ret { val: il::Val::Imm(99) }; |
|
| 73 | + | alloc::reset(&mut code); |
|
| 74 | + | set gen = generator(&mut code); |
|
| 75 | + | isel::selectFn(&mut gen.e, &allocation, &view); |
|
| 76 | + | assert gen.e.error == nil and gen.e.codeLen == expected.len; |
|
| 77 | + | for word, i in expected { |
|
| 78 | + | assert gen.e.code[i] == word; |
|
| 79 | + | } |
|
| 77 | 80 | } |
|
| 81 | + | alloc::reset(&mut scratchArena); |
|
| 82 | + | assert view.blocks.len == 3; |
|
| 83 | + | assert il::published::successors(&view.blocks[0].instructions[0]).len > 0; |
|
| 78 | 84 | } |
|
| 79 | 85 | } |
|
| 80 | 86 | } |
|
| 81 | 87 | ||
| 82 | 88 | /// Reusable emitter allocation storage. |
|
| 83 | 89 | static MEMORY: [u8; 16777216] = [0; 16777216]; |
|
| 90 | + | /// Published function storage for disjoint-lifetime selection. |
|
| 91 | + | static FUNCTION_STORAGE: [u8; 4096] = [0; 4096]; |
|
| 84 | 92 | ||
| 85 | 93 | /// Checked IL construction reaches instruction selection without a raw IL import. |
|
| 86 | 94 | @test unsafe fn constructedInstructions() throws (testing::TestError) { |
|
| 87 | 95 | let mut arena = alloc::new(&mut SCRATCH[..]); |
|
| 88 | 96 | let mut code = alloc::new(&mut MEMORY[..]); |
| 90 | 98 | use arena as storage in { |
|
| 91 | 99 | try! selectConstructed(&storage, &mut e); |
|
| 92 | 100 | } |
|
| 93 | 101 | } |
|
| 94 | 102 | ||
| 95 | - | /// Construct, allocate, and select a function that returns a constant. |
|
| 96 | - | fn selectConstructed 'view (storage: &Session 'view, e: &mut emit::Emitter) |
|
| 97 | - | throws (alloc::AllocError) |
|
| 98 | - | { |
|
| 103 | + | /// Construct a published function that returns a constant. |
|
| 104 | + | fn constructedFunction 'view (storage: &Session 'view) |
|
| 105 | + | -> il::published::Function 'view throws (alloc::AllocError) { |
|
| 99 | 106 | let copy = try storage.new(il::published::Code 'view::Fixed( |
|
| 100 | 107 | il::Instr::Copy { dst: il::Reg { n: 0 }, val: il::Val::Imm(7) })); |
|
| 101 | 108 | let ret = try storage.new(il::published::Code 'view::Fixed( |
|
| 102 | 109 | il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 0 }) })); |
|
| 103 | 110 | let first = try il::published::buildInstruction(copy, nil, storage); |
| 107 | 114 | let params: &'view [il::Param] = try storage.copy(&noParams[..]); |
|
| 108 | 115 | let noLocations: [il::SrcLoc; 0] = []; |
|
| 109 | 116 | let locations = try storage.copy(&noLocations[..]); |
|
| 110 | 117 | let blocks = try storage.fill(il::published::Block 'view { identity: nil, params, |
|
| 111 | 118 | instructions: &instructions[..], locations, loopDepth: 0 }, 1); |
|
| 112 | - | let function = il::published::Function 'view { name: "p::constructed", isLeaf: true, |
|
| 119 | + | return il::published::Function 'view { name: "p::constructed", isLeaf: true, |
|
| 113 | 120 | params, blocks: &blocks[..] }; |
|
| 114 | - | let config = super::targetConfig(); |
|
| 115 | - | let allocation = try regalloc::allocate(&function, &config, storage); |
|
| 116 | - | isel::selectFn(e, &allocation, &function); |
|
| 121 | + | } |
|
| 122 | + | ||
| 123 | + | /// Check the selected constant materialization and register move. |
|
| 124 | + | fn checkConstructedSelection 'scratch ( |
|
| 125 | + | e: &emit::Emitter, allocation: ®alloc::AllocResult 'scratch |
|
| 126 | + | ) { |
|
| 117 | 127 | assert e.error == nil; |
|
| 118 | 128 | let assigned = allocation.assignments[0] else panic; |
|
| 119 | 129 | let expected = encode::addi(super::SCRATCH1, super::ZERO, 7); |
|
| 120 | 130 | let move = encode::mv(assigned, super::SCRATCH1); |
|
| 121 | 131 | let output: 'output = &*e in { |
| 131 | 141 | } |
|
| 132 | 142 | assert found and moved; |
|
| 133 | 143 | } |
|
| 134 | 144 | } |
|
| 135 | 145 | ||
| 146 | + | /// Construct, allocate, and select a function that returns a constant. |
|
| 147 | + | fn selectConstructed 'view (storage: &Session 'view, e: &mut emit::Emitter) |
|
| 148 | + | throws (alloc::AllocError) |
|
| 149 | + | { |
|
| 150 | + | let function = try constructedFunction(storage); |
|
| 151 | + | let config = super::targetConfig(); |
|
| 152 | + | let allocation = try regalloc::allocate(&function, &config, storage); |
|
| 153 | + | isel::selectFn(e, &allocation, &function); |
|
| 154 | + | checkConstructedSelection(e, &allocation); |
|
| 155 | + | } |
|
| 156 | + | ||
| 157 | + | /// Published IL remains valid after disjoint register-allocation scratch is reclaimed. |
|
| 158 | + | @test unsafe fn disjointPublishedAndScratchSessions() throws (testing::TestError) { |
|
| 159 | + | let mut functionArena = alloc::new(&mut FUNCTION_STORAGE[..]); |
|
| 160 | + | let mut scratchArena = alloc::new(&mut SCRATCH[..]); |
|
| 161 | + | let mut codeArena = alloc::new(&mut MEMORY[..]); |
|
| 162 | + | let mut e = try! emit::emitter(&mut codeArena, false); |
|
| 163 | + | use functionArena as functionStorage in { |
|
| 164 | + | let function = try! constructedFunction(&functionStorage); |
|
| 165 | + | use scratchArena as scratchStorage in { |
|
| 166 | + | let config = super::targetConfig(); |
|
| 167 | + | let allocation = try! regalloc::allocate(&function, &config, &scratchStorage); |
|
| 168 | + | isel::selectFn(&mut e, &allocation, &function); |
|
| 169 | + | checkConstructedSelection(&e, &allocation); |
|
| 170 | + | } |
|
| 171 | + | alloc::reset(&mut scratchArena); |
|
| 172 | + | let copyInstruction = &function.blocks[0].instructions[0]; |
|
| 173 | + | let case il::published::Code::Fixed(copyCode) = |
|
| 174 | + | il::published::code(copyInstruction) else panic; |
|
| 175 | + | let case il::Instr::Copy { dst, val } = copyCode else panic; |
|
| 176 | + | assert dst.n == 0 and val == il::Val::Imm(7); |
|
| 177 | + | let operands = il::published::registers(&function.blocks[0].instructions[1]); |
|
| 178 | + | assert operands.len == 1 and operands[0].n == 0; |
|
| 179 | + | } |
|
| 180 | + | } |
|
| 181 | + | ||
| 136 | 182 | /// Selection owns call arguments and instructions used to size the stack frame. |
|
| 137 | 183 | @test unsafe fn publishedCalls() throws (testing::TestError) { |
|
| 138 | 184 | for indirect in [false, true] { |
|
| 139 | 185 | let params = [il::Param { value: il::Reg { n: 0 }, type: il::Type::W64 }]; |
|
| 140 | 186 | let mut args = [il::Val::Imm(7), il::Val::Reg(il::Reg { n: 1 })]; |
lib/std/arch/rv64/isel.rad
+8 -8
| 299 | 299 | } |
|
| 300 | 300 | return ReserveInfo { size: offset, isDynamic }; |
|
| 301 | 301 | } |
|
| 302 | 302 | ||
| 303 | 303 | /// Select instructions from published IL and its register assignments. |
|
| 304 | - | export fn selectFn 'scratch ( |
|
| 304 | + | export fn selectFn 'input 'scratch ( |
|
| 305 | 305 | e: &mut emit::Emitter, |
|
| 306 | 306 | ralloc: ®alloc::AllocResult 'scratch, |
|
| 307 | - | view: &il::published::Function 'scratch |
|
| 307 | + | view: &il::published::Function 'input |
|
| 308 | 308 | ) { |
|
| 309 | 309 | if e.error <> nil { |
|
| 310 | 310 | return; |
|
| 311 | 311 | } |
|
| 312 | 312 | // Reset block offsets for this function. |
| 380 | 380 | emit::patchLocalBranches(s.e); |
|
| 381 | 381 | } |
|
| 382 | 382 | } |
|
| 383 | 383 | ||
| 384 | 384 | /// Select instructions for a block. |
|
| 385 | - | fn selectBlock 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, frame: &emit::Frame, view: &il::published::Function 'scratch) where 'scratch: 'selection { |
|
| 385 | + | fn selectBlock 'input 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, frame: &emit::Frame, view: &il::published::Function 'input) where 'scratch: 'selection { |
|
| 386 | 386 | // Record block address for branch patching. |
|
| 387 | 387 | emit::recordBlock(s.e, blockIdx); |
|
| 388 | 388 | ||
| 389 | 389 | // Block parameters are handled at jump sites (in `Jmp`/`Br`). |
|
| 390 | 390 | // By the time we enter the block, the arguments have already been |
| 414 | 414 | } |
|
| 415 | 415 | } |
|
| 416 | 416 | } |
|
| 417 | 417 | ||
| 418 | 418 | /// Select instructions for a single IL instruction. |
|
| 419 | - | fn selectInstr 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, instr: &il::published::Code 'scratch, frame: &emit::Frame, view: &il::published::Function 'scratch) where 'scratch: 'selection { |
|
| 419 | + | fn selectInstr 'input 'scratch 'selection (s: &mut Selector 'scratch 'selection, blockIdx: u32, instr: &il::published::Code 'input, frame: &emit::Frame, view: &il::published::Function 'input) where 'scratch: 'selection { |
|
| 420 | 420 | match *instr { |
|
| 421 | 421 | case il::published::Code::Jmp { target, args } => { |
|
| 422 | 422 | selectJump(s, blockIdx, target, view.blocks[target].params, args); |
|
| 423 | 423 | }, |
|
| 424 | 424 | case il::published::Code::Br { thenTarget, thenArgs, elseTarget, elseArgs, .. } => { |
|
| 425 | - | let params: &'scratch [il::Param] = view.blocks[thenTarget].params |
|
| 425 | + | let params: &'input [il::Param] = view.blocks[thenTarget].params |
|
| 426 | 426 | if thenArgs.len > 0 and elseArgs.len == 0 |
|
| 427 | 427 | else view.blocks[elseTarget].params if elseArgs.len > 0 and thenArgs.len == 0 |
|
| 428 | 428 | else &view.params[..0]; |
|
| 429 | 429 | selectBranch(s, blockIdx, instr, params, thenArgs, elseArgs); |
|
| 430 | 430 | }, |
|
| 431 | 431 | case il::published::Code::Switch { val, defaultTarget, defaultArgs, cases } => { |
|
| 432 | 432 | let rs1 = resolveVal(s, super::SCRATCH1, val); |
|
| 433 | 433 | for c in cases { |
|
| 434 | - | let params: &'scratch [il::Param] = view.blocks[c.target].params |
|
| 434 | + | let params: &'input [il::Param] = view.blocks[c.target].params |
|
| 435 | 435 | if c.args.len > 0 else &view.params[..0]; |
|
| 436 | 436 | selectSwitchCase(s, rs1, c.value, c.target, params, c.args); |
|
| 437 | 437 | } |
|
| 438 | 438 | // Fall through to default. |
|
| 439 | 439 | emitBlockArgs(s, view.blocks[defaultTarget].params, defaultArgs); |
| 489 | 489 | emit::recordBranch(s.e, target, emit::BranchKind::Jump); |
|
| 490 | 490 | } |
|
| 491 | 491 | } |
|
| 492 | 492 | ||
| 493 | 493 | /// Select a conditional branch with parameters for its argument-bearing edge. |
|
| 494 | - | fn selectBranch 'scratch 'selection ( |
|
| 494 | + | fn selectBranch 'input 'scratch 'selection ( |
|
| 495 | 495 | s: &mut Selector 'scratch 'selection, |
|
| 496 | 496 | blockIdx: u32, |
|
| 497 | - | instr: &il::published::Code 'scratch, |
|
| 497 | + | instr: &il::published::Code 'input, |
|
| 498 | 498 | params: &[il::Param], |
|
| 499 | 499 | thenArgs: &[il::Val], |
|
| 500 | 500 | elseArgs: &[il::Val] |
|
| 501 | 501 | ) where 'scratch: 'selection { |
|
| 502 | 502 | let case il::published::Code::Br { op, typ, a, b, thenTarget, elseTarget, .. } = *instr |