compiler: Check variable-operand symbol collection
a2a9b893bb8a7e6df66b1076b6e964b15d2634c460c513bbde4a2f6df3deafc0
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, |