Add instructions using CPU flags.
Add integer and floating comparison instructions that return CPU flags: ifcmp, ifcmp_imm, and ffcmp. Add conditional branch instructions that check CPU flags: brif, brff Add instructions that check a condition in the CPU flags and return a b1: trueif, trueff.
This commit is contained in:
@@ -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
|
||||
---------------------
|
||||
|
||||
|
||||
46
cranelift/filetests/parser/flags.cton
Normal file
46
cranelift/filetests/parser/flags.cton
Normal file
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 { .. } |
|
||||
|
||||
@@ -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..]))?;
|
||||
write_ebb_args(w, &args[1..])
|
||||
}
|
||||
Ok(())
|
||||
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]);
|
||||
|
||||
|
||||
@@ -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))?;
|
||||
|
||||
Reference in New Issue
Block a user