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:
Jakob Stoklund Olesen
2017-10-12 15:21:40 -07:00
parent 15461c1e4b
commit 1f98fc491c
8 changed files with 262 additions and 28 deletions

View File

@@ -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
---------------------

View 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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 { .. } |

View File

@@ -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]);

View File

@@ -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))?;