lib/std/lang/il/binary/structure.rad 7.6 KiB raw
1
//! Structural reconstruction for binary RIL using existing allocator indexes.
2
//!
3
//! Register definitions use the existing byte-keyed dictionary. Numeric keys
4
//! have stable arena storage. Branch edges rebuild unique predecessors.
5
//! These checks establish structure, not dominance, provenance, or type safety.
6
7
use std::collections::dict;
8
use std::lang::il;
9
use std::lang::gen::regalloc::liveness;
10
11
/// State for counting and then materializing unique predecessor lists.
12
record Edges: Copy {
13
    /// Decoder-owned blocks.
14
    blocks: *mut [il::Block],
15
    /// Incoming counts, or absolute insertion cursors during the second pass.
16
    counts: *mut [u32],
17
    /// Last source block seen for each target; duplicate edges are ignored.
18
    last: *mut [u32],
19
    /// Contiguous mutable storage backing all published predecessor slices.
20
    predecessors: *mut [u32],
21
    /// Whether predecessor storage has been allocated and should be filled.
22
    fill: bool,
23
}
24
25
/// Defined-register index and the result of inspecting an instruction's operands.
26
record Registers: Copy {
27
    /// Dictionary whose keys are the binary u32 register indexes.
28
    definitions: dict::Dict,
29
    /// Every operand seen so far has a corresponding definition.
30
    valid: bool,
31
}
32
33
/// Insert a unique definition within the shared allocator's register capacity.
34
fn define(map: *mut dict::Dict, numbers: *mut [u32], index: u32, reg: il::Reg, offset: u32) throws (super::Error) {
35
    if reg.n >= liveness::MAX_SSA_REGS { throw super::error(offset, "RIL register exceeds allocator capacity"); }
36
    set numbers[index] = reg.n;
37
    let key = @sliceOf(&numbers[index] as *u8, @sizeOf(u32));
38
    if dict::get(map, key) <> nil { throw super::error(offset, "duplicate RIL register definition"); }
39
    dict::insert(map, key, index as i32);
40
}
41
42
/// Check one operand through the shared IL register visitor.
43
fn operand(reg: il::Reg, context: *mut opaque) {
44
    let state = context as *mut Registers;
45
    let number = reg.n;
46
    let key = @sliceOf(&number as *u8, @sizeOf(u32));
47
    if dict::get(&state.definitions, key) == nil { set state.valid = false; }
48
}
49
50
/// Identify the terminators used by existing lowering and instruction selection.
51
fn terminator(value: il::Instr) -> bool {
52
    match value {
53
        case il::Instr::Ret { .. }, il::Instr::Jmp { .. }, il::Instr::Br { .. },
54
             il::Instr::Switch { .. }, il::Instr::Unreachable => return true,
55
        else => return false,
56
    }
57
}
58
59
/// Check a target and its arity, then count or emit one unique predecessor.
60
fn edge(e: *mut Edges, source: u32, target: u32, count: u32, offset: u32) throws (super::Error) {
61
    if target >= e.blocks.len { throw super::error(offset, "RIL block target out of range"); }
62
    let block = &e.blocks[target];
63
    if count <> block.params.len { throw super::error(offset, "RIL branch argument count mismatch"); }
64
    if block.instrs.len == 0 { throw super::error(offset, "RIL branch targets an empty block"); }
65
    if e.last[target] == source { return; }
66
    set e.last[target] = source;
67
    if e.fill { set e.predecessors[e.counts[target]] = source; }
68
    set e.counts[target] += 1;
69
}
70
71
/// Traverse explicit branch fields, including switch default and case edges.
72
fn edges(e: *mut Edges, source: u32, value: il::Instr, offset: u32) throws (super::Error) {
73
    match value {
74
        case il::Instr::Jmp { target, args } => try edge(e, source, target, args.len, offset),
75
        case il::Instr::Br { thenTarget, thenArgs, elseTarget, elseArgs, .. } => {
76
            try edge(e, source, thenTarget, thenArgs.len, offset);
77
            try edge(e, source, elseTarget, elseArgs.len, offset);
78
        }
79
        case il::Instr::Switch { defaultTarget, defaultArgs, cases, .. } => {
80
            try edge(e, source, defaultTarget, defaultArgs.len, offset);
81
            for item in cases { try edge(e, source, item.target, item.args.len, offset); }
82
        }
83
        else => {},
84
    }
85
}
86
87
/// Reconstruct allocator metadata while retaining decoder ownership of blocks.
88
/// Existing register indexes and immutable argument slices are preserved.
89
export fn reconstruct(r: *mut super::Reader, func: *mut il::Fn, blocks: *mut [il::Block], offset: u32) throws (super::Error) {
90
    if func.isExtern {
91
        if blocks.len <> 0 { throw super::error(offset, "extern RIL function has a body"); }
92
    } else if blocks.len == 0 or blocks[0].instrs.len == 0 {
93
        throw super::error(offset, "RIL function has no entry block instructions");
94
    }
95
    let mut names = try super::dictionary(r.arena, blocks.len, offset);
96
    let mut definitions = func.params.len as u64;
97
    for block, i in blocks {
98
        if dict::get(&names, block.label) <> nil { throw super::error(offset, "duplicate RIL block label"); }
99
        dict::insert(&mut names, block.label, i as i32);
100
        set definitions += block.params.len as u64;
101
        for item in block.instrs { if il::instrDst(item) <> nil { set definitions += 1; } }
102
    }
103
    if definitions > liveness::MAX_SSA_REGS as u64 {
104
        throw super::error(offset, "RIL function exceeds allocator register capacity");
105
    }
106
    let numbers = try super::storage(r.arena, @sizeOf(u32), @alignOf(u32), definitions as u32, offset) as *mut [u32];
107
    let mut map = try super::dictionary(r.arena, definitions as u32, offset);
108
    let mut index: u32 = 0;
109
    for param in func.params {
110
        try define(&mut map, numbers, index, param.value, offset);
111
        set index += 1;
112
    }
113
    for block in blocks {
114
        for param in block.params {
115
            try define(&mut map, numbers, index, param.value, offset);
116
            set index += 1;
117
        }
118
        for item, i in block.instrs {
119
            if let dst = il::instrDst(item) {
120
                try define(&mut map, numbers, index, dst, block.locs[i].offset);
121
                set index += 1;
122
            }
123
        }
124
    }
125
    let counts = try super::storage(r.arena, @sizeOf(u32), @alignOf(u32), blocks.len, offset) as *mut [u32];
126
    let last = try super::storage(r.arena, @sizeOf(u32), @alignOf(u32), blocks.len, offset) as *mut [u32];
127
    for i in 0..blocks.len { set counts[i] = 0; set last[i] = 0xFFFFFFFF; }
128
    let mut e = Edges { blocks, counts, last, predecessors: &mut [], fill: false };
129
    let mut registers = Registers { definitions: map, valid: true };
130
    for block, b in blocks {
131
        for item, i in block.instrs {
132
            let pos = block.locs[i].offset;
133
            if terminator(item) and i + 1 <> block.instrs.len { throw super::error(pos, "RIL instruction follows a terminator"); }
134
            if not terminator(item) and i + 1 == block.instrs.len { throw super::error(pos, "RIL block is missing a terminator"); }
135
            if il::isCall(item) { set func.isLeaf = false; }
136
            try edges(&mut e, b, item, pos);
137
            il::forEachReg(item, operand, &mut registers as *mut opaque);
138
            if not registers.valid { throw super::error(pos, "undefined RIL register"); }
139
        }
140
    }
141
    let mut total: u64 = 0;
142
    for count in counts { set total += count as u64; }
143
    if total > 0xFFFFFFFF { throw super::error(offset, "too many RIL control-flow edges"); }
144
    set e.predecessors = try super::storage(r.arena, @sizeOf(u32), @alignOf(u32), total as u32, offset) as *mut [u32];
145
    let mut start: u32 = 0;
146
    for i in 0..blocks.len {
147
        let end = start + counts[i];
148
        set blocks[i].preds = &e.predecessors[start..end];
149
        set counts[i] = start;
150
        set last[i] = 0xFFFFFFFF;
151
        set start = end;
152
    }
153
    set e.fill = true;
154
    for block, b in blocks {
155
        for item, i in block.instrs { try edges(&mut e, b, item, block.locs[i].offset); }
156
    }
157
    // Binary offsets identify decoding errors, not source-language locations.
158
    for i in 0..blocks.len { set blocks[i].locs = &[]; }
159
}