[codegen] reintroduce support for carry and borrow instructions in RI… (#1005)

Reintroduce support for iadd carry variants and isub borrow variants for
RISC ISAs which had been removed in
https://github.com/CraneStation/cranelift/pull/961 and
https://github.com/CraneStation/cranelift/pull/962 because of the lack
of a proper flags register in RISC architectures.
This commit is contained in:
Ujjwal Sharma
2019-09-13 20:57:50 +05:30
committed by Benjamin Bouvier
parent cadd0ac655
commit 3418fb6e18
17 changed files with 326 additions and 85 deletions

View File

@@ -1879,10 +1879,16 @@ pub(crate) fn define(
let a = &operand("a", iB);
let x = &operand("x", iB);
let y = &operand("y", iB);
let c_in = &operand_doc("c_in", iflags, "Input carry flag");
let c_out = &operand_doc("c_out", iflags, "Output carry flag");
let b_in = &operand_doc("b_in", iflags, "Input borrow flag");
let b_out = &operand_doc("b_out", iflags, "Output borrow flag");
let c_in = &operand_doc("c_in", b1, "Input carry flag");
let c_out = &operand_doc("c_out", b1, "Output carry flag");
let b_in = &operand_doc("b_in", b1, "Input borrow flag");
let b_out = &operand_doc("b_out", b1, "Output borrow flag");
let c_if_in = &operand("c_in", iflags);
let c_if_out = &operand("c_out", iflags);
let b_if_in = &operand("b_in", iflags);
let b_if_out = &operand("b_out", iflags);
ig.push(
Inst::new(
@@ -1904,6 +1910,26 @@ pub(crate) fn define(
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"iadd_ifcin",
r#"
Add integers with carry in.
Same as `iadd` with an additional carry flag input. Computes:
```text
a = x + y + c_{in} \pmod 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
)
.operands_in(vec![x, y, c_if_in])
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"iadd_cout",
@@ -1925,6 +1951,27 @@ pub(crate) fn define(
.operands_out(vec![a, c_out]),
);
ig.push(
Inst::new(
"iadd_ifcout",
r#"
Add integers with carry out.
Same as `iadd` with an additional carry flag output.
```text
a &= x + y \pmod 2^B \\
c_{out} &= x+y >= 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
)
.operands_in(vec![x, y])
.operands_out(vec![a, c_if_out]),
);
ig.push(
Inst::new(
"iadd_carry",
@@ -1946,6 +1993,27 @@ pub(crate) fn define(
.operands_out(vec![a, c_out]),
);
ig.push(
Inst::new(
"iadd_ifcarry",
r#"
Add integers with carry in and out.
Same as `iadd` with an additional carry flag input and output.
```text
a &= x + y + c_{in} \pmod 2^B \\
c_{out} &= x + y + c_{in} >= 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
)
.operands_in(vec![x, y, c_if_in])
.operands_out(vec![a, c_if_out]),
);
ig.push(
Inst::new(
"isub_bin",
@@ -1966,6 +2034,26 @@ pub(crate) fn define(
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"isub_ifbin",
r#"
Subtract integers with borrow in.
Same as `isub` with an additional borrow flag input. Computes:
```text
a = x - (y + b_{in}) \pmod 2^B
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
)
.operands_in(vec![x, y, b_if_in])
.operands_out(vec![a]),
);
ig.push(
Inst::new(
"isub_bout",
@@ -1987,6 +2075,27 @@ pub(crate) fn define(
.operands_out(vec![a, b_out]),
);
ig.push(
Inst::new(
"isub_ifbout",
r#"
Subtract integers with borrow out.
Same as `isub` with an additional borrow flag output.
```text
a &= x - y \pmod 2^B \\
b_{out} &= x < y
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
)
.operands_in(vec![x, y])
.operands_out(vec![a, b_if_out]),
);
ig.push(
Inst::new(
"isub_borrow",
@@ -2008,6 +2117,27 @@ pub(crate) fn define(
.operands_out(vec![a, b_out]),
);
ig.push(
Inst::new(
"isub_ifborrow",
r#"
Subtract integers with borrow in and out.
Same as `isub` with an additional borrow flag input and output.
```text
a &= x - (y + b_{in}) \pmod 2^B \\
b_{out} &= x < y + b_{in}
```
Polymorphic over all scalar integer types, but does not support vector
types.
"#,
)
.operands_in(vec![x, y, b_if_in])
.operands_out(vec![a, b_if_out]),
);
let bits = &TypeVar::new(
"bits",
"Any integer, float, or boolean scalar or vector type",

View File

@@ -69,6 +69,9 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
let iadd = insts.by_name("iadd");
let iadd_cin = insts.by_name("iadd_cin");
let iadd_cout = insts.by_name("iadd_cout");
let iadd_carry = insts.by_name("iadd_carry");
let iadd_ifcin = insts.by_name("iadd_ifcin");
let iadd_ifcout = insts.by_name("iadd_ifcout");
let iadd_imm = insts.by_name("iadd_imm");
let icmp = insts.by_name("icmp");
let icmp_imm = insts.by_name("icmp_imm");
@@ -88,6 +91,9 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
let isub = insts.by_name("isub");
let isub_bin = insts.by_name("isub_bin");
let isub_bout = insts.by_name("isub_bout");
let isub_borrow = insts.by_name("isub_borrow");
let isub_ifbin = insts.by_name("isub_ifbin");
let isub_ifbout = insts.by_name("isub_ifbout");
let load = insts.by_name("load");
let popcnt = insts.by_name("popcnt");
let rotl = insts.by_name("rotl");
@@ -154,11 +160,15 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
let b2 = var("b2");
let b3 = var("b3");
let b4 = var("b4");
let b_in = var("b_in");
let b_int = var("b_int");
let c = var("c");
let c1 = var("c1");
let c2 = var("c2");
let c3 = var("c3");
let c4 = var("c4");
let c_in = var("c_in");
let c_int = var("c_int");
let d = var("d");
let d1 = var("d1");
let d2 = var("d2");
@@ -192,28 +202,6 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
// embedded as part of arguments), so use a custom legalization for now.
narrow.custom_legalize(iconst, "narrow_iconst");
narrow.legalize(
def!(a = iadd(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, c) = iadd_cout(xl, yl)),
def!(ah = iadd_cin(xh, yh, c)),
def!(a = iconcat(al, ah)),
],
);
narrow.legalize(
def!(a = isub(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, b) = isub_bout(xl, yl)),
def!(ah = isub_bin(xh, yh, b)),
def!(a = iconcat(al, ah)),
],
);
for &bin_op in &[band, bor, bxor, band_not, bor_not, bxor_not] {
narrow.legalize(
def!(a = bin_op(x, y)),
@@ -502,6 +490,58 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
}
}
// Expand integer operations with carry for RISC architectures that don't have
// the flags.
let intcc_ult = Literal::enumerator_for(&imm.intcc, "ult");
expand.legalize(
def!((a, c) = iadd_cout(x, y)),
vec![def!(a = iadd(x, y)), def!(c = icmp(intcc_ult, a, x))],
);
let intcc_ugt = Literal::enumerator_for(&imm.intcc, "ugt");
expand.legalize(
def!((a, b) = isub_bout(x, y)),
vec![def!(a = isub(x, y)), def!(b = icmp(intcc_ugt, a, x))],
);
expand.legalize(
def!(a = iadd_cin(x, y, c)),
vec![
def!(a1 = iadd(x, y)),
def!(c_int = bint(c)),
def!(a = iadd(a1, c_int)),
],
);
expand.legalize(
def!(a = isub_bin(x, y, b)),
vec![
def!(a1 = isub(x, y)),
def!(b_int = bint(b)),
def!(a = isub(a1, b_int)),
],
);
expand.legalize(
def!((a, c) = iadd_carry(x, y, c_in)),
vec![
def!((a1, c1) = iadd_cout(x, y)),
def!(c_int = bint(c_in)),
def!((a, c2) = iadd_cout(a1, c_int)),
def!(c = bor(c1, c2)),
],
);
expand.legalize(
def!((a, b) = isub_borrow(x, y, b_in)),
vec![
def!((a1, b1) = isub_bout(x, y)),
def!(b_int = bint(b_in)),
def!((a, b2) = isub_bout(a1, b_int)),
def!(b = bor(b1, b2)),
],
);
// Expansions for fcvt_from_{u,s}int for smaller integer types.
// These use expand and not widen because the controlling type variable for
// these instructions are f32/f64, which are legalized as part of the expand
@@ -758,7 +798,7 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
let mut groups = TransformGroups::new();
narrow.build_and_add_to(&mut groups);
let narrow_id = narrow.build_and_add_to(&mut groups);
let expand_id = expand.build_and_add_to(&mut groups);
// Expansions using CPU flags.
@@ -796,6 +836,82 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
expand_flags.build_and_add_to(&mut groups);
// Narrow legalizations using CPU flags.
let mut narrow_flags = TransformGroupBuilder::new(
"narrow_flags",
r#"
Narrow instructions for architectures with flags.
Narrow some instructions using CPU flags, then fall back to the normal
legalizations. Not all architectures support CPU flags, so these
patterns are kept separate.
"#,
)
.chain_with(narrow_id);
narrow_flags.legalize(
def!(a = iadd(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, c) = iadd_ifcout(xl, yl)),
def!(ah = iadd_ifcin(xh, yh, c)),
def!(a = iconcat(al, ah)),
],
);
narrow_flags.legalize(
def!(a = isub(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, b) = isub_ifbout(xl, yl)),
def!(ah = isub_ifbin(xh, yh, b)),
def!(a = iconcat(al, ah)),
],
);
narrow_flags.build_and_add_to(&mut groups);
// TODO(ryzokuken): figure out a way to legalize iadd_c* to iadd_ifc* (and
// similarly isub_b* to isub_ifb*) on expand_flags so that this isn't required.
// Narrow legalizations for ISAs that don't have CPU flags.
let mut narrow_no_flags = TransformGroupBuilder::new(
"narrow_no_flags",
r#"
Narrow instructions for architectures without flags.
Narrow some instructions avoiding the use of CPU flags, then fall back
to the normal legalizations. Not all architectures support CPU flags,
so these patterns are kept separate.
"#,
)
.chain_with(narrow_id);
narrow_no_flags.legalize(
def!(a = iadd(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, c) = iadd_cout(xl, yl)),
def!(ah = iadd_cin(xh, yh, c)),
def!(a = iconcat(al, ah)),
],
);
narrow_no_flags.legalize(
def!(a = isub(x, y)),
vec![
def!((xl, xh) = isplit(x)),
def!((yl, yh) = isplit(y)),
def!((al, b) = isub_bout(xl, yl)),
def!(ah = isub_bin(xh, yh, b)),
def!(a = iconcat(al, ah)),
],
);
narrow_no_flags.build_and_add_to(&mut groups);
// TODO The order of declarations unfortunately matters to be compatible with the Python code.
// When it's all migrated, we can put this next to the narrow/expand build_and_add_to calls
// above.