lib/std/lang/il/binary/instructions.rad 14.7 KiB raw
1
//! Explicit binary RIL instruction tags and operand payloads.
2
//!
3
//! Type tags are byte widths 1/2/4/8. Value tags are Reg=0, Imm=1,
4
//! DataSym=2, FnAddr=3, Undef=4. Instruction tags are Reserve=0, Load=1,
5
//! Sload=2, Store=3, Blit=4, Copy=5, BinOp=6, UnOp=7, Zext=8, Sext=9,
6
//! Call=10, Ret=11, Jmp=12, Br=13, Switch=14, Unreachable=15, Ecall=16,
7
//! Ebreak=17, MemoryFence=18. Payload fields follow the shared IL declaration
8
//! order, except BinOp/UnOp put their operation byte first. Optional operands
9
//! use a canonical 0/1 byte followed by the payload only when present.
10
11
use std::lang::il;
12
13
/// Write one logical word width.
14
export fn putType(w: *mut super::Writer, typ: il::Type) throws (super::Error) {
15
    try super::put8(w, il::typeSize(typ) as u8);
16
}
17
18
/// Read one logical word width, rejecting unknown tags.
19
export fn getType(r: *mut super::Reader) -> il::Type throws (super::Error) {
20
    let offset = r.offset;
21
    match try super::get8(r) {
22
        case 1 => return il::Type::W8,
23
        case 2 => return il::Type::W16,
24
        case 4 => return il::Type::W32,
25
        case 8 => return il::Type::W64,
26
        else => throw super::error(offset, "invalid RIL word width"),
27
    }
28
}
29
30
/// Write a typed value without conflating integers and symbolic addresses.
31
export fn putVal(w: *mut super::Writer, val: il::Val) throws (super::Error) {
32
    match val {
33
        case il::Val::Reg(reg) => { try super::put8(w, 0); try super::put32(w, reg.n); }
34
        case il::Val::Imm(value) => { try super::put8(w, 1); try super::put64(w, value as u64); }
35
        case il::Val::DataSym(name) => { try super::put8(w, 2); try super::putSymbol(w, name, 1); }
36
        case il::Val::FnAddr(name) => { try super::put8(w, 3); try super::putSymbol(w, name, 2); }
37
        case il::Val::Undef => try super::put8(w, 4),
38
    }
39
}
40
41
/// Read a typed value; no raw integer is ever interpreted as a host pointer.
42
export fn getVal(r: *mut super::Reader) -> il::Val throws (super::Error) {
43
    let offset = r.offset;
44
    match try super::get8(r) {
45
        case 0 => return il::Val::Reg(try getReg(r)),
46
        case 1 => return il::Val::Imm((try super::get64(r)) as i64),
47
        case 2 => return il::Val::DataSym(try super::getSymbol(r, 1)),
48
        case 3 => return il::Val::FnAddr(try super::getSymbol(r, 2)),
49
        case 4 => return il::Val::Undef,
50
        else => throw super::error(offset, "invalid RIL value tag"),
51
    }
52
}
53
54
/// Read an SSA register index for the shared allocator.
55
export fn getReg(r: *mut super::Reader) -> il::Reg throws (super::Error) {
56
    return il::Reg { n: try super::get32(r) };
57
}
58
59
/// Write a length-prefixed argument list.
60
export fn putArgs(w: *mut super::Writer, args: *[il::Val]) throws (super::Error) {
61
    try super::put32(w, args.len);
62
    for arg in args { try putVal(w, arg); }
63
}
64
65
/// Read a length-prefixed argument list into caller-owned mutable storage.
66
export fn getArgs(r: *mut super::Reader) -> *mut [il::Val] throws (super::Error) {
67
    let count = try super::count(r, 1);
68
    let args = try super::storage(r.arena, @sizeOf(il::Val), @alignOf(il::Val), count, r.offset) as *mut [il::Val];
69
    for i in 0..count { set args[i] = try getVal(r); }
70
    return args;
71
}
72
73
/// Stable binary ALU tags: arithmetic 0..6, comparisons 7..12, bitwise 13..18.
74
fn binTag(op: il::BinOp) -> u8 {
75
    match op {
76
        case il::BinOp::Add => return 0,
77
        case il::BinOp::Sub => return 1,
78
        case il::BinOp::Mul => return 2,
79
        case il::BinOp::Sdiv => return 3,
80
        case il::BinOp::Udiv => return 4,
81
        case il::BinOp::Srem => return 5,
82
        case il::BinOp::Urem => return 6,
83
        case il::BinOp::Eq => return 7,
84
        case il::BinOp::Ne => return 8,
85
        case il::BinOp::Slt => return 9,
86
        case il::BinOp::Sge => return 10,
87
        case il::BinOp::Ult => return 11,
88
        case il::BinOp::Uge => return 12,
89
        case il::BinOp::And => return 13,
90
        case il::BinOp::Or => return 14,
91
        case il::BinOp::Xor => return 15,
92
        case il::BinOp::Shl => return 16,
93
        case il::BinOp::Sshr => return 17,
94
        case il::BinOp::Ushr => return 18,
95
    }
96
}
97
98
/// Decode a binary ALU operation without depending on union layout.
99
fn getBinOp(r: *mut super::Reader) -> il::BinOp throws (super::Error) {
100
    let offset = r.offset;
101
    match try super::get8(r) {
102
        case 0 => return il::BinOp::Add,
103
        case 1 => return il::BinOp::Sub,
104
        case 2 => return il::BinOp::Mul,
105
        case 3 => return il::BinOp::Sdiv,
106
        case 4 => return il::BinOp::Udiv,
107
        case 5 => return il::BinOp::Srem,
108
        case 6 => return il::BinOp::Urem,
109
        case 7 => return il::BinOp::Eq,
110
        case 8 => return il::BinOp::Ne,
111
        case 9 => return il::BinOp::Slt,
112
        case 10 => return il::BinOp::Sge,
113
        case 11 => return il::BinOp::Ult,
114
        case 12 => return il::BinOp::Uge,
115
        case 13 => return il::BinOp::And,
116
        case 14 => return il::BinOp::Or,
117
        case 15 => return il::BinOp::Xor,
118
        case 16 => return il::BinOp::Shl,
119
        case 17 => return il::BinOp::Sshr,
120
        case 18 => return il::BinOp::Ushr,
121
        else => throw super::error(offset, "invalid RIL binary operation tag"),
122
    }
123
}
124
125
/// Stable compare-and-branch tags: Eq=0, Ne=1, Slt=2, Ult=3.
126
fn cmpTag(op: il::CmpOp) -> u8 {
127
    match op {
128
        case il::CmpOp::Eq => return 0,
129
        case il::CmpOp::Ne => return 1,
130
        case il::CmpOp::Slt => return 2,
131
        case il::CmpOp::Ult => return 3,
132
    }
133
}
134
135
/// Decode one compare-and-branch operation.
136
fn getCmpOp(r: *mut super::Reader) -> il::CmpOp throws (super::Error) {
137
    let offset = r.offset;
138
    match try super::get8(r) {
139
        case 0 => return il::CmpOp::Eq,
140
        case 1 => return il::CmpOp::Ne,
141
        case 2 => return il::CmpOp::Slt,
142
        case 3 => return il::CmpOp::Ult,
143
        else => throw super::error(offset, "invalid RIL comparison tag"),
144
    }
145
}
146
147
/// Read a nonzero power-of-two memory alignment.
148
export fn getAlignment(r: *mut super::Reader) -> u32 throws (super::Error) {
149
    let offset = r.offset;
150
    let alignment = try super::get32(r);
151
    if alignment == 0 or (alignment & (alignment - 1)) <> 0 {
152
        throw super::error(offset, "RIL alignment must be a nonzero power of two");
153
    }
154
    return alignment;
155
}
156
157
/// Serialize every existing instruction variant with explicit field encodings.
158
export fn put(w: *mut super::Writer, instr: il::Instr) throws (super::Error) {
159
    match instr {
160
        case il::Instr::Reserve { dst, size, alignment } => {
161
            try super::put8(w, 0); try super::put32(w, dst.n);
162
            try putVal(w, size); try super::put32(w, alignment);
163
        }
164
        case il::Instr::Load { typ, dst, src, offset } => {
165
            try super::put8(w, 1); try putType(w, typ); try super::put32(w, dst.n);
166
            try super::put32(w, src.n); try super::put32(w, offset as u32);
167
        }
168
        case il::Instr::Sload { typ, dst, src, offset } => {
169
            try super::put8(w, 2); try putType(w, typ); try super::put32(w, dst.n);
170
            try super::put32(w, src.n); try super::put32(w, offset as u32);
171
        }
172
        case il::Instr::Store { typ, src, dst, offset } => {
173
            try super::put8(w, 3); try putType(w, typ); try putVal(w, src);
174
            try super::put32(w, dst.n); try super::put32(w, offset as u32);
175
        }
176
        case il::Instr::Blit { dst, src, size, alignment } => {
177
            try super::put8(w, 4); try super::put32(w, dst.n); try super::put32(w, src.n);
178
            try putVal(w, size); try super::put32(w, alignment);
179
        }
180
        case il::Instr::Copy { dst, val } => {
181
            try super::put8(w, 5); try super::put32(w, dst.n); try putVal(w, val);
182
        }
183
        case il::Instr::BinOp { op, typ, dst, a, b } => {
184
            try super::put8(w, 6); try super::put8(w, binTag(op)); try putType(w, typ);
185
            try super::put32(w, dst.n); try putVal(w, a); try putVal(w, b);
186
        }
187
        case il::Instr::UnOp { op, typ, dst, a } => {
188
            try super::put8(w, 7);
189
            match op { case il::UnOp::Neg => try super::put8(w, 0), case il::UnOp::Not => try super::put8(w, 1) }
190
            try putType(w, typ); try super::put32(w, dst.n); try putVal(w, a);
191
        }
192
        case il::Instr::Zext { typ, dst, val } => {
193
            try super::put8(w, 8); try putType(w, typ); try super::put32(w, dst.n); try putVal(w, val);
194
        }
195
        case il::Instr::Sext { typ, dst, val } => {
196
            try super::put8(w, 9); try putType(w, typ); try super::put32(w, dst.n); try putVal(w, val);
197
        }
198
        case il::Instr::Call { retTy, dst, func, args } => {
199
            try super::put8(w, 10); try putType(w, retTy);
200
            if let reg = dst { try super::put8(w, 1); try super::put32(w, reg.n); }
201
            else { try super::put8(w, 0); }
202
            try putVal(w, func); try putArgs(w, args);
203
        }
204
        case il::Instr::Ret { val } => {
205
            try super::put8(w, 11);
206
            if let value = val { try super::put8(w, 1); try putVal(w, value); }
207
            else { try super::put8(w, 0); }
208
        }
209
        case il::Instr::Jmp { target, args } => {
210
            try super::put8(w, 12); try super::put32(w, target); try putArgs(w, args);
211
        }
212
        case il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs } => {
213
            try super::put8(w, 13); try super::put8(w, cmpTag(op)); try putType(w, typ);
214
            try putVal(w, a); try putVal(w, b); try super::put32(w, thenTarget);
215
            try putArgs(w, thenArgs); try super::put32(w, elseTarget); try putArgs(w, elseArgs);
216
        }
217
        case il::Instr::Switch { val, defaultTarget, defaultArgs, cases } => {
218
            try super::put8(w, 14); try putVal(w, val); try super::put32(w, defaultTarget);
219
            try putArgs(w, defaultArgs); try super::put32(w, cases.len);
220
            for item in cases {
221
                try super::put64(w, item.value as u64); try super::put32(w, item.target); try putArgs(w, item.args);
222
            }
223
        }
224
        case il::Instr::Unreachable => try super::put8(w, 15),
225
        case il::Instr::Ecall { dst, num, a0, a1, a2, a3 } => {
226
            try super::put8(w, 16); try super::put32(w, dst.n); try putVal(w, num);
227
            try putVal(w, a0); try putVal(w, a1); try putVal(w, a2); try putVal(w, a3);
228
        }
229
        case il::Instr::Ebreak => try super::put8(w, 17),
230
        case il::Instr::MemoryFence => try super::put8(w, 18),
231
    }
