diff --git a/cranelift/filetests/isa/riscv/legalize-i64.cton b/cranelift/filetests/isa/riscv/legalize-i64.cton index 2aa4be9e58..916986fe68 100644 --- a/cranelift/filetests/isa/riscv/legalize-i64.cton +++ b/cranelift/filetests/isa/riscv/legalize-i64.cton @@ -11,8 +11,10 @@ ebb0(v1: i64, v2: i64): ; regex: VX=vx\d+ ; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1 ; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2 -; check: $(v3l=$V) = band $v1l, $v2l -; check: $(v3h=$V) = band $v1h, $v2h +; check: [R#ec +; sameln: $(v3l=$V) = band $v1l, $v2l +; check: [R#ec +; sameln: $(v3h=$V) = band $v1h, $v2h ; check: $v3 = iconcat_lohi $v3l, $v3h function bitwise_or(i64, i64) -> i64 { @@ -24,8 +26,10 @@ ebb0(v1: i64, v2: i64): ; regex: VX=vx\d+ ; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1 ; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2 -; check: $(v3l=$V) = bor $v1l, $v2l -; check: $(v3h=$V) = bor $v1h, $v2h +; check: [R#cc +; sameln: $(v3l=$V) = bor $v1l, $v2l +; check: [R#cc +; sameln: $(v3h=$V) = bor $v1h, $v2h ; check: $v3 = iconcat_lohi $v3l, $v3h function bitwise_xor(i64, i64) -> i64 { @@ -37,6 +41,8 @@ ebb0(v1: i64, v2: i64): ; regex: VX=vx\d+ ; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1 ; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2 -; check: $(v3l=$V) = bxor $v1l, $v2l -; check: $(v3h=$V) = bxor $v1h, $v2h +; check: [R#8c +; sameln: $(v3l=$V) = bxor $v1l, $v2l +; check: [R#8c +; sameln: $(v3h=$V) = bxor $v1h, $v2h ; check: $v3 = iconcat_lohi $v3l, $v3h diff --git a/lib/cretonne/meta/gen_legalizer.py b/lib/cretonne/meta/gen_legalizer.py index d6b0f5eb2c..3efb120124 100644 --- a/lib/cretonne/meta/gen_legalizer.py +++ b/lib/cretonne/meta/gen_legalizer.py @@ -139,8 +139,6 @@ def gen_xform(xform, fmt): `inst: Inst` is the variable to be replaced. It is pointed to by `pos: Cursor`. `dfg: DataFlowGraph` is available and mutable. - - Produce an `Option` result at the end. """ # Unwrap the source instruction, create local variables for the input # variables. @@ -150,9 +148,6 @@ def gen_xform(xform, fmt): for dst in xform.dst.rtl: emit_dst_inst(dst, fmt) - # TODO: Return the first replacement instruction. - fmt.line('None') - def gen_xform_group(xgrp, fmt): # type: (XFormGroup, Formatter) -> None @@ -165,8 +160,7 @@ def gen_xform_group(xgrp, fmt): fmt.line('#[allow(unused_variables,unused_assignments)]') with fmt.indented( 'fn ' + xgrp.name + - '(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> ' + - 'Option {', + '(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> bool {', '}'): # Gen the instruction to be legalized. The cursor we're passed must be # pointing at an instruction. @@ -179,7 +173,8 @@ def gen_xform_group(xgrp, fmt): 'Opcode::{} => {{'.format(inst.camel_name), '}'): gen_xform(xform, fmt) # We'll assume there are uncovered opcodes. - fmt.line('_ => None,') + fmt.line('_ => return false,') + fmt.line('true') def generate(isas, out_dir): diff --git a/lib/cretonne/src/legalizer.rs b/lib/cretonne/src/legalizer.rs index 2a84ee520c..a83485b5e2 100644 --- a/lib/cretonne/src/legalizer.rs +++ b/lib/cretonne/src/legalizer.rs @@ -13,7 +13,7 @@ //! The legalizer does not deal with register allocation constraints. These constraints are derived //! from the encoding recipes, and solved later by the register allocator. -use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, Inst, InstBuilder}; +use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, InstBuilder}; use ir::condcodes::IntCC; use isa::{TargetIsa, Legalize}; @@ -27,34 +27,44 @@ pub fn legalize_function(func: &mut Function, isa: &TargetIsa) { func.encodings.resize(func.dfg.num_insts()); let mut pos = Cursor::new(&mut func.layout); while let Some(_ebb) = pos.next_ebb() { + // Keep track of the cursor position before the instruction being processed, so we can + // double back when replacing instructions. + let mut prev_pos = pos.position(); + while let Some(inst) = pos.next_inst() { match isa.encode(&func.dfg, &func.dfg[inst]) { - Ok(encoding) => func.encodings[inst] = encoding, - Err(Legalize::Expand) => { - expand(&mut pos, &mut func.dfg); + Ok(encoding) => *func.encodings.ensure(inst) = encoding, + Err(action) => { + // We should transform the instruction into legal equivalents. + // Possible strategies are: + // 1. Legalize::Expand: Expand instruction into sequence of legal instructions. + // Possibly iteratively. () + // 2. Legalize::Narrow: Split the controlling type variable into high and low + // parts. This applies both to SIMD vector types which can be halved and to + // integer types such as `i64` used on a 32-bit ISA. (). + // 3. TODO: Promote the controlling type variable to a larger type. This + // typically means expressing `i8` and `i16` arithmetic in terms if `i32` + // operations on RISC targets. (It may or may not be beneficial to promote + // small vector types versus splitting them.) + // 4. TODO: Convert to library calls. For example, floating point operations on + // an ISA with no IEEE 754 support. + let changed = match action { + Legalize::Expand => expand(&mut pos, &mut func.dfg), + Legalize::Narrow => narrow(&mut pos, &mut func.dfg), + }; + // If the current instruction was replaced, we need to double back and revisit + // the expanded sequence. This is both to assign encodings and possible to + // expand further. + // There's a risk of infinite looping here if the legalization patterns are + // unsound. Should we attempt to detect that? + if changed { + pos.set_position(prev_pos); + } } - Err(Legalize::Narrow) => { - narrow(&mut pos, &mut func.dfg); - } - // TODO: We should transform the instruction into legal equivalents. - // Possible strategies are: - // 1. Expand instruction into sequence of legal instructions. Possibly - // iteratively. - // 2. Split the controlling type variable into high and low parts. This applies - // both to SIMD vector types which can be halved and to integer types such - // as `i64` used on a 32-bit ISA. - // 3. Promote the controlling type variable to a larger type. This typically - // means expressing `i8` and `i16` arithmetic in terms if `i32` operations - // on RISC targets. (It may or may not be beneficial to promote small vector - // types versus splitting them.) - // 4. Convert to library calls. For example, floating point operations on an - // ISA with no IEEE 754 support. - // - // The iteration scheme used here is not going to cut it. Transforming - // instructions involves changing `function.layout` which is impossiblr while - // it is referenced by the two iterators. We need a layout cursor that can - // maintain a position *and* permit inserting and replacing instructions. } + + // Remember this position in case we need to double back. + prev_pos = pos.position(); } } }