[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:
committed by
Benjamin Bouvier
parent
cadd0ac655
commit
3418fb6e18
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user