compiler: Own published debug-location tables

c69442cd7d6fe1f3f61fecc8f03b447b75b96e8e168b531adb837541a7ed5c41
Alexis Sellier committed ago 1 parent b6aefd8a
lib/std/arch/rv64/bounds.rad +41 -0
75 75
    }
76 76
}
77 77
78 78
/// Reusable emitter allocation storage.
79 79
static MEMORY: [u8; 16777216] = [0; 16777216];
80 +
81 +
/// Selection retains published locations and consecutive-location deduplication.
82 +
@test unsafe fn publishedLocations() throws (testing::TestError) {
83 +
    // Storage for machine instructions, fixups, and source location entries.
84 +
    static DEBUG_MEMORY: [u8; 33554432] = [0; 33554432];
85 +
    let mut locations = [il::SrcLoc { moduleId: 1, offset: 10 },
86 +
        il::SrcLoc { moduleId: 1, offset: 10 }, il::SrcLoc { moduleId: 2, offset: 20 }];
87 +
    let mut instructions = [
88 +
        il::Instr::Copy { dst: il::Reg { n: 0 }, val: il::Val::Imm(3) },
89 +
        il::Instr::Copy { dst: il::Reg { n: 1 }, val: il::Val::Reg(il::Reg { n: 0 }) },
90 +
        il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 1 }) },
91 +
    ];
92 +
    let blocks = [il::Block { label: "entry", params: &[], instrs: &mut instructions[..],
93 +
        locs: &locations[..], preds: &[], loopDepth: 0 }];
94 +
    let func = il::Fn { name: "p::locations", params: &[], returnType: il::Type::W64,
95 +
        isExtern: false, isLeaf: true, blocks: &blocks[..] };
96 +
    let mut arena = alloc::new(&mut SCRATCH[..]);
97 +
    let mut debugCode = alloc::new(&mut DEBUG_MEMORY[..]);
98 +
    let mut emitter = try emit::emitter(&mut debugCode, true) catch {
99 +
        io::printLn("publishedLocations: emitter allocation");
100 +
        throw testing::TestError::Failed;
101 +
    };
102 +
    use arena as storage in {
103 +
        let view = try! il::published::publish(&func, &storage);
104 +
        let config = super::targetConfig();
105 +
        let allocation = try! regalloc::allocate(&view, &config, &storage);
106 +
        for i in 0..locations.len {
107 +
            set locations[i] = il::SrcLoc { moduleId: 9, offset: 99 };
108 +
        }
109 +
        isel::selectFn(&mut emitter, &allocation, &func, &view);
110 +
        assert emitter.error == nil;
111 +
        let output: 'output = &emitter in {
112 +
            let entries = emit::getDebugEntries(output);
113 +
            assert entries.len == 2;
114 +
            assert entries[0].moduleId == 1 and entries[0].offset == 10;
115 +
            assert entries[1].moduleId == 2 and entries[1].offset == 20;
116 +
            assert entries[0].pc <= entries[1].pc;
117 +
        }
118 +
    }
119 +
}
120 +
80 121
/// Function and liveness test storage.
81 122
static SCRATCH: [u8; 65536] = [0; 65536];
82 123
/// Code output with guard words for instruction selection checks.
83 124
static SELECTION_WORDS: [u32; 130] = [0; 130];
84 125
lib/std/arch/rv64/isel.rad +3 -2
390 390
    // Block parameters are handled at jump sites (in `Jmp`/`Br`).
391 391
    // By the time we enter the block, the arguments have already been
392 392
    // moved to the parameter registers by the predecessor's terminator.
393 393
394 394
    // Process each instruction, auto-committing any pending spill after each.
395 -
    let hasLocs = block.locs.len > 0;
395 +
    let locations = view.blocks[blockIdx].locations;
396 +
    let hasLocs = locations.len > 0;
