rv64: separate IL and allocation lifetimes

1fe3802cc7eeba9385d3a982e550c04978eb105fcc2fd119731538ce3655cd79
Alexis Sellier committed ago 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: &params[..], 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: &regalloc::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: &regalloc::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