Add the br_icmp instruction.
This instruction behaves like icmp fused with brnz, and it can be used to represent fused compare+branch instruction on Intel when optimizing for macro-op fusion. RISC-V provides compare-and-branch instructions directly, and it is needed there too.
This commit is contained in:
@@ -321,6 +321,7 @@ instruction in the EBB.
|
|||||||
.. autoinst:: jump
|
.. autoinst:: jump
|
||||||
.. autoinst:: brz
|
.. autoinst:: brz
|
||||||
.. autoinst:: brnz
|
.. autoinst:: brnz
|
||||||
|
.. autoinst:: br_icmp
|
||||||
.. autoinst:: br_table
|
.. autoinst:: br_table
|
||||||
|
|
||||||
.. inst:: JT = jump_table EBB0, EBB1, ..., EBBn
|
.. inst:: JT = jump_table EBB0, EBB1, ..., EBBn
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ ebb0(vx0: i32, vx1: i32):
|
|||||||
v1 = icmp ult, vx0, vx1
|
v1 = icmp ult, vx0, vx1
|
||||||
v2 = icmp_imm sge, vx0, -12
|
v2 = icmp_imm sge, vx0, -12
|
||||||
v3 = irsub_imm vx1, 45
|
v3 = irsub_imm vx1, 45
|
||||||
|
br_icmp eq, vx0, vx1, ebb0(vx1, vx0)
|
||||||
}
|
}
|
||||||
; sameln: function icmp(i32, i32) {
|
; sameln: function icmp(i32, i32) {
|
||||||
; nextln: ebb0(vx0: i32, vx1: i32):
|
; nextln: ebb0(vx0: i32, vx1: i32):
|
||||||
@@ -63,6 +64,7 @@ ebb0(vx0: i32, vx1: i32):
|
|||||||
; nextln: v1 = icmp ult, vx0, vx1
|
; nextln: v1 = icmp ult, vx0, vx1
|
||||||
; nextln: v2 = icmp_imm sge, vx0, -12
|
; nextln: v2 = icmp_imm sge, vx0, -12
|
||||||
; nextln: v3 = irsub_imm vx1, 45
|
; nextln: v3 = irsub_imm vx1, 45
|
||||||
|
; nextln: br_icmp eq, vx0, vx1, ebb0(vx1, vx0)
|
||||||
; nextln: }
|
; nextln: }
|
||||||
|
|
||||||
; Floating condition codes.
|
; Floating condition codes.
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
|
|||||||
|
|
||||||
Jump = InstructionFormat(ebb, VARIABLE_ARGS)
|
Jump = InstructionFormat(ebb, VARIABLE_ARGS)
|
||||||
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS)
|
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS)
|
||||||
|
BranchIcmp = InstructionFormat(intcc, VALUE, VALUE, ebb, VARIABLE_ARGS)
|
||||||
BranchTable = InstructionFormat(VALUE, jump_table)
|
BranchTable = InstructionFormat(VALUE, jump_table)
|
||||||
|
|
||||||
Call = InstructionFormat(
|
Call = InstructionFormat(
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ Any = TypeVar(
|
|||||||
# Control flow
|
# Control flow
|
||||||
#
|
#
|
||||||
c = Operand('c', Testable, doc='Controlling value to test')
|
c = Operand('c', Testable, doc='Controlling value to test')
|
||||||
|
Cond = Operand('Cond', intcc)
|
||||||
|
x = Operand('x', iB)
|
||||||
|
y = Operand('y', iB)
|
||||||
EBB = Operand('EBB', entities.ebb, doc='Destination extended basic block')
|
EBB = Operand('EBB', entities.ebb, doc='Destination extended basic block')
|
||||||
args = Operand('args', VARIABLE_ARGS, doc='EBB arguments')
|
args = Operand('args', VARIABLE_ARGS, doc='EBB arguments')
|
||||||
|
|
||||||
@@ -64,6 +67,26 @@ brnz = Instruction(
|
|||||||
""",
|
""",
|
||||||
ins=(c, EBB, args), is_branch=True)
|
ins=(c, EBB, args), is_branch=True)
|
||||||
|
|
||||||
|
br_icmp = Instruction(
|
||||||
|
'br_icmp', r"""
|
||||||
|
Compare scalar integers and branch.
|
||||||
|
|
||||||
|
Compare ``x`` and ``y`` in the same way as the :inst:`icmp` instruction
|
||||||
|
and take the branch if the condition is true::
|
||||||
|
|
||||||
|
br_icmp ugt v1, v2, ebb4(v5, v6)
|
||||||
|
|
||||||
|
is semantically equivalent to::
|
||||||
|
|
||||||
|
v10 = icmp ugt, v1, v2
|
||||||
|
brnz v10, ebb4(v5, v6)
|
||||||
|
|
||||||
|
Some RISC architectures like MIPS and RISC-V provide instructions that
|
||||||
|
implement all or some of the condition codes. The instruction can also
|
||||||
|
be used to represent *macro-op fusion* on architectures like Intel's.
|
||||||
|
""",
|
||||||
|
ins=(Cond, x, y, EBB, args), is_branch=True)
|
||||||
|
|
||||||
x = Operand('x', iB, doc='index into jump table')
|
x = Operand('x', iB, doc='index into jump table')
|
||||||
JT = Operand('JT', entities.jump_table)
|
JT = Operand('JT', entities.jump_table)
|
||||||
br_table = Instruction(
|
br_table = Instruction(
|
||||||
|
|||||||
@@ -200,6 +200,13 @@ pub enum InstructionData {
|
|||||||
destination: Ebb,
|
destination: Ebb,
|
||||||
args: ValueList,
|
args: ValueList,
|
||||||
},
|
},
|
||||||
|
BranchIcmp {
|
||||||
|
opcode: Opcode,
|
||||||
|
ty: Type,
|
||||||
|
cond: IntCC,
|
||||||
|
destination: Ebb,
|
||||||
|
args: ValueList,
|
||||||
|
},
|
||||||
BranchTable {
|
BranchTable {
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
@@ -303,6 +310,9 @@ impl InstructionData {
|
|||||||
&InstructionData::Branch { destination, ref args, .. } => {
|
&InstructionData::Branch { destination, ref args, .. } => {
|
||||||
BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..])
|
BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..])
|
||||||
}
|
}
|
||||||
|
&InstructionData::BranchIcmp { destination, ref args, .. } => {
|
||||||
|
BranchInfo::SingleDest(destination, &args.as_slice(pool)[2..])
|
||||||
|
}
|
||||||
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
|
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
|
||||||
_ => BranchInfo::NotABranch,
|
_ => BranchInfo::NotABranch,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,11 +218,9 @@ impl<'a> Verifier<'a> {
|
|||||||
&MultiAry { ref args, .. } => {
|
&MultiAry { ref args, .. } => {
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
&Jump { destination, ref args, .. } => {
|
&Jump { destination, ref args, .. } |
|
||||||
self.verify_ebb(inst, destination)?;
|
&Branch { destination, ref args, .. } |
|
||||||
self.verify_value_list(inst, args)?;
|
&BranchIcmp { destination, ref args, .. } => {
|
||||||
}
|
|
||||||
&Branch { destination, ref args, .. } => {
|
|
||||||
self.verify_ebb(inst, destination)?;
|
self.verify_ebb(inst, destination)?;
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -274,15 +274,19 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
|
|||||||
}
|
}
|
||||||
Branch { destination, ref args, .. } => {
|
Branch { destination, ref args, .. } => {
|
||||||
let args = args.as_slice(pool);
|
let args = args.as_slice(pool);
|
||||||
if args.len() == 1 {
|
write!(w, " {}, {}", args[0], destination)?;
|
||||||
write!(w, " {}, {}", args[0], destination)
|
if args.len() > 1 {
|
||||||
} else {
|
write!(w, "({})", DisplayValues(&args[1..]))?;
|
||||||
write!(w,
|
|
||||||
" {}, {}({})",
|
|
||||||
args[0],
|
|
||||||
destination,
|
|
||||||
DisplayValues(&args[1..]))
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
BranchIcmp { cond, destination, ref args, .. } => {
|
||||||
|
let args = args.as_slice(pool);
|
||||||
|
write!(w, " {}, {}, {}, {}", cond, args[0], args[1], destination)?;
|
||||||
|
if args.len() > 2 {
|
||||||
|
write!(w, "({})", DisplayValues(&args[2..]))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
BranchTable { arg, table, .. } => write!(w, " {}, {}", arg, table),
|
BranchTable { arg, table, .. } => write!(w, " {}, {}", arg, table),
|
||||||
Call { func_ref, ref args, .. } => {
|
Call { func_ref, ref args, .. } => {
|
||||||
|
|||||||
@@ -241,20 +241,14 @@ impl<'a> Context<'a> {
|
|||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionData::Jump { ref mut destination, ref mut args, .. } => {
|
InstructionData::Jump { ref mut destination, ref mut args, .. } |
|
||||||
|
InstructionData::Branch { ref mut destination, ref mut args, .. } |
|
||||||
|
InstructionData::BranchIcmp { ref mut destination, ref mut args, .. } => {
|
||||||
self.map.rewrite_ebb(destination, loc)?;
|
self.map.rewrite_ebb(destination, loc)?;
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionData::Branch { ref mut destination, ref mut args, .. } => {
|
InstructionData::Call { ref mut args, .. } |
|
||||||
self.map.rewrite_ebb(destination, loc)?;
|
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionData::Call { ref mut args, .. } => {
|
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionData::IndirectCall { ref mut args, .. } => {
|
InstructionData::IndirectCall { ref mut args, .. } => {
|
||||||
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
self.map.rewrite_values(args.as_mut_slice(value_lists), loc)?;
|
||||||
}
|
}
|
||||||
@@ -1521,6 +1515,23 @@ impl<'a> Parser<'a> {
|
|||||||
args: args.into_value_list(&[ctrl_arg], &mut ctx.function.dfg.value_lists),
|
args: args.into_value_list(&[ctrl_arg], &mut ctx.function.dfg.value_lists),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InstructionFormat::BranchIcmp => {
|
||||||
|
let cond = self.match_enum("expected intcc condition code")?;
|
||||||
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
|
let lhs = self.match_value("expected SSA value first operand")?;
|
||||||
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
|
let rhs = self.match_value("expected SSA value second operand")?;
|
||||||
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
|
let ebb_num = self.match_ebb("expected branch destination EBB")?;
|
||||||
|
let args = self.parse_opt_value_list()?;
|
||||||
|
InstructionData::BranchIcmp {
|
||||||
|
opcode: opcode,
|
||||||
|
ty: VOID,
|
||||||
|
cond: cond,
|
||||||
|
destination: ebb_num,
|
||||||
|
args: args.into_value_list(&[lhs, rhs], &mut ctx.function.dfg.value_lists),
|
||||||
|
}
|
||||||
|
}
|
||||||
InstructionFormat::InsertLane => {
|
InstructionFormat::InsertLane => {
|
||||||
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