diff --git a/docs/langref.rst b/docs/langref.rst index 507cef3cc4..47a0dae37c 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -321,6 +321,7 @@ instruction in the EBB. .. autoinst:: jump .. autoinst:: brz .. autoinst:: brnz +.. autoinst:: br_icmp .. autoinst:: br_table .. inst:: JT = jump_table EBB0, EBB1, ..., EBBn diff --git a/filetests/parser/tiny.cton b/filetests/parser/tiny.cton index eb6a28c982..d5790c0e89 100644 --- a/filetests/parser/tiny.cton +++ b/filetests/parser/tiny.cton @@ -56,6 +56,7 @@ ebb0(vx0: i32, vx1: i32): v1 = icmp ult, vx0, vx1 v2 = icmp_imm sge, vx0, -12 v3 = irsub_imm vx1, 45 + br_icmp eq, vx0, vx1, ebb0(vx1, vx0) } ; sameln: function icmp(i32, i32) { ; nextln: ebb0(vx0: i32, vx1: i32): @@ -63,6 +64,7 @@ ebb0(vx0: i32, vx1: i32): ; nextln: v1 = icmp ult, vx0, vx1 ; nextln: v2 = icmp_imm sge, vx0, -12 ; nextln: v3 = irsub_imm vx1, 45 +; nextln: br_icmp eq, vx0, vx1, ebb0(vx1, vx0) ; nextln: } ; Floating condition codes. diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index a80fa03fd9..cf1922627f 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -43,6 +43,7 @@ FloatCompare = InstructionFormat(floatcc, VALUE, VALUE) Jump = InstructionFormat(ebb, VARIABLE_ARGS) Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS) +BranchIcmp = InstructionFormat(intcc, VALUE, VALUE, ebb, VARIABLE_ARGS) BranchTable = InstructionFormat(VALUE, jump_table) Call = InstructionFormat( diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index f618be7a75..4776f576c1 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -33,6 +33,9 @@ Any = TypeVar( # Control flow # 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') args = Operand('args', VARIABLE_ARGS, doc='EBB arguments') @@ -64,6 +67,26 @@ brnz = Instruction( """, 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') JT = Operand('JT', entities.jump_table) br_table = Instruction( diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 8502954cd1..d3ca0bc62f 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -200,6 +200,13 @@ pub enum InstructionData { destination: Ebb, args: ValueList, }, + BranchIcmp { + opcode: Opcode, + ty: Type, + cond: IntCC, + destination: Ebb, + args: ValueList, + }, BranchTable { opcode: Opcode, ty: Type, @@ -303,6 +310,9 @@ impl InstructionData { &InstructionData::Branch { destination, ref args, .. } => { 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), _ => BranchInfo::NotABranch, } diff --git a/lib/cretonne/src/verifier.rs b/lib/cretonne/src/verifier.rs index 0267d297a2..589f117e73 100644 --- a/lib/cretonne/src/verifier.rs +++ b/lib/cretonne/src/verifier.rs @@ -218,11 +218,9 @@ impl<'a> Verifier<'a> { &MultiAry { ref args, .. } => { self.verify_value_list(inst, args)?; } - &Jump { destination, ref args, .. } => { - self.verify_ebb(inst, destination)?; - self.verify_value_list(inst, args)?; - } - &Branch { destination, ref args, .. } => { + &Jump { destination, ref args, .. } | + &Branch { destination, ref args, .. } | + &BranchIcmp { destination, ref args, .. } => { self.verify_ebb(inst, destination)?; self.verify_value_list(inst, args)?; } diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 23889390e7..e1ac42ac09 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -274,15 +274,19 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result } Branch { destination, ref args, .. } => { let args = args.as_slice(pool); - if args.len() == 1 { - write!(w, " {}, {}", args[0], destination) - } else { - write!(w, - " {}, {}({})", - args[0], - destination, - DisplayValues(&args[1..])) + write!(w, " {}, {}", args[0], destination)?; + if args.len() > 1 { + write!(w, "({})", 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), Call { func_ref, ref args, .. } => { diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 2e523eeb01..e8d88dde89 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -241,20 +241,14 @@ impl<'a> Context<'a> { 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_values(args.as_mut_slice(value_lists), loc)?; } - InstructionData::Branch { ref mut destination, 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::Call { ref mut args, .. } | InstructionData::IndirectCall { ref mut args, .. } => { 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), } } + 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 => { let lhs = self.match_value("expected SSA value first operand")?; self.match_token(Token::Comma, "expected ',' between operands")?;