diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index 5d2b46cc73..ea060551be 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -329,6 +329,8 @@ instruction in the EBB. .. autoinst:: brz .. autoinst:: brnz .. autoinst:: br_icmp +.. autoinst:: brif +.. autoinst:: brff .. autoinst:: br_table .. inst:: JT = jump_table EBB0, EBB1, ..., EBBn @@ -722,6 +724,8 @@ Integer operations .. autoinst:: icmp .. autoinst:: icmp_imm +.. autoinst:: ifcmp +.. autoinst:: ifcmp_imm .. autoinst:: iadd .. autoinst:: iadd_imm .. autoinst:: iadd_cin @@ -814,6 +818,7 @@ Floating point operations These operations generally follow IEEE 754-2008 semantics. .. autoinst:: fcmp +.. autoinst:: ffcmp .. autoinst:: fadd .. autoinst:: fsub .. autoinst:: fmul @@ -857,6 +862,12 @@ represented as a floating point number. .. autoinst:: trunc .. autoinst:: nearest +CPU flag operations +------------------- + +.. autoinst:: trueif +.. autoinst:: trueff + Conversion operations --------------------- diff --git a/cranelift/filetests/parser/flags.cton b/cranelift/filetests/parser/flags.cton new file mode 100644 index 0000000000..a315fbba56 --- /dev/null +++ b/cranelift/filetests/parser/flags.cton @@ -0,0 +1,46 @@ +test cat +test verifier + +function %iflags(i32) { +ebb0(v0: i32): + v1 = ifcmp_imm v0, 17 + brif eq v1, ebb1 + brif ugt v1, ebb2 + v2 = iconst.i32 34 + v3 = ifcmp v0, v2 + v4 = trueif eq v3 + brnz v4, ebb2 + return + +ebb1: + return + +ebb2: + trap oob +} +; check: $v1 = ifcmp_imm $v0, 17 +; check: brif eq $v1, $ebb1 +; check: brif ugt $v1, $ebb2 +; check: $v3 = ifcmp $v0, $v2 +; check: $v4 = trueif eq $v3 + +function %fflags(f32) { +ebb0(v0: f32): + v1 = f32const 0x34.0p0 + v2 = ffcmp v0, v1 + brff eq v2, ebb1 + brff ord v2, ebb2 + v3 = trueff gt v2 + brnz v3, ebb2 + return + +ebb1: + return + +ebb2: + trap oob +} +; check: $v2 = ffcmp $v0, $v1 +; check: brff eq $v2, $ebb1 +; check: brff ord $v2, $ebb2 +; check: $v3 = trueff gt $v2 diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 0f31ff2c5f..4e1ff3500a 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -37,10 +37,14 @@ ExtractLane = InstructionFormat(VALUE, ('lane', uimm8)) IntCompare = InstructionFormat(intcc, VALUE, VALUE) IntCompareImm = InstructionFormat(intcc, VALUE, imm64) +IntCond = InstructionFormat(intcc, VALUE) FloatCompare = InstructionFormat(floatcc, VALUE, VALUE) +FloatCond = InstructionFormat(floatcc, VALUE) Jump = InstructionFormat(ebb, VARIABLE_ARGS) Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS) +BranchInt = InstructionFormat(intcc, VALUE, ebb, VARIABLE_ARGS) +BranchFloat = InstructionFormat(floatcc, VALUE, ebb, VARIABLE_ARGS) BranchIcmp = InstructionFormat(intcc, VALUE, VALUE, ebb, VARIABLE_ARGS) BranchTable = InstructionFormat(VALUE, entities.jump_table) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 6e94b4c553..48c37c280c 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -8,7 +8,7 @@ from __future__ import absolute_import from cdsl.operands import Operand, VARIABLE_ARGS from cdsl.typevar import TypeVar from cdsl.instructions import Instruction, InstructionGroup -from base.types import f32, f64, b1 +from base.types import f32, f64, b1, iflags, fflags from base.immediates import imm64, uimm8, uimm32, ieee32, ieee64, offset32 from base.immediates import boolean, intcc, floatcc, memflags, regunit from base.immediates import trapcode @@ -112,6 +112,23 @@ br_icmp = Instruction( """, ins=(Cond, x, y, EBB, args), is_branch=True) +f = Operand('f', iflags) + +brif = Instruction( + 'brif', r""" + Branch when condition is true in integer CPU flags. + """, + ins=(Cond, f, EBB, args), is_branch=True) + +Cond = Operand('Cond', floatcc) +f = Operand('f', fflags) + +brff = Instruction( + 'brff', r""" + Branch when condition is true in floating point CPU flags. + """, + ins=(Cond, f, EBB, args), is_branch=True) + x = Operand('x', iB, doc='index into jump table') JT = Operand('JT', entities.jump_table) br_table = Instruction( @@ -677,6 +694,28 @@ icmp_imm = Instruction( """, ins=(Cond, x, Y), outs=a) +f = Operand('f', iflags) +x = Operand('x', iB) +y = Operand('y', iB) + +ifcmp = Instruction( + 'ifcmp', r""" + Compare scalar integers and return flags. + + Compare two scalar integer values and return integer CPU flags + representing the result. + """, + ins=(x, y), outs=f) + +ifcmp_imm = Instruction( + 'ifcmp_imm', r""" + Compare scalar integer to a constant and return flags. + + Like :inst:`icmp_imm`, but returns integer CPU flags instead of testing + a specific condition code. + """, + ins=(x, Y), outs=f) + a = Operand('a', Int) x = Operand('x', Int) y = Operand('y', Int) @@ -1176,6 +1215,7 @@ popcnt = Instruction( Float = TypeVar( 'Float', 'A scalar or vector floating point number', floats=True, simd=True) +fB = TypeVar('fB', 'A scalar floating point number', floats=True) Cond = Operand('Cond', floatcc) x = Operand('x', Float) @@ -1246,6 +1286,17 @@ fcmp = Instruction( """, ins=(Cond, x, y), outs=a) +f = Operand('f', fflags) + +ffcmp = Instruction( + 'ffcmp', r""" + Floating point comparison returning flags. + + Compares two numbers like :inst:`fcmp`, but returns floating point CPU + flags instead of testing a specific condition. + """, + ins=(x, y), outs=f) + x = Operand('x', Float) y = Operand('y', Float) z = Operand('z', Float) @@ -1387,6 +1438,35 @@ nearest = Instruction( """, ins=x, outs=a) +# +# CPU flag operations +# + + +Cond = Operand('Cond', intcc) +f = Operand('f', iflags) +a = Operand('a', b1) + +trueif = Instruction( + 'trueif', r""" + Test integer CPU flags for a specific condition. + + Check the CPU flags in ``f`` against the ``Cond`` condition code and + return true when the condition code is satisfied. + """, + ins=(Cond, f), outs=a) + +Cond = Operand('Cond', floatcc) +f = Operand('f', fflags) + +trueff = Instruction( + 'trueff', r""" + Test floating point CPU flags for a specific condition. + + Check the CPU flags in ``f`` against the ``Cond`` condition code and + return true when the condition code is satisfied. + """, + ins=(Cond, f), outs=a) # # Conversions diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 2174bb4c99..0cb9234ed6 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -141,11 +141,21 @@ pub enum InstructionData { arg: Value, imm: Imm64, }, + IntCond { + opcode: Opcode, + cond: IntCC, + arg: Value, + }, FloatCompare { opcode: Opcode, cond: FloatCC, args: [Value; 2], }, + FloatCond { + opcode: Opcode, + cond: FloatCC, + arg: Value, + }, Jump { opcode: Opcode, destination: Ebb, @@ -162,6 +172,18 @@ pub enum InstructionData { destination: Ebb, args: ValueList, }, + BranchInt { + opcode: Opcode, + cond: IntCC, + destination: Ebb, + args: ValueList, + }, + BranchFloat { + opcode: Opcode, + cond: FloatCC, + destination: Ebb, + args: ValueList, + }, BranchTable { opcode: Opcode, arg: Value, diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index 58da610829..f143531148 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -288,6 +288,16 @@ impl<'a> Verifier<'a> { ref args, .. } | + BranchInt { + destination, + ref args, + .. + } | + BranchFloat { + destination, + ref args, + .. + } | BranchIcmp { destination, ref args, @@ -340,7 +350,9 @@ impl<'a> Verifier<'a> { ExtractLane { .. } | IntCompare { .. } | IntCompareImm { .. } | + IntCond { .. } | FloatCompare { .. } | + FloatCond { .. } | Load { .. } | Store { .. } | RegMove { .. } | diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index a1e195cd17..ebcd703aaa 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -299,22 +299,16 @@ pub fn write_operands( ExtractLane { lane, arg, .. } => write!(w, " {}, {}", arg, lane), IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), + IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), + FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), Jump { destination, ref args, .. } => { - if args.is_empty() { - write!(w, " {}", destination) - } else { - write!( - w, - " {}({})", - destination, - DisplayValues(args.as_slice(pool)) - ) - } + write!(w, " {}", destination)?; + write_ebb_args(w, args.as_slice(pool)) } Branch { destination, @@ -323,10 +317,27 @@ pub fn write_operands( } => { let args = args.as_slice(pool); write!(w, " {}, {}", args[0], destination)?; - if args.len() > 1 { - write!(w, "({})", DisplayValues(&args[1..]))?; - } - Ok(()) + write_ebb_args(w, &args[1..]) + } + BranchInt { + cond, + destination, + ref args, + .. + } => { + let args = args.as_slice(pool); + write!(w, " {} {}, {}", cond, args[0], destination)?; + write_ebb_args(w, &args[1..]) + } + BranchFloat { + cond, + destination, + ref args, + .. + } => { + let args = args.as_slice(pool); + write!(w, " {} {}, {}", cond, args[0], destination)?; + write_ebb_args(w, &args[1..]) } BranchIcmp { cond, @@ -336,10 +347,7 @@ pub fn write_operands( } => { let args = args.as_slice(pool); write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?; - if args.len() > 2 { - write!(w, "({})", DisplayValues(&args[2..]))?; - } - Ok(()) + write_ebb_args(w, &args[2..]) } BranchTable { arg, table, .. } => write!(w, " {}, {}", arg, table), Call { func_ref, ref args, .. } => { @@ -406,6 +414,15 @@ pub fn write_operands( } } +/// Write EBB args using optional parantheses. +fn write_ebb_args(w: &mut Write, args: &[Value]) -> Result { + if args.is_empty() { + Ok(()) + } else { + write!(w, "({})", DisplayValues(args)) + } +} + /// Displayable slice of values. struct DisplayValues<'a>(&'a [Value]); diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 354cb02a22..86642277c9 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -1975,6 +1975,38 @@ impl<'a> Parser<'a> { args: args.into_value_list(&[ctrl_arg], &mut ctx.function.dfg.value_lists), } } + InstructionFormat::BranchInt => { + let cond = self.match_enum("expected intcc condition code")?; + let arg = self.match_value("expected SSA value first 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::BranchInt { + opcode, + cond, + destination: ebb_num, + args: args.into_value_list(&[arg], &mut ctx.function.dfg.value_lists), + } + } + InstructionFormat::BranchFloat => { + let cond = self.match_enum("expected floatcc condition code")?; + let arg = self.match_value("expected SSA value first 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::BranchFloat { + opcode, + cond, + destination: ebb_num, + args: args.into_value_list(&[arg], &mut ctx.function.dfg.value_lists), + } + } InstructionFormat::BranchIcmp => { let cond = self.match_enum("expected intcc condition code")?; let lhs = self.match_value("expected SSA value first operand")?; @@ -1996,6 +2028,15 @@ impl<'a> Parser<'a> { args: args.into_value_list(&[lhs, rhs], &mut ctx.function.dfg.value_lists), } } + InstructionFormat::BranchTable => { + let arg = self.match_value("expected SSA value operand")?; + self.match_token( + Token::Comma, + "expected ',' between operands", + )?; + let table = self.match_jt().and_then(|num| ctx.get_jt(num, &self.loc))?; + InstructionData::BranchTable { opcode, arg, table } + } InstructionFormat::InsertLane => { let lhs = self.match_value("expected SSA value first operand")?; self.match_token( @@ -2052,6 +2093,11 @@ impl<'a> Parser<'a> { imm: rhs, } } + InstructionFormat::IntCond => { + let cond = self.match_enum("expected intcc condition code")?; + let arg = self.match_value("expected SSA value")?; + InstructionData::IntCond { opcode, cond, arg } + } InstructionFormat::FloatCompare => { let cond = self.match_enum("expected floatcc condition code")?; let lhs = self.match_value("expected SSA value first operand")?; @@ -2066,6 +2112,11 @@ impl<'a> Parser<'a> { args: [lhs, rhs], } } + InstructionFormat::FloatCond => { + let cond = self.match_enum("expected floatcc condition code")?; + let arg = self.match_value("expected SSA value")?; + InstructionData::FloatCond { opcode, cond, arg } + } InstructionFormat::Call => { let func_ref = self.match_fn("expected function reference").and_then( |num| { @@ -2121,15 +2172,6 @@ impl<'a> Parser<'a> { )?; InstructionData::FuncAddr { opcode, func_ref } } - InstructionFormat::BranchTable => { - let arg = self.match_value("expected SSA value operand")?; - self.match_token( - Token::Comma, - "expected ',' between operands", - )?; - let table = self.match_jt().and_then(|num| ctx.get_jt(num, &self.loc))?; - InstructionData::BranchTable { opcode, arg, table } - } InstructionFormat::StackLoad => { let ss = self.match_ss("expected stack slot number: ss«n»") .and_then(|num| ctx.get_ss(num, &self.loc))?;