lib/std/lang/il/binary/records.rad 8.3 KiB raw
1
//! Binary data, function and block records built directly from shared RIL.
2
//!
3
//! Data: symbol:u32, size:u32, alignment:u32, flags:u8 (readonly bit 0,
4
//! zero-init bit 1), values:list. Each value is count:u32 then item tag:u8:
5
//! Val=0 (type:u8, bits:u64), Sym=1 (symbol:u32), Fn=2 (symbol:u32),
6
//! Str=3 (bytes:string), Undef=4 (one zero/padding byte per repetition).
7
//! Function: symbol:u32, return type:u8, extern:bool, params:list, blocks:list.
8
//! Parameter: register:u32, type:u8. Block: label:string, loopDepth:u32,
9
//! params:list, instructions:list. Debug locations are deliberately omitted.
10
11
use std::lang::il;
12
use super::instructions;
13
use super::structure;
14
15
/// Serialize typed parameters with their original register identifiers.
16
fn putParams(w: *mut super::Writer, params: *[il::Param]) throws (super::Error) {
17
    try super::put32(w, params.len);
18
    for param in params {
19
        try super::put32(w, param.value.n);
20
        try instructions::putType(w, param.type);
21
    }
22
}
23
24
/// Decode parameters into caller-owned storage.
25
fn getParams(r: *mut super::Reader) -> *[il::Param] throws (super::Error) {
26
    let count = try super::count(r, 5);
27
    let params = try super::storage(r.arena, @sizeOf(il::Param), @alignOf(il::Param), count, r.offset) as *mut [il::Param];
28
    for i in 0..count {
29
        let value = try instructions::getReg(r);
30
        let typ = try instructions::getType(r);
31
        set params[i] = il::Param { value, type: typ };
32
    }
33
    return params;
34
}
35
36
/// Serialize one data declaration including zero repetition counts and flags.
37
export fn putData(w: *mut super::Writer, data: *il::Data) throws (super::Error) {
38
    try checkData(data, w.offset);
39
    try super::putSymbol(w, data.name, 1);
40
    try super::put32(w, data.size);
41
    try super::put32(w, data.alignment);
42
    let mut flags: u8 = 0;
43
    if data.readOnly { set flags |= 1; }
44
    if data.isZeroInit { set flags |= 2; }
45
    try super::put8(w, flags);
46
    try super::put32(w, data.values.len);
47
    for value in data.values {
48
        try super::put32(w, value.count);
49
        match value.item {
50
            case il::DataItem::Val { typ, val } => {
51
                try super::put8(w, 0); try instructions::putType(w, typ); try super::put64(w, val as u64);
52
            }
53
            case il::DataItem::Sym(name) => { try super::put8(w, 1); try super::putSymbol(w, name, 1); }
54
            case il::DataItem::Fn(name) => { try super::put8(w, 2); try super::putSymbol(w, name, 2); }
55
            case il::DataItem::Str(bytes) => { try super::put8(w, 3); try super::putString(w, bytes); }
56
            case il::DataItem::Undef => try super::put8(w, 4),
57
        }
58
    }
59
}
60
61
/// Decode one initializer item with its exact explicit discriminant.
62
fn getItem(r: *mut super::Reader) -> il::DataItem throws (super::Error) {
63
    let offset = r.offset;
64
    match try super::get8(r) {
65
        case 0 => {
66
            let typ = try instructions::getType(r);
67
            let val = (try super::get64(r)) as i64;
68
            return il::DataItem::Val { typ, val };
69
        }
70
        case 1 => return il::DataItem::Sym(try super::getSymbol(r, 1)),
71
        case 2 => return il::DataItem::Fn(try super::getSymbol(r, 2)),
72
        case 3 => return il::DataItem::Str(try super::getString(r)),
73
        case 4 => return il::DataItem::Undef,
74
        else => throw super::error(offset, "invalid RIL data item tag"),
75
    }
76
}
77
78
/// Decode and structurally validate a data record before native emission.
79
export fn getData(r: *mut super::Reader) -> il::Data throws (super::Error) {
80
    let offset = r.offset;
81
    let name = try super::declaration(r, 1);
82
    let size = try super::get32(r);
83
    let alignment = try instructions::getAlignment(r);
84
    let flagOffset = r.offset;
85
    let flags = try super::get8(r);
86
    if flags > 3 { throw super::error(flagOffset, "unknown RIL data flags"); }
87
    let count = try super::count(r, 5);
88
    let values = try super::storage(r.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue), count, r.offset) as *mut [il::DataValue];
89
    for i in 0..count {
90
        let repeat = try super::get32(r);
91
        let item = try getItem(r);
92
        set values[i] = il::DataValue { item, count: repeat };
93
    }
