diff --git a/cranelift/filetests/isa/intel/legalize-custom.cton b/cranelift/filetests/isa/intel/legalize-custom.cton index 39b92c6306..8c9e898586 100644 --- a/cranelift/filetests/isa/intel/legalize-custom.cton +++ b/cranelift/filetests/isa/intel/legalize-custom.cton @@ -68,3 +68,13 @@ ebb0: ; check: $v1 = bitcast.f64 $tmp return v1 } + +function %select_f64(f64, f64, i32) -> f64 { +ebb0(v0: f64, v1: f64, v2: i32): + v3 = select v2, v0, v1 + ; check: brnz v2, $(new=$EBB)($v0) + ; nextln: jump $new($v1) + ; check: $new($v3: f64): + ; nextln: return $v3 + return v3 +} diff --git a/cranelift/filetests/wasm/select.cton b/cranelift/filetests/wasm/select.cton new file mode 100644 index 0000000000..fc08c92a8a --- /dev/null +++ b/cranelift/filetests/wasm/select.cton @@ -0,0 +1,32 @@ +; Test basic code generation for the select WebAssembly instruction. +test compile + +set is_64bit=0 +isa intel haswell + +set is_64bit=1 +isa intel haswell + +function %select_i32(i32, i32, i32) -> i32 { +ebb0(v0: i32, v1: i32, v2: i32): + v3 = select v2, v0, v1 + return v3 +} + +function %select_i64(i64, i64, i32) -> i64 { +ebb0(v0: i64, v1: i64, v2: i32): + v3 = select v2, v0, v1 + return v3 +} + +function %select_f32(f32, f32, i32) -> f32 { +ebb0(v0: f32, v1: f32, v2: i32): + v3 = select v2, v0, v1 + return v3 +} + +function %select_f64(f64, f64, i32) -> f64 { +ebb0(v0: f64, v1: f64, v2: i32): + v3 = select v2, v0, v1 + return v3 +} diff --git a/lib/cretonne/meta/base/legalize.py b/lib/cretonne/meta/base/legalize.py index 8608feb392..ebcda3fee1 100644 --- a/lib/cretonne/meta/base/legalize.py +++ b/lib/cretonne/meta/base/legalize.py @@ -16,7 +16,7 @@ from .instructions import imul, imul_imm from .instructions import band, bor, bxor, isplit, iconcat from .instructions import bnot, band_not, bor_not, bxor_not from .instructions import icmp, icmp_imm -from .instructions import iconst, bint +from .instructions import iconst, bint, select from .instructions import ishl, ishl_imm, sshr, sshr_imm, ushr, ushr_imm from .instructions import rotl, rotl_imm, rotr, rotr_imm from .instructions import f32const, f64const @@ -56,6 +56,7 @@ expand.custom_legalize(insts.heap_addr, 'expand_heap_addr') expand.custom_legalize(insts.trapz, 'expand_cond_trap') expand.custom_legalize(insts.trapnz, 'expand_cond_trap') expand.custom_legalize(insts.br_table, 'expand_br_table') +expand.custom_legalize(insts.select, 'expand_select') # Custom expansions for floating point constants. # These expansions require bit-casting or creating constant pool entries. @@ -116,6 +117,16 @@ for bitop in [band, bor, bxor]: a << iconcat(al, ah) )) +narrow.legalize( + a << select(c, x, y), + Rtl( + (xl, xh) << isplit(x), + (yl, yh) << isplit(y), + al << select(c, xl, yl), + ah << select(c, xh, yh), + a << iconcat(al, ah) + )) + # Expand integer operations with carry for RISC architectures that don't have # the flags. expand.legalize( diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 61a433b6ed..a56de36a3c 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -179,6 +179,41 @@ fn expand_br_table(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlo cfg.recompute_ebb(pos.func, ebb); } +/// Expand the select instruction. +/// +/// Conditional moves are available in some ISAs for some register classes. The remaining selects +/// are handled by a branch. +fn expand_select(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph) { + let (ctrl, tval, fval) = match func.dfg[inst] { + ir::InstructionData::Ternary { + opcode: ir::Opcode::Select, + args, + } => (args[0], args[1], args[2]), + _ => panic!("Expected select: {}", func.dfg.display_inst(inst, None)), + }; + + // Replace `result = select ctrl, tval, fval` with: + // + // brnz ctrl, new_ebb(tval) + // jump new_ebb(fval) + // new_ebb(result): + let old_ebb = func.layout.pp_ebb(inst); + let result = func.dfg.first_result(inst); + func.dfg.clear_results(inst); + let new_ebb = func.dfg.make_ebb(); + func.dfg.attach_ebb_arg(new_ebb, result); + + func.dfg.replace(inst).brnz(ctrl, new_ebb, &[tval]); + let mut pos = FuncCursor::new(func).after_inst(inst); + pos.use_srcloc(inst); + pos.ins().jump(new_ebb, &[fval]); + pos.insert_ebb(new_ebb); + + cfg.recompute_ebb(pos.func, new_ebb); + cfg.recompute_ebb(pos.func, old_ebb); +} + + /// Expand illegal `f32const` and `f64const` instructions. fn expand_fconst(inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph) { let ty = func.dfg.value_type(func.dfg.first_result(inst));