compiler: Own published debug-location tables
c69442cd7d6fe1f3f61fecc8f03b447b75b96e8e168b531adb837541a7ed5c41
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 | } |