Remove instructions used by old br_table legalization
This commit is contained in:
@@ -70,8 +70,6 @@ pub(crate) struct InstructionContent {
|
|||||||
pub is_terminator: bool,
|
pub is_terminator: bool,
|
||||||
/// True for all branch or jump instructions.
|
/// True for all branch or jump instructions.
|
||||||
pub is_branch: bool,
|
pub is_branch: bool,
|
||||||
/// True for all indirect branch or jump instructions.',
|
|
||||||
pub is_indirect_branch: bool,
|
|
||||||
/// Is this a call instruction?
|
/// Is this a call instruction?
|
||||||
pub is_call: bool,
|
pub is_call: bool,
|
||||||
/// Is this a return instruction?
|
/// Is this a return instruction?
|
||||||
@@ -145,7 +143,6 @@ pub(crate) struct InstructionBuilder {
|
|||||||
// See Instruction comments for the meaning of these fields.
|
// See Instruction comments for the meaning of these fields.
|
||||||
is_terminator: bool,
|
is_terminator: bool,
|
||||||
is_branch: bool,
|
is_branch: bool,
|
||||||
is_indirect_branch: bool,
|
|
||||||
is_call: bool,
|
is_call: bool,
|
||||||
is_return: bool,
|
is_return: bool,
|
||||||
is_ghost: bool,
|
is_ghost: bool,
|
||||||
@@ -168,7 +165,6 @@ impl InstructionBuilder {
|
|||||||
|
|
||||||
is_terminator: false,
|
is_terminator: false,
|
||||||
is_branch: false,
|
is_branch: false,
|
||||||
is_indirect_branch: false,
|
|
||||||
is_call: false,
|
is_call: false,
|
||||||
is_return: false,
|
is_return: false,
|
||||||
is_ghost: false,
|
is_ghost: false,
|
||||||
@@ -210,12 +206,6 @@ impl InstructionBuilder {
|
|||||||
self
|
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)]
|
#[allow(clippy::wrong_self_convention)]
|
||||||
pub fn is_call(mut self, val: bool) -> Self {
|
pub fn is_call(mut self, val: bool) -> Self {
|
||||||
self.is_call = val;
|
self.is_call = val;
|
||||||
@@ -300,7 +290,6 @@ impl InstructionBuilder {
|
|||||||
imm_opnums,
|
imm_opnums,
|
||||||
is_terminator: self.is_terminator,
|
is_terminator: self.is_terminator,
|
||||||
is_branch: self.is_branch,
|
is_branch: self.is_branch,
|
||||||
is_indirect_branch: self.is_indirect_branch,
|
|
||||||
is_call: self.is_call,
|
is_call: self.is_call,
|
||||||
is_return: self.is_return,
|
is_return: self.is_return,
|
||||||
is_ghost: self.is_ghost,
|
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.",
|
"True for all branch or jump instructions.",
|
||||||
fmt,
|
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(
|
gen_bool_accessor(
|
||||||
all_inst,
|
all_inst,
|
||||||
|inst| inst.is_call,
|
|inst| inst.is_call,
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ pub(crate) struct Formats {
|
|||||||
pub(crate) branch_icmp: Rc<InstructionFormat>,
|
pub(crate) branch_icmp: Rc<InstructionFormat>,
|
||||||
pub(crate) branch_int: Rc<InstructionFormat>,
|
pub(crate) branch_int: Rc<InstructionFormat>,
|
||||||
pub(crate) branch_table: 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: Rc<InstructionFormat>,
|
||||||
pub(crate) call_indirect: Rc<InstructionFormat>,
|
pub(crate) call_indirect: Rc<InstructionFormat>,
|
||||||
pub(crate) cond_trap: 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) float_cond_trap: Rc<InstructionFormat>,
|
||||||
pub(crate) func_addr: Rc<InstructionFormat>,
|
pub(crate) func_addr: Rc<InstructionFormat>,
|
||||||
pub(crate) heap_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: Rc<InstructionFormat>,
|
||||||
pub(crate) int_compare_imm: Rc<InstructionFormat>,
|
pub(crate) int_compare_imm: Rc<InstructionFormat>,
|
||||||
pub(crate) int_cond: Rc<InstructionFormat>,
|
pub(crate) int_cond: Rc<InstructionFormat>,
|
||||||
@@ -172,22 +169,6 @@ impl Formats {
|
|||||||
.imm(&entities.jump_table)
|
.imm(&entities.jump_table)
|
||||||
.build(),
|
.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")
|
call: Builder::new("Call")
|
||||||
.imm(&entities.func_ref)
|
.imm(&entities.func_ref)
|
||||||
.varargs()
|
.varargs()
|
||||||
|
|||||||
@@ -214,68 +214,6 @@ fn define_control_flow(
|
|||||||
TypeSetBuilder::new().ints(32..64).refs(32..64).build(),
|
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(
|
ig.push(
|
||||||
Inst::new(
|
Inst::new(
|
||||||
"debugtrap",
|
"debugtrap",
|
||||||
|
|||||||
@@ -229,7 +229,6 @@ impl InstructionData {
|
|||||||
Self::BranchTable {
|
Self::BranchTable {
|
||||||
table, destination, ..
|
table, destination, ..
|
||||||
} => BranchInfo::Table(table, Some(destination)),
|
} => BranchInfo::Table(table, Some(destination)),
|
||||||
Self::IndirectJump { table, .. } => BranchInfo::Table(table, None),
|
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!self.opcode().is_branch());
|
debug_assert!(!self.opcode().is_branch());
|
||||||
BranchInfo::NotABranch
|
BranchInfo::NotABranch
|
||||||
@@ -248,7 +247,7 @@ impl InstructionData {
|
|||||||
| Self::BranchInt { destination, .. }
|
| Self::BranchInt { destination, .. }
|
||||||
| Self::BranchFloat { destination, .. }
|
| Self::BranchFloat { destination, .. }
|
||||||
| Self::BranchIcmp { destination, .. } => Some(destination),
|
| Self::BranchIcmp { destination, .. } => Some(destination),
|
||||||
Self::BranchTable { .. } | Self::IndirectJump { .. } => None,
|
Self::BranchTable { .. } => None,
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!self.opcode().is_branch());
|
debug_assert!(!self.opcode().is_branch());
|
||||||
None
|
None
|
||||||
@@ -282,7 +281,7 @@ impl InstructionData {
|
|||||||
ref mut destination,
|
ref mut destination,
|
||||||
..
|
..
|
||||||
} => Some(destination),
|
} => Some(destination),
|
||||||
Self::BranchTable { .. } | Self::IndirectJump { .. } => None,
|
Self::BranchTable { .. } => None,
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!self.opcode().is_branch());
|
debug_assert!(!self.opcode().is_branch());
|
||||||
None
|
None
|
||||||
@@ -297,8 +296,7 @@ impl InstructionData {
|
|||||||
&InstructionData::UnaryBool { imm, .. } => Some(DataValue::from(imm)),
|
&InstructionData::UnaryBool { imm, .. } => Some(DataValue::from(imm)),
|
||||||
// 8-bit.
|
// 8-bit.
|
||||||
&InstructionData::BinaryImm8 { imm, .. }
|
&InstructionData::BinaryImm8 { imm, .. }
|
||||||
| &InstructionData::TernaryImm8 { imm, .. }
|
| &InstructionData::TernaryImm8 { imm, .. } => Some(DataValue::from(imm as i8)), // Note the switch from unsigned to signed.
|
||||||
| &InstructionData::BranchTableEntry { imm, .. } => Some(DataValue::from(imm as i8)), // Note the switch from unsigned to signed.
|
|
||||||
// 32-bit
|
// 32-bit
|
||||||
&InstructionData::UnaryIeee32 { imm, .. } => Some(DataValue::from(imm)),
|
&InstructionData::UnaryIeee32 { imm, .. } => Some(DataValue::from(imm)),
|
||||||
&InstructionData::HeapAddr { 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 => {
|
Opcode::Debugtrap => {
|
||||||
ctx.emit(Inst::Brk);
|
ctx.emit(Inst::Brk);
|
||||||
}
|
}
|
||||||
@@ -2180,7 +2176,6 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
| Opcode::BrIcmp
|
| Opcode::BrIcmp
|
||||||
| Opcode::Brif
|
| Opcode::Brif
|
||||||
| Opcode::Brff
|
| Opcode::Brff
|
||||||
| Opcode::IndirectJumpTableBr
|
|
||||||
| Opcode::BrTable => {
|
| Opcode::BrTable => {
|
||||||
panic!("Branch opcode reached non-branch lowering logic!");
|
panic!("Branch opcode reached non-branch lowering logic!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2915,14 +2915,10 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
| Opcode::BrIcmp
|
| Opcode::BrIcmp
|
||||||
| Opcode::Brif
|
| Opcode::Brif
|
||||||
| Opcode::Brff
|
| Opcode::Brff
|
||||||
| Opcode::IndirectJumpTableBr
|
|
||||||
| Opcode::BrTable => {
|
| Opcode::BrTable => {
|
||||||
panic!("Branch opcode reached non-branch lowering logic!");
|
panic!("Branch opcode reached non-branch lowering logic!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::JumpTableEntry | Opcode::JumpTableBase => {
|
|
||||||
panic!("Should not appear: we handle BrTable directly");
|
|
||||||
}
|
|
||||||
|
|
||||||
Opcode::Safepoint => {
|
Opcode::Safepoint => {
|
||||||
panic!("safepoint instructions not used by new backend's safepoints!");
|
panic!("safepoint instructions not used by new backend's safepoints!");
|
||||||
|
|||||||
@@ -6874,10 +6874,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
panic!("Unused opcode should not be encountered.");
|
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 => {
|
Opcode::Trapz | Opcode::Trapnz | Opcode::ResumableTrapnz => {
|
||||||
panic!("trapz / trapnz / resumable_trapnz should have been removed by legalization!");
|
panic!("trapz / trapnz / resumable_trapnz should have been removed by legalization!");
|
||||||
}
|
}
|
||||||
@@ -6889,7 +6885,6 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
| Opcode::BrIcmp
|
| Opcode::BrIcmp
|
||||||
| Opcode::Brif
|
| Opcode::Brif
|
||||||
| Opcode::Brff
|
| Opcode::Brff
|
||||||
| Opcode::IndirectJumpTableBr
|
|
||||||
| Opcode::BrTable => {
|
| Opcode::BrTable => {
|
||||||
panic!("Branch opcode reached non-branch lowering logic!");
|
panic!("Branch opcode reached non-branch lowering logic!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -663,11 +663,6 @@ impl<'a> Verifier<'a> {
|
|||||||
self.verify_block(inst, destination, errors)?;
|
self.verify_block(inst, destination, errors)?;
|
||||||
self.verify_jump_table(inst, table, errors)?;
|
self.verify_jump_table(inst, table, errors)?;
|
||||||
}
|
}
|
||||||
BranchTableBase { table, .. }
|
|
||||||
| BranchTableEntry { table, .. }
|
|
||||||
| IndirectJump { table, .. } => {
|
|
||||||
self.verify_jump_table(inst, table, errors)?;
|
|
||||||
}
|
|
||||||
Call {
|
Call {
|
||||||
func_ref, ref args, ..
|
func_ref, ref args, ..
|
||||||
} => {
|
} => {
|
||||||
|
|||||||
@@ -462,11 +462,6 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
|
|||||||
table,
|
table,
|
||||||
..
|
..
|
||||||
} => write!(w, " {}, {}, {}", arg, destination, 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 {
|
Call {
|
||||||
func_ref, ref args, ..
|
func_ref, ref args, ..
|
||||||
} => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
|
} => 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:
|
block0:
|
||||||
v0 = iconst.i64 1
|
v0 = iconst.i64 1
|
||||||
v1 = jump_table_base.i64 jt0
|
br_table v0, block1, jt0
|
||||||
v2 = jump_table_entry.i64 v0, v1, 4, jt0
|
|
||||||
v3 = iadd v1, v2
|
|
||||||
indirect_jump_table_br v3, jt0
|
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1026,10 +1026,6 @@ where
|
|||||||
assign(vectorizelanes(&new_vec, ctrl_ty)?)
|
assign(vectorizelanes(&new_vec, ctrl_ty)?)
|
||||||
}
|
}
|
||||||
Opcode::IaddPairwise => assign(binary_pairwise(arg(0)?, arg(1)?, ctrl_ty, Value::add)?),
|
Opcode::IaddPairwise => assign(binary_pairwise(arg(0)?, arg(1)?, ctrl_ty, Value::add)?),
|
||||||
|
|
||||||
Opcode::JumpTableBase | Opcode::JumpTableEntry | Opcode::IndirectJumpTableBr => {
|
|
||||||
unimplemented!("Legacy instruction: {}", inst.opcode())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2676,34 +2676,6 @@ impl<'a> Parser<'a> {
|
|||||||
table,
|
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 => {
|
InstructionFormat::TernaryImm8 => {
|
||||||
let lhs = self.match_value("expected SSA value first operand")?;
|
let lhs = self.match_value("expected SSA value first operand")?;
|
||||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
|
|||||||
Reference in New Issue
Block a user