232
}
233
234
/// Decode every current instruction variant. Sequential locals make wire order
235
/// independent of record-literal expression evaluation and compiler ABI layout.
236
export fn get(r: *mut super::Reader) -> il::Instr throws (super::Error) {
237
    let offset = r.offset;
238
    let tag = try super::get8(r);
239
    match tag {
240
        case 0 => {
241
            let dst = try getReg(r); let size = try getVal(r); let alignment = try getAlignment(r);
242
            return il::Instr::Reserve { dst, size, alignment };
243
        }
244
        case 1, 2 => {
245
            let typ = try getType(r); let dst = try getReg(r); let src = try getReg(r);
246
            let offset = (try super::get32(r)) as i32;
247
            if tag == 1 { return il::Instr::Load { typ, dst, src, offset }; }
248
            return il::Instr::Sload { typ, dst, src, offset };
249
        }
250
        case 3 => {
251
            let typ = try getType(r); let src = try getVal(r); let dst = try getReg(r);
252
            let offset = (try super::get32(r)) as i32;
253
            return il::Instr::Store { typ, src, dst, offset };
254
        }
255
        case 4 => {
256
            let dst = try getReg(r); let src = try getReg(r); let size = try getVal(r);
257
            let alignment = try getAlignment(r);
258
            return il::Instr::Blit { dst, src, size, alignment };
259
        }
260
        case 5 => {
261
            let dst = try getReg(r); let val = try getVal(r);
262
            return il::Instr::Copy { dst, val };
263
        }
264
        case 6 => {
265
            let op = try getBinOp(r); let typ = try getType(r); let dst = try getReg(r);
266
            let a = try getVal(r); let b = try getVal(r);
267
            return il::Instr::BinOp { op, typ, dst, a, b };
268
        }
269
        case 7 => {
270
            let opOffset = r.offset; let opTag = try super::get8(r);
271
            if opTag > 1 { throw super::error(opOffset, "invalid RIL unary operation tag"); }
272
            let op = il::UnOp::Neg if opTag == 0 else il::UnOp::Not;
273
            let typ = try getType(r); let dst = try getReg(r); let a = try getVal(r);
274
            return il::Instr::UnOp { op, typ, dst, a };
275
        }
276
        case 8, 9 => {
277
            let typ = try getType(r); let dst = try getReg(r); let val = try getVal(r);
278
            if tag == 8 { return il::Instr::Zext { typ, dst, val }; }
279
            return il::Instr::Sext { typ, dst, val };
280
        }
281
        case 10 => {
282
            let retTy = try getType(r); let present = try super::getBool(r);
283
            let mut dst: ?il::Reg = nil;
284
            if present { set dst = try getReg(r); }
285
            let func = try getVal(r); let args = try getArgs(r);
286
            return il::Instr::Call { retTy, dst, func, args };
287
        }
288
        case 11 => {
289
            let present = try super::getBool(r);
290
            let mut val: ?il::Val = nil;
291
            if present { set val = try getVal(r); }
292
            return il::Instr::Ret { val };
293
        }
294
        case 12 => {
295
            let target = try super::get32(r); let args = try getArgs(r);
296
            return il::Instr::Jmp { target, args };
297
        }
298
        case 13 => {
299
            let op = try getCmpOp(r); let typ = try getType(r);
300
            let a = try getVal(r); let b = try getVal(r);
301
            let thenTarget = try super::get32(r); let thenArgs = try getArgs(r);
302
            let elseTarget = try super::get32(r); let elseArgs = try getArgs(r);
303
            return il::Instr::Br { op, typ, a, b, thenTarget, thenArgs, elseTarget, elseArgs };
304
        }
305
        case 14 => {
306
            let val = try getVal(r); let defaultTarget = try super::get32(r); let defaultArgs = try getArgs(r);
307
            let count = try super::count(r, 16);
308
            let cases = try super::storage(r.arena, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), count, r.offset) as *mut [il::SwitchCase];
309
            for i in 0..count {
310
                let value = (try super::get64(r)) as i64; let target = try super::get32(r); let args = try getArgs(r);
311
                set cases[i] = il::SwitchCase { value, target, args };
312
            }
313
            return il::Instr::Switch { val, defaultTarget, defaultArgs, cases };
314
        }
315
        case 15 => return il::Instr::Unreachable,
316
        case 16 => {
317
            let dst = try getReg(r); let num = try getVal(r); let a0 = try getVal(r);
318
            let a1 = try getVal(r); let a2 = try getVal(r); let a3 = try getVal(r);
319
            return il::Instr::Ecall { dst, num, a0, a1, a2, a3 };
320
        }
321
        case 17 => return il::Instr::Ebreak,
322
        case 18 => return il::Instr::MemoryFence,
323
        else => throw super::error(offset, "invalid RIL instruction tag"),
324
    }
325
}