Merge pull request #3446 from bjorn3/remove_old_insts
Remove many unused instructions
This commit is contained in:
@@ -70,8 +70,6 @@ pub(crate) struct InstructionContent {
|
||||
pub is_terminator: bool,
|
||||
/// True for all branch or jump instructions.
|
||||
pub is_branch: bool,
|
||||
/// True for all indirect branch or jump instructions.',
|
||||
pub is_indirect_branch: bool,
|
||||
/// Is this a call instruction?
|
||||
pub is_call: bool,
|
||||
/// Is this a return instruction?
|
||||
@@ -145,7 +143,6 @@ pub(crate) struct InstructionBuilder {
|
||||
// See Instruction comments for the meaning of these fields.
|
||||
is_terminator: bool,
|
||||
is_branch: bool,
|
||||
is_indirect_branch: bool,
|
||||
is_call: bool,
|
||||
is_return: bool,
|
||||
is_ghost: bool,
|
||||
@@ -168,7 +165,6 @@ impl InstructionBuilder {
|
||||
|
||||
is_terminator: false,
|
||||
is_branch: false,
|
||||
is_indirect_branch: false,
|
||||
is_call: false,
|
||||
is_return: false,
|
||||
is_ghost: false,
|
||||
@@ -210,12 +206,6 @@ impl InstructionBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn is_indirect_branch(mut self, val: bool) -> Self {
|
||||
self.is_indirect_branch = val;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn is_call(mut self, val: bool) -> Self {
|
||||
self.is_call = val;
|
||||
@@ -300,7 +290,6 @@ impl InstructionBuilder {
|
||||
imm_opnums,
|
||||
is_terminator: self.is_terminator,
|
||||
is_branch: self.is_branch,
|
||||
is_indirect_branch: self.is_indirect_branch,
|
||||
is_call: self.is_call,
|
||||
is_return: self.is_return,
|
||||
is_ghost: self.is_ghost,
|
||||
|
||||
@@ -468,13 +468,6 @@ fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {
|
||||
"True for all branch or jump instructions.",
|
||||
fmt,
|
||||
);
|
||||
gen_bool_accessor(
|
||||
all_inst,
|
||||
|inst| inst.is_indirect_branch,
|
||||
"is_indirect_branch",
|
||||
"True for all indirect branch or jump instructions.",
|
||||
fmt,
|
||||
);
|
||||
gen_bool_accessor(
|
||||
all_inst,
|
||||
|inst| inst.is_call,
|
||||
|
||||
@@ -13,8 +13,6 @@ pub(crate) struct Formats {
|
||||
pub(crate) branch_icmp: Rc<InstructionFormat>,
|
||||
pub(crate) branch_int: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table_base: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table_entry: Rc<InstructionFormat>,
|
||||
pub(crate) call: Rc<InstructionFormat>,
|
||||
pub(crate) call_indirect: Rc<InstructionFormat>,
|
||||
pub(crate) cond_trap: Rc<InstructionFormat>,
|
||||
@@ -23,7 +21,6 @@ pub(crate) struct Formats {
|
||||
pub(crate) float_cond_trap: Rc<InstructionFormat>,
|
||||
pub(crate) func_addr: Rc<InstructionFormat>,
|
||||
pub(crate) heap_addr: Rc<InstructionFormat>,
|
||||
pub(crate) indirect_jump: Rc<InstructionFormat>,
|
||||
pub(crate) int_compare: Rc<InstructionFormat>,
|
||||
pub(crate) int_compare_imm: Rc<InstructionFormat>,
|
||||
pub(crate) int_cond: Rc<InstructionFormat>,
|
||||
@@ -172,22 +169,6 @@ impl Formats {
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
branch_table_entry: Builder::new("BranchTableEntry")
|
||||
.value()
|
||||
.value()
|
||||
.imm(&imm.uimm8)
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
branch_table_base: Builder::new("BranchTableBase")
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
indirect_jump: Builder::new("IndirectJump")
|
||||
.value()
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
call: Builder::new("Call")
|
||||
.imm(&entities.func_ref)
|
||||
.varargs()
|
||||
|
||||
@@ -38,26 +38,6 @@ fn define_control_flow(
|
||||
.is_branch(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"fallthrough",
|
||||
r#"
|
||||
Fall through to the next block.
|
||||
|
||||
This is the same as `jump`, except the destination block must be
|
||||
the next one in the layout.
|
||||
|
||||
Jumps are turned into fall-through instructions by the branch
|
||||
relaxation pass. There is no reason to use this instruction outside
|
||||
that pass.
|
||||
"#,
|
||||
&formats.jump,
|
||||
)
|
||||
.operands_in(vec![block, args])
|
||||
.is_terminator(true)
|
||||
.is_branch(true),
|
||||
);
|
||||
|
||||
let Testable = &TypeVar::new(
|
||||
"Testable",
|
||||
"A scalar boolean or integer type",
|
||||
@@ -214,68 +194,6 @@ fn define_control_flow(
|
||||
TypeSetBuilder::new().ints(32..64).refs(32..64).build(),
|
||||
);
|
||||
|
||||
{
|
||||
let x = &Operand::new("x", iAddr).with_doc("index into jump table");
|
||||
let addr = &Operand::new("addr", iAddr);
|
||||
let Size = &Operand::new("Size", &imm.uimm8).with_doc("Size in bytes");
|
||||
let JT = &Operand::new("JT", &entities.jump_table);
|
||||
let entry = &Operand::new("entry", iAddr).with_doc("entry of jump table");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"jump_table_entry",
|
||||
r#"
|
||||
Get an entry from a jump table.
|
||||
|
||||
Load a serialized ``entry`` from a jump table ``JT`` at a given index
|
||||
``addr`` with a specific ``Size``. The retrieved entry may need to be
|
||||
decoded after loading, depending upon the jump table type used.
|
||||
|
||||
Currently, the only type supported is entries which are relative to the
|
||||
base of the jump table.
|
||||
"#,
|
||||
&formats.branch_table_entry,
|
||||
)
|
||||
.operands_in(vec![x, addr, Size, JT])
|
||||
.operands_out(vec![entry])
|
||||
.can_load(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"jump_table_base",
|
||||
r#"
|
||||
Get the absolute base address of a jump table.
|
||||
|
||||
This is used for jump tables wherein the entries are stored relative to
|
||||
the base of jump table. In order to use these, generated code should first
|
||||
load an entry using ``jump_table_entry``, then use this instruction to add
|
||||
the relative base back to it.
|
||||
"#,
|
||||
&formats.branch_table_base,
|
||||
)
|
||||
.operands_in(vec![JT])
|
||||
.operands_out(vec![addr]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"indirect_jump_table_br",
|
||||
r#"
|
||||
Branch indirectly via a jump table entry.
|
||||
|
||||
Unconditionally jump via a jump table entry that was previously loaded
|
||||
with the ``jump_table_entry`` instruction.
|
||||
"#,
|
||||
&formats.indirect_jump,
|
||||
)
|
||||
.operands_in(vec![addr, JT])
|
||||
.is_indirect_branch(true)
|
||||
.is_terminator(true)
|
||||
.is_branch(true),
|
||||
);
|
||||
}
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"debugtrap",
|
||||
@@ -1880,156 +1798,6 @@ pub(crate) fn define(
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"spill",
|
||||
r#"
|
||||
Spill a register value to a stack slot.
|
||||
|
||||
This instruction behaves exactly like `copy`, but the result
|
||||
value is assigned to a spill slot.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a])
|
||||
.can_store(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"fill",
|
||||
r#"
|
||||
Load a register value from a stack slot.
|
||||
|
||||
This instruction behaves exactly like `copy`, but creates a new
|
||||
SSA value for the spilled input value.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a])
|
||||
.can_load(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"fill_nop",
|
||||
r#"
|
||||
This is identical to `fill`, except it has no encoding, since it is a no-op.
|
||||
|
||||
This instruction is created only during late-stage redundant-reload removal, after all
|
||||
registers and stack slots have been assigned. It is used to replace `fill`s that have
|
||||
been identified as redundant.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a])
|
||||
.can_load(true),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"copy_nop",
|
||||
r#"
|
||||
Stack-slot-to-the-same-stack-slot copy, which is guaranteed to turn
|
||||
into a no-op. This instruction is for use only within Cranelift itself.
|
||||
|
||||
This instruction copies its input, preserving the value type.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
let delta = &Operand::new("delta", Int);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"adjust_sp_down",
|
||||
r#"
|
||||
Subtracts ``delta`` offset value from the stack pointer register.
|
||||
|
||||
This instruction is used to adjust the stack pointer by a dynamic amount.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![delta])
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let Offset = &Operand::new("Offset", &imm.imm64).with_doc("Offset from current stack pointer");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"adjust_sp_up_imm",
|
||||
r#"
|
||||
Adds ``Offset`` immediate offset value to the stack pointer register.
|
||||
|
||||
This instruction is used to adjust the stack pointer, primarily in function
|
||||
prologues and epilogues. ``Offset`` is constrained to the size of a signed
|
||||
32-bit integer.
|
||||
"#,
|
||||
&formats.unary_imm,
|
||||
)
|
||||
.operands_in(vec![Offset])
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let Offset = &Operand::new("Offset", &imm.imm64).with_doc("Offset from current stack pointer");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"adjust_sp_down_imm",
|
||||
r#"
|
||||
Subtracts ``Offset`` immediate offset value from the stack pointer
|
||||
register.
|
||||
|
||||
This instruction is used to adjust the stack pointer, primarily in function
|
||||
prologues and epilogues. ``Offset`` is constrained to the size of a signed
|
||||
32-bit integer.
|
||||
"#,
|
||||
&formats.unary_imm,
|
||||
)
|
||||
.operands_in(vec![Offset])
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let f = &Operand::new("f", iflags);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"ifcmp_sp",
|
||||
r#"
|
||||
Compare ``addr`` with the stack pointer and set the CPU flags.
|
||||
|
||||
This is like `ifcmp` where ``addr`` is the LHS operand and the stack
|
||||
pointer is the RHS.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![addr])
|
||||
.operands_out(vec![f]),
|
||||
);
|
||||
|
||||
let N =
|
||||
&Operand::new("args", &entities.varargs).with_doc("Variable number of args for StackMap");
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"safepoint",
|
||||
r#"
|
||||
This instruction will provide live reference values at a point in
|
||||
the function. It can only be used by the compiler.
|
||||
"#,
|
||||
&formats.multiary,
|
||||
)
|
||||
.operands_in(vec![N])
|
||||
.other_side_effects(true),
|
||||
);
|
||||
|
||||
let x = &Operand::new("x", TxN).with_doc("Vector to split");
|
||||
let lo = &Operand::new("lo", &TxN.half_vector()).with_doc("Low-numbered lanes of `x`");
|
||||
let hi = &Operand::new("hi", &TxN.half_vector()).with_doc("High-numbered lanes of `x`");
|
||||
|
||||
@@ -274,12 +274,12 @@ impl Function {
|
||||
let mut inst_iter = inst_iter.skip_while(|&inst| !dfg[inst].opcode().is_branch());
|
||||
|
||||
// A conditional branch is permitted in a basic block only when followed
|
||||
// by a terminal jump or fallthrough instruction.
|
||||
// by a terminal jump instruction.
|
||||
if let Some(_branch) = inst_iter.next() {
|
||||
if let Some(next) = inst_iter.next() {
|
||||
match dfg[next].opcode() {
|
||||
Opcode::Fallthrough | Opcode::Jump => (),
|
||||
_ => return Err((next, "post-branch instruction not fallthrough or jump")),
|
||||
Opcode::Jump => (),
|
||||
_ => return Err((next, "post-branch instruction not jump")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,6 @@ impl InstructionData {
|
||||
Self::BranchTable {
|
||||
table, destination, ..
|
||||
} => BranchInfo::Table(table, Some(destination)),
|
||||
Self::IndirectJump { table, .. } => BranchInfo::Table(table, None),
|
||||
_ => {
|
||||
debug_assert!(!self.opcode().is_branch());
|
||||
BranchInfo::NotABranch
|
||||
@@ -248,7 +247,7 @@ impl InstructionData {
|
||||
| Self::BranchInt { destination, .. }
|
||||
| Self::BranchFloat { destination, .. }
|
||||
| Self::BranchIcmp { destination, .. } => Some(destination),
|
||||
Self::BranchTable { .. } | Self::IndirectJump { .. } => None,
|
||||
Self::BranchTable { .. } => None,
|
||||
_ => {
|
||||
debug_assert!(!self.opcode().is_branch());
|
||||
None
|
||||
@@ -282,7 +281,7 @@ impl InstructionData {
|
||||
ref mut destination,
|
||||
..
|
||||
} => Some(destination),
|
||||
Self::BranchTable { .. } | Self::IndirectJump { .. } => None,
|
||||
Self::BranchTable { .. } => None,
|
||||
_ => {
|
||||
debug_assert!(!self.opcode().is_branch());
|
||||
None
|
||||
@@ -297,8 +296,7 @@ impl InstructionData {
|
||||
&InstructionData::UnaryBool { imm, .. } => Some(DataValue::from(imm)),
|
||||
// 8-bit.
|
||||
&InstructionData::BinaryImm8 { imm, .. }
|
||||
| &InstructionData::TernaryImm8 { imm, .. }
|
||||
| &InstructionData::BranchTableEntry { imm, .. } => Some(DataValue::from(imm as i8)), // Note the switch from unsigned to signed.
|
||||
| &InstructionData::TernaryImm8 { imm, .. } => Some(DataValue::from(imm as i8)), // Note the switch from unsigned to signed.
|
||||
// 32-bit
|
||||
&InstructionData::UnaryIeee32 { imm, .. } => Some(DataValue::from(imm)),
|
||||
&InstructionData::HeapAddr { imm, .. } => {
|
||||
|
||||
@@ -2029,10 +2029,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::JumpTableEntry | Opcode::JumpTableBase => {
|
||||
panic!("Should not appear: we handle BrTable directly");
|
||||
}
|
||||
|
||||
Opcode::Debugtrap => {
|
||||
ctx.emit(Inst::Brk);
|
||||
}
|
||||
@@ -2076,10 +2072,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
});
|
||||
}
|
||||
|
||||
Opcode::Safepoint => {
|
||||
panic!("safepoint instructions not used by new backend's safepoints!");
|
||||
}
|
||||
|
||||
Opcode::Trapz | Opcode::Trapnz | Opcode::ResumableTrapnz => {
|
||||
panic!("trapz / trapnz / resumable_trapnz should have been removed by legalization!");
|
||||
}
|
||||
@@ -2162,25 +2154,12 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
ctx.emit(Inst::gen_move(writable_xreg(PINNED_REG), rm, I64));
|
||||
}
|
||||
|
||||
Opcode::Spill
|
||||
| Opcode::Fill
|
||||
| Opcode::FillNop
|
||||
| Opcode::CopyNop
|
||||
| Opcode::AdjustSpDown
|
||||
| Opcode::AdjustSpUpImm
|
||||
| Opcode::AdjustSpDownImm
|
||||
| Opcode::IfcmpSp => {
|
||||
panic!("Unused opcode should not be encountered.");
|
||||
}
|
||||
|
||||
Opcode::Jump
|
||||
| Opcode::Fallthrough
|
||||
| Opcode::Brz
|
||||
| Opcode::Brnz
|
||||
| Opcode::BrIcmp
|
||||
| Opcode::Brif
|
||||
| Opcode::Brff
|
||||
| Opcode::IndirectJumpTableBr
|
||||
| Opcode::BrTable => {
|
||||
panic!("Branch opcode reached non-branch lowering logic!");
|
||||
}
|
||||
@@ -3794,7 +3773,7 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
let op0 = ctx.data(branches[0]).opcode();
|
||||
let op1 = ctx.data(branches[1]).opcode();
|
||||
|
||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
||||
assert!(op1 == Opcode::Jump);
|
||||
let taken = BranchTarget::Label(targets[0]);
|
||||
// not_taken target is the target of the second branch, even if it is a Fallthrough
|
||||
// instruction: because we reorder blocks while we lower, the fallthrough in the new
|
||||
@@ -3937,11 +3916,8 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
// Must be an unconditional branch or an indirect branch.
|
||||
let op = ctx.data(branches[0]).opcode();
|
||||
match op {
|
||||
Opcode::Jump | Opcode::Fallthrough => {
|
||||
Opcode::Jump => {
|
||||
assert!(branches.len() == 1);
|
||||
// In the Fallthrough case, the machine-independent driver
|
||||
// fills in `targets[0]` with our fallthrough block, so this
|
||||
// is valid for both Jump and Fallthrough.
|
||||
ctx.emit(Inst::Jump {
|
||||
dest: BranchTarget::Label(targets[0]),
|
||||
});
|
||||
|
||||
@@ -542,10 +542,9 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
) -> CodegenResult<()> {
|
||||
// A block should end with at most two branches. The first may be a
|
||||
// conditional branch; a conditional branch can be followed only by an
|
||||
// unconditional branch or fallthrough. Otherwise, if only one branch,
|
||||
// it may be an unconditional branch, a fallthrough, a return, or a
|
||||
// trap. These conditions are verified by `is_ebb_basic()` during the
|
||||
// verifier pass.
|
||||
// unconditional branch. Otherwise, if only one branch, it may be an
|
||||
// unconditional branch, a return, or a trap. These conditions are verified
|
||||
// by `is_ebb_basic()` during the verifier pass.
|
||||
assert!(branches.len() <= 2);
|
||||
|
||||
if branches.len() == 2 {
|
||||
@@ -553,7 +552,7 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
let op0 = ctx.data(branches[0]).opcode();
|
||||
let op1 = ctx.data(branches[1]).opcode();
|
||||
|
||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
||||
assert!(op1 == Opcode::Jump);
|
||||
let taken = BranchTarget::Label(targets[0]);
|
||||
let not_taken = BranchTarget::Label(targets[1]);
|
||||
|
||||
@@ -586,11 +585,8 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
// Must be an unconditional branch or an indirect branch.
|
||||
let op = ctx.data(branches[0]).opcode();
|
||||
match op {
|
||||
Opcode::Jump | Opcode::Fallthrough => {
|
||||
Opcode::Jump => {
|
||||
assert_eq!(branches.len(), 1);
|
||||
// In the Fallthrough case, the machine-independent driver
|
||||
// fills in `targets[0]` with our fallthrough block, so this
|
||||
// is valid for both Jump and Fallthrough.
|
||||
ctx.emit(Inst::Jump {
|
||||
dest: BranchTarget::Label(targets[0]),
|
||||
});
|
||||
|
||||
@@ -2888,17 +2888,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
|
||||
Opcode::Isplit | Opcode::Iconcat => unimplemented!("Wide integer ops not implemented."),
|
||||
|
||||
Opcode::Spill
|
||||
| Opcode::Fill
|
||||
| Opcode::FillNop
|
||||
| Opcode::CopyNop
|
||||
| Opcode::AdjustSpDown
|
||||
| Opcode::AdjustSpUpImm
|
||||
| Opcode::AdjustSpDownImm
|
||||
| Opcode::IfcmpSp => {
|
||||
panic!("Unused opcode should not be encountered.");
|
||||
}
|
||||
|
||||
Opcode::Ifcmp
|
||||
| Opcode::Ffcmp
|
||||
| Opcode::Trapff
|
||||
@@ -2909,25 +2898,15 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
}
|
||||
|
||||
Opcode::Jump
|
||||
| Opcode::Fallthrough
|
||||
| Opcode::Brz
|
||||
| Opcode::Brnz
|
||||
| Opcode::BrIcmp
|
||||
| Opcode::Brif
|
||||
| Opcode::Brff
|
||||
| Opcode::IndirectJumpTableBr
|
||||
| Opcode::BrTable => {
|
||||
panic!("Branch opcode reached non-branch lowering logic!");
|
||||
}
|
||||
|
||||
Opcode::JumpTableEntry | Opcode::JumpTableBase => {
|
||||
panic!("Should not appear: we handle BrTable directly");
|
||||
}
|
||||
|
||||
Opcode::Safepoint => {
|
||||
panic!("safepoint instructions not used by new backend's safepoints!");
|
||||
}
|
||||
|
||||
Opcode::IaddImm
|
||||
| Opcode::ImulImm
|
||||
| Opcode::UdivImm
|
||||
@@ -2984,7 +2963,7 @@ fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
let op0 = ctx.data(branches[0]).opcode();
|
||||
let op1 = ctx.data(branches[1]).opcode();
|
||||
|
||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
||||
assert!(op1 == Opcode::Jump);
|
||||
let taken = BranchTarget::Label(targets[0]);
|
||||
let not_taken = BranchTarget::Label(targets[1]);
|
||||
|
||||
@@ -3033,11 +3012,8 @@ fn lower_branch<C: LowerCtx<I = Inst>>(
|
||||
// Must be an unconditional branch or an indirect branch.
|
||||
let op = ctx.data(branches[0]).opcode();
|
||||
match op {
|
||||
Opcode::Jump | Opcode::Fallthrough => {
|
||||
Opcode::Jump => {
|
||||
assert!(branches.len() == 1);
|
||||
// In the Fallthrough case, the machine-independent driver
|
||||
// fills in `targets[0]` with our fallthrough block, so this
|
||||
// is valid for both Jump and Fallthrough.
|
||||
ctx.emit(Inst::Jump {
|
||||
dest: BranchTarget::Label(targets[0]),
|
||||
});
|
||||
|
||||
@@ -6858,38 +6858,20 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
panic!("table_addr should have been removed by legalization!");
|
||||
}
|
||||
|
||||
Opcode::Safepoint => {
|
||||
panic!("safepoint instructions not used by new backend's safepoints!");
|
||||
}
|
||||
|
||||
Opcode::Spill
|
||||
| Opcode::Fill
|
||||
| Opcode::FillNop
|
||||
| Opcode::CopyNop
|
||||
| Opcode::AdjustSpDown
|
||||
| Opcode::AdjustSpUpImm
|
||||
| Opcode::AdjustSpDownImm
|
||||
| Opcode::IfcmpSp
|
||||
| Opcode::Copy => {
|
||||
Opcode::Copy => {
|
||||
panic!("Unused opcode should not be encountered.");
|
||||
}
|
||||
|
||||
Opcode::JumpTableEntry | Opcode::JumpTableBase => {
|
||||
panic!("Should not appear: we handle BrTable directly");
|
||||
}
|
||||
|
||||
Opcode::Trapz | Opcode::Trapnz | Opcode::ResumableTrapnz => {
|
||||
panic!("trapz / trapnz / resumable_trapnz should have been removed by legalization!");
|
||||
}
|
||||
|
||||
Opcode::Jump
|
||||
| Opcode::Fallthrough
|
||||
| Opcode::Brz
|
||||
| Opcode::Brnz
|
||||
| Opcode::BrIcmp
|
||||
| Opcode::Brif
|
||||
| Opcode::Brff
|
||||
| Opcode::IndirectJumpTableBr
|
||||
| Opcode::BrTable => {
|
||||
panic!("Branch opcode reached non-branch lowering logic!");
|
||||
}
|
||||
@@ -6936,13 +6918,10 @@ impl LowerBackend for X64Backend {
|
||||
op0,
|
||||
op1
|
||||
);
|
||||
assert!(op1 == Opcode::Jump || op1 == Opcode::Fallthrough);
|
||||
assert!(op1 == Opcode::Jump);
|
||||
|
||||
let taken = targets[0];
|
||||
// not_taken target is the target of the second branch, even if it is a Fallthrough
|
||||
// instruction: because we reorder blocks while we lower, the fallthrough in the new
|
||||
// order is not (necessarily) the same as the fallthrough in CLIF. So we use the
|
||||
// explicitly-provided target.
|
||||
// not_taken target is the target of the second branch.
|
||||
let not_taken = targets[1];
|
||||
|
||||
match op0 {
|
||||
@@ -7094,23 +7073,6 @@ impl LowerBackend for X64Backend {
|
||||
let cond_code = emit_cmp(ctx, ifcmp, cond_code);
|
||||
let cc = CC::from_intcc(cond_code);
|
||||
ctx.emit(Inst::jmp_cond(cc, taken, not_taken));
|
||||
} else if let Some(ifcmp_sp) = matches_input(ctx, flag_input, Opcode::IfcmpSp) {
|
||||
let operand = put_input_in_reg(
|
||||
ctx,
|
||||
InsnInput {
|
||||
insn: ifcmp_sp,
|
||||
input: 0,
|
||||
},
|
||||
);
|
||||
let ty = ctx.input_ty(ifcmp_sp, 0);
|
||||
ctx.emit(Inst::cmp_rmi_r(
|
||||
OperandSize::from_ty(ty),
|
||||
RegMemImm::reg(regs::rsp()),
|
||||
operand,
|
||||
));
|
||||
let cond_code = ctx.data(branches[0]).cond_code().unwrap();
|
||||
let cc = CC::from_intcc(cond_code);
|
||||
ctx.emit(Inst::jmp_cond(cc, taken, not_taken));
|
||||
} else {
|
||||
// Should be disallowed by flags checks in verifier.
|
||||
unimplemented!("Brif with non-ifcmp input");
|
||||
@@ -7152,7 +7114,7 @@ impl LowerBackend for X64Backend {
|
||||
// Must be an unconditional branch or trap.
|
||||
let op = ctx.data(branches[0]).opcode();
|
||||
match op {
|
||||
Opcode::Jump | Opcode::Fallthrough => {
|
||||
Opcode::Jump => {
|
||||
ctx.emit(Inst::jmp_known(targets[0]));
|
||||
}
|
||||
|
||||
|
||||
@@ -1295,32 +1295,23 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
|
||||
pub(crate) fn visit_block_succs<F: FnMut(Inst, Block)>(f: &Function, block: Block, mut visit: F) {
|
||||
for inst in f.layout.block_likely_branches(block) {
|
||||
if f.dfg[inst].opcode().is_branch() {
|
||||
visit_branch_targets(f, block, inst, &mut visit);
|
||||
visit_branch_targets(f, inst, &mut visit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_branch_targets<F: FnMut(Inst, Block)>(
|
||||
f: &Function,
|
||||
block: Block,
|
||||
inst: Inst,
|
||||
visit: &mut F,
|
||||
) {
|
||||
if f.dfg[inst].opcode() == Opcode::Fallthrough {
|
||||
visit(inst, f.layout.next_block(block).unwrap());
|
||||
} else {
|
||||
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
|
||||
BranchInfo::NotABranch => {}
|
||||
BranchInfo::SingleDest(dest, _) => {
|
||||
fn visit_branch_targets<F: FnMut(Inst, Block)>(f: &Function, inst: Inst, visit: &mut F) {
|
||||
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
|
||||
BranchInfo::NotABranch => {}
|
||||
BranchInfo::SingleDest(dest, _) => {
|
||||
visit(inst, dest);
|
||||
}
|
||||
BranchInfo::Table(table, maybe_dest) => {
|
||||
if let Some(dest) = maybe_dest {
|
||||
visit(inst, dest);
|
||||
}
|
||||
BranchInfo::Table(table, maybe_dest) => {
|
||||
if let Some(dest) = maybe_dest {
|
||||
visit(inst, dest);
|
||||
}
|
||||
for &dest in f.jump_tables[table].as_slice() {
|
||||
visit(inst, dest);
|
||||
}
|
||||
for &dest in f.jump_tables[table].as_slice() {
|
||||
visit(inst, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,6 @@ use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
peepmatic_traits::define_parse_and_typing_rules_for_operator! {
|
||||
Opcode {
|
||||
adjust_sp_down => AdjustSpDown {
|
||||
parameters(iNN);
|
||||
result(void);
|
||||
}
|
||||
adjust_sp_down_imm => AdjustSpDownImm {
|
||||
immediates(iNN);
|
||||
result(void);
|
||||
}
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
@@ -888,10 +880,6 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
}
|
||||
|
||||
InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::AdjustSpDown,
|
||||
arg,
|
||||
}
|
||||
| InstructionData::Unary {
|
||||
opcode: opcode @ Opcode::Bint,
|
||||
arg,
|
||||
}
|
||||
@@ -920,10 +908,6 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
}
|
||||
|
||||
InstructionData::UnaryImm {
|
||||
opcode: opcode @ Opcode::AdjustSpDownImm,
|
||||
imm,
|
||||
}
|
||||
| InstructionData::UnaryImm {
|
||||
opcode: opcode @ Opcode::Iconst,
|
||||
imm,
|
||||
} => {
|
||||
@@ -949,15 +933,6 @@ unsafe impl<'a, 'b> InstructionSet<'b> for &'a dyn TargetIsa {
|
||||
|
||||
let root = root.resolve_inst(&pos.func.dfg).unwrap();
|
||||
match operator {
|
||||
Opcode::AdjustSpDown => {
|
||||
let a = part_to_value(pos, root, a).unwrap();
|
||||
pos.ins().adjust_sp_down(a).into()
|
||||
}
|
||||
Opcode::AdjustSpDownImm => {
|
||||
let c = a.unwrap_constant();
|
||||
let imm = Imm64::try_from(c).unwrap();
|
||||
pos.ins().adjust_sp_down_imm(imm).into()
|
||||
}
|
||||
Opcode::Bconst => {
|
||||
let c = a.unwrap_constant();
|
||||
let val = const_to_value(pos.ins(), c, root);
|
||||
|
||||
@@ -82,9 +82,6 @@
|
||||
(fits-in-native-word $C))
|
||||
(irsub_imm $C $x))
|
||||
|
||||
;; Unary instructions whose operand is constant.
|
||||
(=> (adjust_sp_down $C) (adjust_sp_down_imm $C))
|
||||
|
||||
;; Fold `(binop_imm $C1 (binop_imm $C2 $x))` into `(binop_imm $(binop $C2 $C1) $x)`.
|
||||
(=> (iadd_imm $C1 (iadd_imm $C2 $x)) (iadd_imm $(iadd $C1 $C2) $x))
|
||||
(=> (imul_imm $C1 (imul_imm $C2 $x)) (imul_imm $(imul $C1 $C2) $x))
|
||||
|
||||
@@ -809,15 +809,6 @@ mod simplify {
|
||||
}
|
||||
}
|
||||
|
||||
InstructionData::Unary { opcode, arg } => {
|
||||
if let Opcode::AdjustSpDown = opcode {
|
||||
if let Some(imm) = resolve_imm64_value(&pos.func.dfg, arg) {
|
||||
// Note this works for both positive and negative immediate values.
|
||||
pos.func.dfg.replace(inst).adjust_sp_down_imm(imm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstructionData::BinaryImm64 { opcode, arg, imm } => {
|
||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||
|
||||
|
||||
@@ -65,8 +65,8 @@ use crate::ir;
|
||||
use crate::ir::entities::AnyEntity;
|
||||
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint};
|
||||
use crate::ir::{
|
||||
types, ArgumentPurpose, Block, Constant, FuncRef, Function, GlobalValue, Inst, InstructionData,
|
||||
JumpTable, Opcode, SigRef, StackSlot, Type, Value, ValueDef, ValueList,
|
||||
types, ArgumentPurpose, Block, Constant, FuncRef, Function, GlobalValue, Inst, JumpTable,
|
||||
Opcode, SigRef, StackSlot, Type, Value, ValueDef, ValueList,
|
||||
};
|
||||
use crate::isa::TargetIsa;
|
||||
use crate::iterators::IteratorExtras;
|
||||
@@ -663,11 +663,6 @@ impl<'a> Verifier<'a> {
|
||||
self.verify_block(inst, destination, errors)?;
|
||||
self.verify_jump_table(inst, table, errors)?;
|
||||
}
|
||||
BranchTableBase { table, .. }
|
||||
| BranchTableEntry { table, .. }
|
||||
| IndirectJump { table, .. } => {
|
||||
self.verify_jump_table(inst, table, errors)?;
|
||||
}
|
||||
Call {
|
||||
func_ref, ref args, ..
|
||||
} => {
|
||||
@@ -1221,9 +1216,6 @@ impl<'a> Verifier<'a> {
|
||||
let _ = self.typecheck_return(inst, errors);
|
||||
let _ = self.typecheck_special(inst, ctrl_type, errors);
|
||||
|
||||
// Misuses of copy_nop instructions are fatal
|
||||
self.typecheck_copy_nop(inst, errors)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1555,36 +1547,6 @@ impl<'a> Verifier<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn typecheck_copy_nop(
|
||||
&self,
|
||||
inst: Inst,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<()> {
|
||||
if let InstructionData::Unary {
|
||||
opcode: Opcode::CopyNop,
|
||||
arg,
|
||||
} = self.func.dfg[inst]
|
||||
{
|
||||
let dst_vals = self.func.dfg.inst_results(inst);
|
||||
if dst_vals.len() != 1 {
|
||||
return errors.fatal((
|
||||
inst,
|
||||
self.context(inst),
|
||||
"copy_nop must produce exactly one result",
|
||||
));
|
||||
}
|
||||
let dst_val = dst_vals[0];
|
||||
if self.func.dfg.value_type(dst_val) != self.func.dfg.value_type(arg) {
|
||||
return errors.fatal((
|
||||
inst,
|
||||
self.context(inst),
|
||||
"copy_nop src and dst types must be the same",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cfg_integrity(
|
||||
&self,
|
||||
cfg: &ControlFlowGraph,
|
||||
@@ -1706,24 +1668,6 @@ impl<'a> Verifier<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_safepoint_unused(
|
||||
&self,
|
||||
inst: Inst,
|
||||
errors: &mut VerifierErrors,
|
||||
) -> VerifierStepResult<()> {
|
||||
if let Some(isa) = self.isa {
|
||||
if !isa.flags().enable_safepoints() && self.func.dfg[inst].opcode() == Opcode::Safepoint
|
||||
{
|
||||
return errors.fatal((
|
||||
inst,
|
||||
self.context(inst),
|
||||
"safepoint instruction cannot be used when it is not enabled.",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||
self.func
|
||||
.signature
|
||||
@@ -1787,7 +1731,6 @@ impl<'a> Verifier<'a> {
|
||||
for inst in self.func.layout.block_insts(block) {
|
||||
self.block_integrity(block, inst, errors)?;
|
||||
self.instruction_integrity(inst, errors)?;
|
||||
self.verify_safepoint_unused(inst, errors)?;
|
||||
self.typecheck(inst, errors)?;
|
||||
self.immediate_constraints(inst, errors)?;
|
||||
}
|
||||
|
||||
@@ -462,11 +462,6 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
|
||||
table,
|
||||
..
|
||||
} => write!(w, " {}, {}, {}", arg, destination, table),
|
||||
BranchTableBase { table, .. } => write!(w, " {}", table),
|
||||
BranchTableEntry {
|
||||
args, imm, table, ..
|
||||
} => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
|
||||
IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
|
||||
Call {
|
||||
func_ref, ref args, ..
|
||||
} => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
test licm
|
||||
target aarch64
|
||||
target x86_64
|
||||
|
||||
function %dont_hoist_jump_table_entry_during_licm() {
|
||||
jt0 = jump_table [block1, block1]
|
||||
|
||||
block0:
|
||||
fallthrough block1
|
||||
|
||||
block1: ; the loop!
|
||||
v2 = iconst.i32 42
|
||||
v3 = ifcmp_imm v2, 0
|
||||
brif uge v3, block1
|
||||
fallthrough block2
|
||||
|
||||
block2:
|
||||
v1 = iconst.i64 -14
|
||||
v8 = ifcmp_imm v1, 2
|
||||
brif uge v8, block1
|
||||
jump block3
|
||||
|
||||
block3:
|
||||
v5 = jump_table_base.i64 jt0
|
||||
v6 = jump_table_entry.i64 v1, v5, 4, jt0
|
||||
v7 = iadd v5, v6
|
||||
indirect_jump_table_br v7, jt0
|
||||
; check: block2:
|
||||
; nextln: v8 = ifcmp_imm.i64 v1, 2
|
||||
; nextln: brif uge v8, block1
|
||||
; nextln: jump block3
|
||||
; check: block3:
|
||||
; nextln: jump_table_entry.i64
|
||||
}
|
||||
@@ -6,10 +6,7 @@ function %rewrite_jump_table() {
|
||||
|
||||
block0:
|
||||
v0 = iconst.i64 1
|
||||
v1 = jump_table_base.i64 jt0
|
||||
v2 = jump_table_entry.i64 v0, v1, 4, jt0
|
||||
v3 = iadd v1, v2
|
||||
indirect_jump_table_br v3, jt0
|
||||
br_table v0, block1, jt0
|
||||
|
||||
block1:
|
||||
return
|
||||
|
||||
@@ -6,12 +6,12 @@ block0(v0: i32):
|
||||
v1 = icmp_imm ule v0, 2
|
||||
v2 = iconst.i32 1
|
||||
brnz v1, block3(v2) ; handle base case, n <= 2
|
||||
fallthrough block1(v0, v2)
|
||||
jump block1(v0, v2)
|
||||
|
||||
block1(v4: i32, v5:i32):
|
||||
v6 = iconst.i32 1
|
||||
v7 = iadd_imm v4, -2
|
||||
fallthrough block2(v7, v5, v6)
|
||||
jump block2(v7, v5, v6)
|
||||
|
||||
block2(v10: i32, v11: i32, v12: i32): ; params: n, fib(n-1), fib(n-2)
|
||||
v13 = iadd v11, v12
|
||||
@@ -40,7 +40,7 @@ function %fibonacci_recursive(i32) -> i32 {
|
||||
block0(v0: i32):
|
||||
v1 = icmp_imm ule v0, 2
|
||||
brnz v1, block2
|
||||
fallthrough block1(v0)
|
||||
jump block1(v0)
|
||||
|
||||
block1(v10: i32):
|
||||
v11 = iadd_imm v10, -1
|
||||
|
||||
@@ -29,18 +29,3 @@ block0:
|
||||
; check: v5 = trueif eq v4
|
||||
return v6
|
||||
}
|
||||
|
||||
function %spill() -> i32 {
|
||||
block0:
|
||||
v0 = iconst.i32 7
|
||||
v1 = spill v0
|
||||
v2 = fill v1
|
||||
v3 = spill v0
|
||||
v4 = fill v1
|
||||
v5 = bor v2, v4
|
||||
; check: v1 = spill v0
|
||||
; check: v2 = fill v1
|
||||
; check: v3 = spill v0
|
||||
; check: v4 = fill v1
|
||||
return v5
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ where
|
||||
|
||||
// Interpret a Cranelift instruction.
|
||||
Ok(match inst.opcode() {
|
||||
Opcode::Jump | Opcode::Fallthrough => ControlFlow::ContinueAt(branch(), args()?),
|
||||
Opcode::Jump => ControlFlow::ContinueAt(branch(), args()?),
|
||||
Opcode::Brz => branch_when(
|
||||
!arg(0)?
|
||||
.convert(ValueConversionKind::ToBoolean)?
|
||||
@@ -448,15 +448,6 @@ where
|
||||
assign(Value::or(mask_a, mask_b)?)
|
||||
}
|
||||
Opcode::Copy => assign(arg(0)?),
|
||||
Opcode::Spill => unimplemented!("Spill"),
|
||||
Opcode::Fill => unimplemented!("Fill"),
|
||||
Opcode::FillNop => assign(arg(0)?),
|
||||
Opcode::CopyNop => unimplemented!("CopyNop"),
|
||||
Opcode::AdjustSpDown => unimplemented!("AdjustSpDown"),
|
||||
Opcode::AdjustSpUpImm => unimplemented!("AdjustSpUpImm"),
|
||||
Opcode::AdjustSpDownImm => unimplemented!("AdjustSpDownImm"),
|
||||
Opcode::IfcmpSp => unimplemented!("IfcmpSp"),
|
||||
Opcode::Safepoint => unimplemented!("Safepoint"),
|
||||
Opcode::Icmp => assign(icmp(
|
||||
ctrl_ty,
|
||||
inst.cond_code().unwrap(),
|
||||
@@ -1026,10 +1017,6 @@ where
|
||||
assign(vectorizelanes(&new_vec, ctrl_ty)?)
|
||||
}
|
||||
Opcode::IaddPairwise => assign(binary_pairwise(arg(0)?, arg(1)?, ctrl_ty, Value::add)?),
|
||||
|
||||
Opcode::JumpTableBase | Opcode::JumpTableEntry | Opcode::IndirectJumpTableBr => {
|
||||
unimplemented!("Legacy instruction: {}", inst.opcode())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -324,15 +324,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_8() {
|
||||
interp(
|
||||
b"
|
||||
(=> (adjust_sp_down $C) (adjust_sp_down_imm $C))
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_9() {
|
||||
interp(
|
||||
|
||||
@@ -6,14 +6,6 @@
|
||||
peepmatic_traits::define_operator! {
|
||||
/// A `TOperator` type for use inside tests.
|
||||
TestOperator {
|
||||
adjust_sp_down => AdjustSpDown {
|
||||
parameters(iNN);
|
||||
result(void);
|
||||
}
|
||||
adjust_sp_down_imm => AdjustSpDownImm {
|
||||
immediates(iNN);
|
||||
result(void);
|
||||
}
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
|
||||
@@ -2676,34 +2676,6 @@ impl<'a> Parser<'a> {
|
||||
table,
|
||||
}
|
||||
}
|
||||
InstructionFormat::BranchTableBase => {
|
||||
let table = self.match_jt()?;
|
||||
ctx.check_jt(table, self.loc)?;
|
||||
InstructionData::BranchTableBase { opcode, table }
|
||||
}
|
||||
InstructionFormat::BranchTableEntry => {
|
||||
let index = self.match_value("expected SSA value operand")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
let base = self.match_value("expected SSA value operand")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
let imm = self.match_uimm8("expected width")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
let table = self.match_jt()?;
|
||||
ctx.check_jt(table, self.loc)?;
|
||||
InstructionData::BranchTableEntry {
|
||||
opcode,
|
||||
args: [index, base],
|
||||
imm,
|
||||
table,
|
||||
}
|
||||
}
|
||||
InstructionFormat::IndirectJump => {
|
||||
let arg = self.match_value("expected SSA value operand")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
let table = self.match_jt()?;
|
||||
ctx.check_jt(table, self.loc)?;
|
||||
InstructionData::IndirectJump { opcode, arg, table }
|
||||
}
|
||||
InstructionFormat::TernaryImm8 => {
|
||||
let lhs = self.match_value("expected SSA value first operand")?;
|
||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||
|
||||
Reference in New Issue
Block a user