compiler: Check binary instruction and switch encoding

9b1ca970d96ade65f0b940b1e2890059f8d1fe20451dd525ba5a686232406547
Alexis Sellier committed ago 1 parent 81eb8a8d
lib/std/lang/il/binary/tests.rad +14 -0
315 315
    }, &[
316 316
        14, 4, 1, 0, 0, 0, 1, 0, 0, 0, 4, 1,
317 317
        0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 2,
318 318
        0, 0, 0, 1, 0, 0, 0, 4,
319 319
    ]);
320 +
    try instruction(il::Instr::Switch {
321 +
        val: il::Val::Undef, defaultTarget: 1, defaultArgs: &mut [], cases: &mut [],
322 +
    }, &[14, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
323 +
    let mut mixedCases = [
324 +
        il::SwitchCase { value: -9223372036854775808, target: 0, args: &mut [] },
325 +
        il::SwitchCase { value: 9223372036854775807, target: 2, args: &mut args[..] },
326 +
    ];
327 +
    try instruction(il::Instr::Switch {
328 +
        val: il::Val::Undef, defaultTarget: 1, defaultArgs: &mut [], cases: &mut mixedCases[..],
329 +
    }, &[
330 +
        14, 4, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
331 +
        0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0,
332 +
        255, 255, 255, 255, 255, 255, 255, 127, 2, 0, 0, 0, 1, 0, 0, 0, 4,
333 +
    ]);
320 334
    try instruction(il::Instr::Unreachable, &[15]);
321 335
    try instruction(il::Instr::Ecall {
322 336
        dst: il::Reg { n: 1 }, num: il::Val::Undef, a0: il::Val::Undef, a1: il::Val::Undef,
323 337
        a2: il::Val::Undef, a3: il::Val::Undef,
324 338
    }, &[16, 1, 0, 0, 0, 4, 4, 4, 4, 4]);
lib/std/lang/il/binary/writer.rad +35 -12
251 251
}
252 252
253 253
/// Write one instruction. Fields follow the IL record declaration order.
254 254
/// All raw instruction operand tables must remain valid during encoding.
255 255
export unsafe fn instr 'buffer (out: &mut Writer 'buffer, item: il::Instr) throws (binary::Error) {
256 -
    match item {
256 +
    try writeInstr(out, &item);
257 +
}
258 +
259 +
/// Write instruction fields and acquire nested operand views at their wire position.
260 +
fn writeInstr 'buffer (out: &mut Writer 'buffer, item: &il::Instr) throws (binary::Error) {
261 +
    match *item {
257 262
        case il::Instr::Call { retTy: vretTy, dst: vdst, func: vfunc, args: vargs } => {
258 263
            try integer(out, super::INSTR_CALL as u64, 1);
259 264
            try typ(out, vretTy);
260 265
            if let present = vdst {
261 266
                try integer(out, 1, 1);
262 267
                try integer(out, present.n as u64, 4);
263 268
            } else {
264 269
                try integer(out, 0, 1);
265 270
            }
266 271
            try val(out, vfunc);
267 -
            try values(out, vargs);
272 +
            unsafe {
273 +
                try values(out, vargs);
274 +
            }
268 275
        },
269 276
        case il::Instr::Jmp { target: vtarget, args: vargs } => {
270 277
            try integer(out, super::INSTR_JMP as u64, 1);
271 278
            try integer(out, vtarget as u64, 4);
272 -
            try values(out, vargs);
279 +
            unsafe {
280 +
                try values(out, vargs);
281 +
            }
273 282
        },
274 283
        case il::Instr::Br { op: vop, typ: vtyp, a: va, b: vb, thenTarget: vthenTarget, thenArgs: vthenArgs, elseTarget: velseTarget, elseArgs: velseArgs } => {
275 284
            try integer(out, super::INSTR_BR as u64, 1);
276 285
            try cmpOp(out, vop);
277 286
            try typ(out, vtyp);
278 287
            try val(out, va);
279 288
            try val(out, vb);
280 289
            try integer(out, vthenTarget as u64, 4);
281 -
            try values(out, vthenArgs);
290 +
            unsafe {
291 +
                try values(out, vthenArgs);
292 +
            }
282 293
            try integer(out, velseTarget as u64, 4);
283 -
            try values(out, velseArgs);
294 +
            unsafe {
295 +
                try values(out, velseArgs);
296 +
            }
284 297
        },
285 298
        case il::Instr::Switch { val: vval, defaultTarget: vdefaultTarget, defaultArgs: vdefaultArgs, cases: vcases } => {
286 299
            try integer(out, super::INSTR_SWITCH as u64, 1);
287 300
            try val(out, vval);
288 301
            try integer(out, vdefaultTarget as u64, 4);
289 -
            try values(out, vdefaultArgs);
290 -
            try integer(out, vcases.len as u64, 4);
291 -
            for branch in vcases {
292 -
                try integer(out, branch.value as u64, 8);
293 -
                try integer(out, branch.target as u64, 4);
294 -
                try values(out, branch.args);
302 +
            unsafe {
303 +
                try values(out, vdefaultArgs);
304 +
                try writeCases(out, vcases);
295 305
            }
296 306
        },
297 -
        else => try fixedInstr(out, item),
307 +
        else => try fixedInstr(out, *item),
308 +
    }
309 +
}
310 +
311 +
/// Write a counted switch-case table with signed values and argument groups.
312 +
fn writeCases 'buffer (out: &mut Writer 'buffer, cases: &[il::SwitchCase]) throws (binary::Error) {
313 +
    try integer(out, cases.len as u64, 4);
314 +
    for i in 0..cases.len {
315 +
        let branch = &cases[i];
316 +
        try integer(out, branch.value as u64, 8);
317 +
        try integer(out, branch.target as u64, 4);
318 +
        unsafe {
319 +
            try values(out, branch.args);
320 +
        }
298 321
    }
299 322
}
300 323
301 324
/// Write one data initializer and its repetition count.
302 325
export fn dataValue 'buffer (out: &mut Writer 'buffer, value: il::DataValue) throws (binary::Error) {