396 397
    for instr, i in block.instrs {
397 398
        if s.e.error <> nil {
398 399
            return;
399 400
        }
400 401
        // Record debug location before emitting machine instructions.
401 402
        if hasLocs {
402 -
            emit::recordSrcLoc(s.e, block.locs[i]);
403 +
            emit::recordSrcLoc(s.e, locations[i]);
403 404
        }
404 405
        set s.pendingSpill = nil;
405 406
        selectInstr(s, blockIdx, &instr, frame, view);
406 407
407 408
        // Flush the pending spill store, if any.
lib/std/lang/il/published.rad +5 -2
23 23
    identity: *unsafe il::Block,
24 24
    /// Block parameter definitions.
25 25
    params: &'view [il::Param],
26 26
    /// Published instruction descriptors.
27 27
    instructions: &'view [Instruction 'view],
28 +
    /// Source locations in instruction order, or an empty slice without debug data.
29 +
    locations: &'view [il::SrcLoc],
28 30
    /// Loop nesting depth for cost weighting.
29 31
    loopDepth: u32,
30 32
}
31 33
32 34
/// An instruction's immutable register-allocation inputs.
66 68
/// Copy block definitions and each instruction's analysis tables.
67 69
unsafe fn publishBlock 'view (block: &il::Block, storage: &Session 'view)
68 70
    -> Block 'view throws (alloc::AllocError)
69 71
{
70 72
    let params = try storage.copy(block.params);
73 +
    let locations = try storage.copy(block.locs);
71 74
    let instructions: &[il::Instr] = block.instrs;
72 75
    if instructions.len == 0 {
73 76
        let none: [Instruction 'view; 0] = [];
74 77
        return Block 'view { identity: block as *unsafe il::Block, params,
75 -
            instructions: try storage.copy(&none[..]), loopDepth: block.loopDepth };
78 +
            instructions: try storage.copy(&none[..]), locations, loopDepth: block.loopDepth };
76 79
    }
77 80
    let first = try publishInstruction(&instructions[0], storage);
78 81
    let views = try storage.fill(first, instructions.len);
79 82
    for i in 1..instructions.len {
80 83
        set views[i] = try publishInstruction(&instructions[i], storage);
81 84
    }
82 85
    return Block 'view { identity: block as *unsafe il::Block, params,
83 -
        instructions: &views[..], loopDepth: block.loopDepth };
86 +
        instructions: &views[..], locations, loopDepth: block.loopDepth };
84 87
}
85 88
86 89
/// Collect source registers and successor positions for one instruction.
87 90
unsafe fn publishInstruction 'view (source: &il::Instr, storage: &Session 'view)
88 91
    -> Instruction 'view throws (alloc::AllocError)
lib/std/lang/il/publishedTests.rad +4 -1
93 93
}
94 94
95 95
/// Allocation failures leave no borrowed source storage in published results.
96 96
@test unsafe fn testPublicationAllocationFailure() throws (testing::TestError) {
97 97
    let args = [il::Val::Reg(il::Reg { n: 4 })];
98 +
    let locations = [il::SrcLoc { moduleId: 5, offset: 17 }];
98 99
    let mut instructions = [il::Instr::Call { retTy: il::Type::W64, dst: nil,
99 100
        func: il::Val::FnAddr("callee"), args: &args[..] }];
100 101
    let blocks = [il::Block { label: "entry", params: &[], instrs: &mut instructions[..],
101 -
        locs: &[], preds: &[], loopDepth: 0 }];
102 +
        locs: &locations[..], preds: &[], loopDepth: 0 }];
102 103
    let function = il::Fn { name: "allocation", params: &[], returnType: il::Type::W64,
103 104
        isExtern: false, isLeaf: false, blocks: &blocks[..] };
104 105
    let mut succeeded = false;
105 106
    for capacity in 0..513 {
106 107
        static DATA: [u8; 512] = [0; 512];
108 109
        let input: 'input = &function in {
109 110
            use arena as storage in {
110 111
                if let view = try? published::publish(input, &storage) {
111 112
                    let registers = published::registers(&view.blocks[0].instructions[0]);
112 113
                    assert registers.len == 1 and registers[0].n == 4;
114 +
                    assert view.blocks[0].locations.len == 1;
115 +
                    assert view.blocks[0].locations[0].offset == 17;
113 116
                    set succeeded = true;
114 117
                } else {
115 118
                    assert not succeeded;
116 119
                }
117 120
            }