94
    let data = il::Data { name, size, alignment, readOnly: (flags & 1) <> 0, isZeroInit: (flags & 2) <> 0, values };
95
    try checkData(&data, offset);
96
    return data;
97
}
98
99
/// Bound initializer byte arithmetic against the declared extent. A zero-init
100
/// flag may not discard nonzero bytes or relocations during image placement.
101
fn checkData(data: *il::Data, offset: u32) throws (super::Error) {
102
    if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 {
103
        throw super::error(offset, "RIL alignment must be a nonzero power of two");
104
    }
105
    if data.readOnly and data.isZeroInit {
106
        throw super::error(offset, "RIL read-only data requires an initializer");
107
    }
108
    let mut size: u64 = 0;
109
    for value in data.values {
110
        let mut width: u32 = 0;
111
        let mut zero = true;
112
        match value.item {
113
            case il::DataItem::Val { typ, val } => {
114
                set width = il::typeSize(typ);
115
                // Only the stored low bytes contribute to narrow initializers.
116
                if width == 8 { set zero = val == 0; }
117
                else { set zero = ((val as u64) & ((1 as u64 << (width as u64 * 8)) - 1)) == 0; }
118
            }
119
            case il::DataItem::Sym(_), il::DataItem::Fn(_) => { set width = 8; set zero = false; }
120
            case il::DataItem::Str(bytes) => {
121
                set width = bytes.len;
122
                for byte in bytes { if byte <> 0 { set zero = false; } }
123
            }
124
            case il::DataItem::Undef => set width = 1,
125
        }
126
        let bytes = width as u64 * value.count as u64;
127
        if bytes > data.size as u64 - size { throw super::error(offset, "RIL initializer exceeds data size"); }
128
        set size += bytes;
129
        if data.isZeroInit and value.count <> 0 and not zero {
130
            throw super::error(offset, "RIL zero-init data contains a nonzero initializer");
131
        }
132
    }
133
    if not data.isZeroInit and size <> data.size as u64 {
134
        throw super::error(offset, "RIL initializer size differs from data size");
135
    }
136
}
137
138
/// Serialize all blocks, including unreferenced empty blocks retained by lowering.
139
export fn putFn(w: *mut super::Writer, func: *il::Fn) throws (super::Error) {
140
    try super::putSymbol(w, func.name, 2);
141
    try instructions::putType(w, func.returnType);
142
    try super::put8(w, 1 if func.isExtern else 0);
143
    try putParams(w, func.params);
144
    try super::put32(w, func.blocks.len);
145
    for block in func.blocks {
146
        try super::putString(w, block.label);
147
        try super::put32(w, block.loopDepth);
148
        try putParams(w, block.params);
149
        try super::put32(w, block.instrs.len);
150
        for instr in block.instrs { try instructions::put(w, instr); }
151
    }
152
}
153
154
/// Decode a function and reconstruct allocator metadata only after all block
155
/// indices are available. Temporary locations identify malformed instructions.
156
export fn getFn(r: *mut super::Reader) -> il::Fn throws (super::Error) {
157
    let offset = r.offset;
158
    let name = try super::declaration(r, 2);
159
    let returnType = try instructions::getType(r);
160
    let isExtern = try super::getBool(r);
161
    let params = try getParams(r);
162
    let count = try super::count(r, 16);
163
    let blocks = try super::storage(r.arena, @sizeOf(il::Block), @alignOf(il::Block), count, r.offset) as *mut [il::Block];
164
    for i in 0..count {
165
        let labelOffset = r.offset;
166
        let bytes = try super::getString(r);
167
        let label = try super::intern(r, bytes, labelOffset);
168
        let loopDepth = try super::get32(r);
169
        let blockParams = try getParams(r);
170
        let instrCount = try super::count(r, 1);
171
        let instrs = try super::storage(r.arena, @sizeOf(il::Instr), @alignOf(il::Instr), instrCount, r.offset) as *mut [il::Instr];
172
        let locs = try super::storage(r.arena, @sizeOf(il::SrcLoc), @alignOf(il::SrcLoc), instrCount, r.offset) as *mut [il::SrcLoc];
173
        for j in 0..instrCount {
174
            set locs[j] = il::SrcLoc { moduleId: 0, offset: r.offset };
175
            set instrs[j] = try instructions::get(r);
176
        }
177
        set blocks[i] = il::Block { label, params: blockParams, instrs, locs, preds: &[], loopDepth };
178
    }
179
    let mut func = il::Fn { name, params, returnType, isExtern, isLeaf: true, blocks };
180
    try structure::reconstruct(r, &mut func, blocks, offset);
181
    return func;
182
}