[codegen] add encodings for iadd carry variants (#961)
* [codegen] add encodings for iadd carry variants Add encodings for iadd carry variants (iadd_cout, iadd_cin, iadd_carry) for x86_32, enabling the legalization for iadd.i64 to work. * [codegen] remove support for iadd carry variants on riscv Previously, the carry variants of iadd (iadd_cin, iadd_cout and iadd_carry) were being legalized for isa/riscv since RISC architectures lack a flags register. This forced us to return and accept booleans for these operations, which proved to be problematic and inconvenient, especially for x86. This commit removes support for said statements and all dependent statements for isa/riscv so that we can work on a better legalization strategy in the future. * [codegen] change operand type from bool to iflag for iadd carry variants The type of the carry operands for the carry variants of the iadd instruction (iadd_cin, iadd_cout, iadd_carry) was bool for compatibility reasons for isa/riscv. Since support for these instructions on RISC architectures has been temporarily suspended, we can safely change the type to iflags.
This commit is contained in:
committed by
Benjamin Bouvier
parent
7e398af999
commit
ea919489ee
@@ -372,6 +372,9 @@ pub fn define(
|
|||||||
let fsub = shared.by_name("fsub");
|
let fsub = shared.by_name("fsub");
|
||||||
let func_addr = shared.by_name("func_addr");
|
let func_addr = shared.by_name("func_addr");
|
||||||
let iadd = shared.by_name("iadd");
|
let iadd = shared.by_name("iadd");
|
||||||
|
let iadd_cout = shared.by_name("iadd_cout");
|
||||||
|
let iadd_cin = shared.by_name("iadd_cin");
|
||||||
|
let iadd_carry = shared.by_name("iadd_carry");
|
||||||
let iadd_imm = shared.by_name("iadd_imm");
|
let iadd_imm = shared.by_name("iadd_imm");
|
||||||
let icmp = shared.by_name("icmp");
|
let icmp = shared.by_name("icmp");
|
||||||
let icmp_imm = shared.by_name("icmp_imm");
|
let icmp_imm = shared.by_name("icmp_imm");
|
||||||
@@ -556,6 +559,8 @@ pub fn define(
|
|||||||
let rec_rfurm = r.template("rfurm");
|
let rec_rfurm = r.template("rfurm");
|
||||||
let rec_rmov = r.template("rmov");
|
let rec_rmov = r.template("rmov");
|
||||||
let rec_rr = r.template("rr");
|
let rec_rr = r.template("rr");
|
||||||
|
let rec_rcin = r.template("rcin");
|
||||||
|
let rec_rcarry = r.template("rcarry");
|
||||||
let rec_rrx = r.template("rrx");
|
let rec_rrx = r.template("rrx");
|
||||||
let rec_safepoint = r.recipe("safepoint");
|
let rec_safepoint = r.recipe("safepoint");
|
||||||
let rec_setf_abcd = r.template("setf_abcd");
|
let rec_setf_abcd = r.template("setf_abcd");
|
||||||
@@ -611,6 +616,10 @@ pub fn define(
|
|||||||
let mut e = PerCpuModeEncodings::new();
|
let mut e = PerCpuModeEncodings::new();
|
||||||
|
|
||||||
e.enc_i32_i64(iadd, rec_rr.opcodes(vec![0x01]));
|
e.enc_i32_i64(iadd, rec_rr.opcodes(vec![0x01]));
|
||||||
|
e.enc_i32_i64(iadd_cout, rec_rr.opcodes(vec![0x01]));
|
||||||
|
e.enc_i32_i64(iadd_cin, rec_rcin.opcodes(vec![0x11]));
|
||||||
|
e.enc_i32_i64(iadd_carry, rec_rcarry.opcodes(vec![0x11]));
|
||||||
|
|
||||||
e.enc_i32_i64(isub, rec_rr.opcodes(vec![0x29]));
|
e.enc_i32_i64(isub, rec_rr.opcodes(vec![0x29]));
|
||||||
e.enc_i32_i64(band, rec_rr.opcodes(vec![0x21]));
|
e.enc_i32_i64(band, rec_rr.opcodes(vec![0x21]));
|
||||||
e.enc_i32_i64(bor, rec_rr.opcodes(vec![0x09]));
|
e.enc_i32_i64(bor, rec_rr.opcodes(vec![0x09]));
|
||||||
|
|||||||
@@ -2528,6 +2528,47 @@ pub fn define<'shared>(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Adding with carry
|
||||||
|
|
||||||
|
// XX /r, MR form. Add two GPR registers and get carry flag.
|
||||||
|
recipes.add_template_recipe(
|
||||||
|
EncodingRecipeBuilder::new("rcin", f_ternary, 1)
|
||||||
|
.operands_in(vec![
|
||||||
|
OperandConstraint::RegClass(gpr),
|
||||||
|
OperandConstraint::RegClass(gpr),
|
||||||
|
OperandConstraint::FixedReg(reg_rflags),
|
||||||
|
])
|
||||||
|
.operands_out(vec![0])
|
||||||
|
.clobbers_flags(true)
|
||||||
|
.emit(
|
||||||
|
r#"
|
||||||
|
{{PUT_OP}}(bits, rex2(in_reg0, in_reg1), sink);
|
||||||
|
modrm_rr(in_reg0, in_reg1, sink);
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// XX /r, MR form. Add two GPR registers with carry flag.
|
||||||
|
recipes.add_template_recipe(
|
||||||
|
EncodingRecipeBuilder::new("rcarry", f_ternary, 1)
|
||||||
|
.operands_in(vec![
|
||||||
|
OperandConstraint::RegClass(gpr),
|
||||||
|
OperandConstraint::RegClass(gpr),
|
||||||
|
OperandConstraint::FixedReg(reg_rflags),
|
||||||
|
])
|
||||||
|
.operands_out(vec![
|
||||||
|
OperandConstraint::TiedInput(0),
|
||||||
|
OperandConstraint::FixedReg(reg_rflags),
|
||||||
|
])
|
||||||
|
.clobbers_flags(true)
|
||||||
|
.emit(
|
||||||
|
r#"
|
||||||
|
{{PUT_OP}}(bits, rex2(in_reg0, in_reg1), sink);
|
||||||
|
modrm_rr(in_reg0, in_reg1, sink);
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Compare and set flags.
|
// Compare and set flags.
|
||||||
|
|
||||||
// XX /r, MR form. Compare two GPR registers and set flags.
|
// XX /r, MR form. Compare two GPR registers and set flags.
|
||||||
|
|||||||
@@ -1864,8 +1864,8 @@ pub fn define(
|
|||||||
let a = &operand("a", iB);
|
let a = &operand("a", iB);
|
||||||
let x = &operand("x", iB);
|
let x = &operand("x", iB);
|
||||||
let y = &operand("y", iB);
|
let y = &operand("y", iB);
|
||||||
let c_in = &operand_doc("c_in", b1, "Input carry flag");
|
let c_in = &operand_doc("c_in", iflags, "Input carry flag");
|
||||||
let c_out = &operand_doc("c_out", b1, "Output carry flag");
|
let c_out = &operand_doc("c_out", iflags, "Output carry flag");
|
||||||
let b_in = &operand_doc("b_in", b1, "Input borrow flag");
|
let b_in = &operand_doc("b_in", b1, "Input borrow flag");
|
||||||
let b_out = &operand_doc("b_out", b1, "Output borrow flag");
|
let b_out = &operand_doc("b_out", b1, "Output borrow flag");
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ pub fn define(insts: &InstructionGroup, immediates: &OperandKinds) -> TransformG
|
|||||||
let fcvt_from_uint = insts.by_name("fcvt_from_uint");
|
let fcvt_from_uint = insts.by_name("fcvt_from_uint");
|
||||||
let fneg = insts.by_name("fneg");
|
let fneg = insts.by_name("fneg");
|
||||||
let iadd = insts.by_name("iadd");
|
let iadd = insts.by_name("iadd");
|
||||||
let iadd_carry = insts.by_name("iadd_carry");
|
|
||||||
let iadd_cin = insts.by_name("iadd_cin");
|
let iadd_cin = insts.by_name("iadd_cin");
|
||||||
let iadd_cout = insts.by_name("iadd_cout");
|
let iadd_cout = insts.by_name("iadd_cout");
|
||||||
let iadd_imm = insts.by_name("iadd_imm");
|
let iadd_imm = insts.by_name("iadd_imm");
|
||||||
@@ -168,8 +167,6 @@ pub fn define(insts: &InstructionGroup, immediates: &OperandKinds) -> TransformG
|
|||||||
let c2 = var("c2");
|
let c2 = var("c2");
|
||||||
let c3 = var("c3");
|
let c3 = var("c3");
|
||||||
let c4 = var("c4");
|
let c4 = var("c4");
|
||||||
let c_in = var("c_in");
|
|
||||||
let c_int = var("c_int");
|
|
||||||
let d = var("d");
|
let d = var("d");
|
||||||
let d1 = var("d1");
|
let d1 = var("d1");
|
||||||
let d2 = var("d2");
|
let d2 = var("d2");
|
||||||
@@ -464,27 +461,12 @@ pub fn define(insts: &InstructionGroup, immediates: &OperandKinds) -> TransformG
|
|||||||
|
|
||||||
// Expand integer operations with carry for RISC architectures that don't have
|
// Expand integer operations with carry for RISC architectures that don't have
|
||||||
// the flags.
|
// the flags.
|
||||||
let intcc_ult = Literal::enumerator_for(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(intcc, "ugt");
|
let intcc_ugt = Literal::enumerator_for(intcc, "ugt");
|
||||||
expand.legalize(
|
expand.legalize(
|
||||||
def!((a, b) = isub_bout(x, y)),
|
def!((a, b) = isub_bout(x, y)),
|
||||||
vec![def!(a = isub(x, y)), def!(b = icmp(intcc_ugt, a, x))],
|
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(
|
expand.legalize(
|
||||||
def!(a = isub_bin(x, y, b)),
|
def!(a = isub_bin(x, y, b)),
|
||||||
vec![
|
vec![
|
||||||
@@ -494,16 +476,6 @@ pub fn define(insts: &InstructionGroup, immediates: &OperandKinds) -> TransformG
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
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(
|
expand.legalize(
|
||||||
def!((a, b) = isub_borrow(x, y, b_in)),
|
def!((a, b) = isub_borrow(x, y, b_in)),
|
||||||
vec![
|
vec![
|
||||||
|
|||||||
@@ -1239,7 +1239,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aliases() {
|
fn aliases() {
|
||||||
use crate::ir::condcodes::IntCC;
|
|
||||||
use crate::ir::InstBuilder;
|
use crate::ir::InstBuilder;
|
||||||
|
|
||||||
let mut func = Function::new();
|
let mut func = Function::new();
|
||||||
@@ -1264,9 +1263,9 @@ mod tests {
|
|||||||
pos.func.dfg.clear_results(iadd);
|
pos.func.dfg.clear_results(iadd);
|
||||||
pos.func.dfg.attach_result(iadd, s);
|
pos.func.dfg.attach_result(iadd, s);
|
||||||
|
|
||||||
// Replace `iadd_cout` with a normal `iadd` and an `icmp`.
|
// Replace `iadd_cout` with a normal `iadd` and an `ifcmp`.
|
||||||
pos.func.dfg.replace(iadd).iadd(v1, arg0);
|
pos.func.dfg.replace(iadd).iadd(v1, arg0);
|
||||||
let c2 = pos.ins().icmp(IntCC::UnsignedLessThan, s, v1);
|
let c2 = pos.ins().ifcmp(s, v1);
|
||||||
pos.func.dfg.change_to_alias(c, c2);
|
pos.func.dfg.change_to_alias(c, c2);
|
||||||
|
|
||||||
assert_eq!(pos.func.dfg.resolve_aliases(c2), c2);
|
assert_eq!(pos.func.dfg.resolve_aliases(c2), c2);
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
; TODO(ryzokuken): figure out a better legalization strategy for platforms that
|
||||||
|
; platforms that don't have flags.
|
||||||
|
|
||||||
; Test the legalization of i32 instructions that don't have RISC-V versions.
|
; Test the legalization of i32 instructions that don't have RISC-V versions.
|
||||||
test legalizer
|
test legalizer
|
||||||
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
; TODO(ryzokuken): figure out a better legalization strategy for platforms that
|
||||||
|
; platforms that don't have flags.
|
||||||
|
|
||||||
; Test the legalization of i64 arithmetic instructions.
|
; Test the legalization of i64 arithmetic instructions.
|
||||||
test legalizer
|
test legalizer
|
||||||
target riscv32 supports_m=1
|
target riscv32 supports_m=1
|
||||||
@@ -469,6 +469,14 @@ ebb0:
|
|||||||
; asm: mov %cl,(%eax,%ebx,1)
|
; asm: mov %cl,(%eax,%ebx,1)
|
||||||
istore8_complex v601, v521+v522 ; bin: heap_oob 88 0c 18
|
istore8_complex v601, v521+v522 ; bin: heap_oob 88 0c 18
|
||||||
|
|
||||||
|
; Carry Addition
|
||||||
|
; asm: addl %esi, %ecx
|
||||||
|
[-,%rcx,%rflags] v701, v702 = iadd_cout v1, v2 ; bin: 01 f1
|
||||||
|
; asm: adcl %esi, %ecx
|
||||||
|
[-,%rcx] v703 = iadd_cin v1, v2, v702 ; bin: 11 f1
|
||||||
|
; asm: adcl %esi, %ecx
|
||||||
|
[-,%rcx,%rflags] v704, v705 = iadd_carry v1, v2, v702 ; bin: 11 f1
|
||||||
|
|
||||||
; asm: testl %ecx, %ecx
|
; asm: testl %ecx, %ecx
|
||||||
; asm: je ebb1
|
; asm: je ebb1
|
||||||
brz v1, ebb1 ; bin: 85 c9 74 0e
|
brz v1, ebb1 ; bin: 85 c9 74 0e
|
||||||
|
|||||||
16
cranelift/filetests/filetests/isa/x86/legalize-i64.clif
Normal file
16
cranelift/filetests/filetests/isa/x86/legalize-i64.clif
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
; Test the legalization of i64 instructions on x86_32.
|
||||||
|
test legalizer
|
||||||
|
target i686 haswell
|
||||||
|
|
||||||
|
; regex: V=v\d+
|
||||||
|
|
||||||
|
function %iadd(i64, i64) -> i64 {
|
||||||
|
ebb0(v1: i64, v2: i64):
|
||||||
|
v10 = iadd v1, v2
|
||||||
|
; check: v1 = iconcat $(v1_lsb=$V), $(v1_msb=$V)
|
||||||
|
; check: v2 = iconcat $(v2_lsb=$V), $(v2_msb=$V)
|
||||||
|
; check: $(v10_lsb=$V), $(carry=$V) = iadd_cout $v1_lsb, $v2_lsb
|
||||||
|
; check: $(v10_msb=$V) = iadd_cin $v1_msb, $v2_msb, $carry
|
||||||
|
; check: v10 = iconcat $v10_lsb, $v10_msb
|
||||||
|
return v10
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user