compiler: Check variable-operand symbol collection

a2a9b893bb8a7e6df66b1076b6e964b15d2634c460c513bbde4a2f6df3deafc0
Alexis Sellier committed ago 1 parent 7738e581
lib/std/lang/il/binary/collect.rad +31 -13
85 85
        try value(names, owner, item);
86 86
    }
87 87
}
88 88
89 89
/// Collect all symbolic instruction operands.
90 -
unsafe fn instruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) {
91 -
    match *instr {
90 +
fn instruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) {
91 +
    match instr {
92 92
        case il::Instr::Call { func, args, .. } => {
93 -
            try value(names, owner, func);
94 -
            try values(names, owner, args);
93 +
            try value(names, owner, *func);
94 +
            unsafe {
95 +
                try values(names, owner, *args);
96 +
            }
97 +
        },
98 +
        case il::Instr::Jmp { args, .. } => {
99 +
            unsafe {
100 +
                try values(names, owner, *args);
101 +
            }
95 102
        },
96 -
        case il::Instr::Jmp { args, .. } => try values(names, owner, args),
97 103
        case il::Instr::Br { a, b, thenArgs, elseArgs, .. } => {
98 -
            try value(names, owner, a);
99 -
            try value(names, owner, b);
100 -
            try values(names, owner, thenArgs);
101 -
            try values(names, owner, elseArgs);
104 +
            try value(names, owner, *a);
105 +
            try value(names, owner, *b);
106 +
            unsafe {
107 +
                try values(names, owner, *thenArgs);
108 +
                try values(names, owner, *elseArgs);
109 +
            }
102 110
        },
103 111
        case il::Instr::Switch { val, defaultArgs, cases, .. } => {
104 -
            try value(names, owner, val);
105 -
            try values(names, owner, defaultArgs);
106 -
            for branch in cases {
107 -
                try values(names, owner, branch.args);
112 +
            try value(names, owner, *val);
113 +
            unsafe {
114 +
                try switchValues(names, owner, *defaultArgs, *cases);
108 115
            }
109 116
        },
110 117
        else => try fixedInstruction(names, owner, instr),
111 118
    }
112 119
}
113 120
121 +
/// Collect default arguments before the arguments of each switch case.
122 +
fn switchValues 'tables (names: &mut Names 'tables, owner: *[u8], defaultArgs: &[il::Val], cases: &[il::SwitchCase]) throws (binary::Error) {
123 +
    try values(names, owner, defaultArgs);
124 +
    for i in 0..cases.len {
125 +
        let branch = &cases[i];
126 +
        unsafe {
127 +
            try values(names, owner, branch.args);
128 +
        }
129 +
    }
130 +
}
131 +
114 132
/// Collect symbolic operands stored directly in an instruction.
115 133
fn fixedInstruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) {
116 134
    match *instr {
117 135
        case il::Instr::Reserve { size, .. } => try value(names, owner, size),
118 136
        case il::Instr::Blit { size, .. } => try value(names, owner, size),
lib/std/lang/il/binary/decodeTests.rad +30 -0
463 463
        il::Instr::Copy { dst: r, val: il::Val::Imm(9) },
464 464
        il::Instr::Unreachable, il::Instr::Ebreak, il::Instr::MemoryFence,
465 465
    ] { try checkInstructionNames(instr, &[]); }
466 466
}
467 467
468 +
/// Variable operand groups retain first-use order and deduplicate names.
469 +
@test unsafe fn variableInstructionCollection() throws (testing::TestError) {
470 +
    let r = il::Reg { n: 0 };
471 +
    let a = il::Val::DataSym("z::data");
472 +
    let b = il::Val::FnAddr("a::fn");
473 +
    let c = il::Val::DataSym("c::data");
474 +
    let imm = il::Val::Imm(0);
475 +
    let mut args = [imm, a, a, b];
476 +
    let mut fallback = [c, a];
477 +
    try checkInstructionNames(il::Instr::Call { retTy: il::Type::W64, dst: r,
478 +
        func: b, args: &args[..] }, &["a::fn", "a", "z::data", "z"]);
479 +
    try checkInstructionNames(il::Instr::Call { retTy: il::Type::W64, dst: nil,
480 +
        func: il::Val::Reg(r), args: &[] }, &[]);
481 +
    try checkInstructionNames(il::Instr::Jmp { target: 0, args: &mut args[..] },
482 +
        &["z::data", "z", "a::fn", "a"]);
483 +
    try checkInstructionNames(il::Instr::Br { op: il::CmpOp::Eq, typ: il::Type::W64,
484 +
        a, b: imm, thenTarget: 0, thenArgs: &mut [], elseTarget: 0, elseArgs: &mut args[..] },
485 +
        &["z::data", "z", "a::fn", "a"]);
486 +
    try checkInstructionNames(il::Instr::Br { op: il::CmpOp::Eq, typ: il::Type::W64,
487 +
        a: imm, b: imm, thenTarget: 0, thenArgs: &mut args[..], elseTarget: 0, elseArgs: &mut fallback[..] },
488 +
        &["z::data", "z", "a::fn", "a", "c::data", "c"]);
489 +
    let mut cases = [il::SwitchCase { value: 0, target: 0, args: &mut [] },
490 +
        il::SwitchCase { value: 1, target: 0, args: &mut args[..] }];
491 +
    try checkInstructionNames(il::Instr::Switch { val: b, defaultTarget: 0,
492 +
        defaultArgs: &mut fallback[..], cases: &mut cases[..] },
493 +
        &["a::fn", "a", "c::data", "c", "z::data", "z"]);
494 +
    try checkInstructionNames(il::Instr::Switch { val: imm, defaultTarget: 0,
495 +
        defaultArgs: &mut [], cases: &mut [] }, &[]);
496 +
}
497 +
468 498
/// Collect one instruction at exact-fit and every shorter symbol capacity.
469 499
unsafe fn checkInstructionNames(instr: il::Instr, expected: &[*[u8]]) throws (testing::TestError) {
470 500
    let mut body = [instr];
471 501
    let function = il::Fn {
472 502
        name: "p::f", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true,