From a7eb13a1518d94c7ddbbfef4f19a7952bac21731 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 8 Dec 2017 10:10:26 -0800 Subject: [PATCH] Expand unknown instructions to runtime library calls. --- .../filetests/isa/intel/legalize-libcall.cton | 15 +++++ lib/cretonne/src/legalizer/libcall.rs | 61 +++++++++++++++++++ lib/cretonne/src/legalizer/mod.rs | 9 +++ 3 files changed, 85 insertions(+) create mode 100644 cranelift/filetests/isa/intel/legalize-libcall.cton create mode 100644 lib/cretonne/src/legalizer/libcall.rs diff --git a/cranelift/filetests/isa/intel/legalize-libcall.cton b/cranelift/filetests/isa/intel/legalize-libcall.cton new file mode 100644 index 0000000000..2d249fd8df --- /dev/null +++ b/cranelift/filetests/isa/intel/legalize-libcall.cton @@ -0,0 +1,15 @@ +test legalizer + +; Pre-SSE 4.1, we need to use runtime library calls for floating point rounding operations. +set is_64bit +isa intel + +function %floor(f32) -> f32 { +ebb0(v0: f32): + v1 = floor v0 + return v1 +} +; check: function %floor(f32 [%xmm0]) -> f32 [%xmm0] native { +; check: sig0 = (f32) -> f32 native +; check: fn0 = sig0 %FloorF32 +; check: $v1 = call fn0($v0) diff --git a/lib/cretonne/src/legalizer/libcall.rs b/lib/cretonne/src/legalizer/libcall.rs new file mode 100644 index 0000000000..156a05b813 --- /dev/null +++ b/lib/cretonne/src/legalizer/libcall.rs @@ -0,0 +1,61 @@ +//! Expanding instructions as runtime library calls. + +use ir; +use ir::InstBuilder; + +/// Try to expand `inst` as a library call, returning true is successful. +pub fn expand_as_libcall(inst: ir::Inst, func: &mut ir::Function) -> bool { + // Does the opcode/ctrl_type combo even have a well-known runtime library name. + let libcall = + match ir::LibCall::for_inst(func.dfg[inst].opcode(), func.dfg.ctrl_typevar(inst)) { + Some(lc) => lc, + None => return false, + }; + + let funcref = find_funcref(libcall, func).unwrap_or_else(|| make_funcref(libcall, inst, func)); + + // Now we convert `inst` to a call. First save the arguments. + let mut args = Vec::new(); + args.extend_from_slice(func.dfg.inst_args(inst)); + // The replace builder will preserve the instruction result values. + func.dfg.replace(inst).call(funcref, &args); + + // TODO: ask the ISA to legalize the signature. + + true +} + +/// Get the existing function reference for `libcall` in `func` if it exists. +fn find_funcref(libcall: ir::LibCall, func: &ir::Function) -> Option { + // We're assuming that all libcall function decls are at the end. + // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless. + for fref in func.dfg.ext_funcs.keys().rev() { + match func.dfg.ext_funcs[fref].name { + ir::ExternalName::LibCall(lc) => { + if lc == libcall { + return Some(fref); + } + } + _ => break, + } + } + None +} + +/// Create a funcref for `libcall` with a signature matching `inst`. +fn make_funcref(libcall: ir::LibCall, inst: ir::Inst, func: &mut ir::Function) -> ir::FuncRef { + // Start with a native calling convention. We'll give the ISA a chance to change it. + let mut sig = ir::Signature::new(ir::CallConv::Native); + for &v in func.dfg.inst_args(inst) { + sig.params.push(ir::AbiParam::new(func.dfg.value_type(v))); + } + for &v in func.dfg.inst_results(inst) { + sig.returns.push(ir::AbiParam::new(func.dfg.value_type(v))); + } + let sigref = func.import_signature(sig); + + func.import_function(ir::ExtFuncData { + name: ir::ExternalName::LibCall(libcall), + signature: sigref, + }) +} diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 2781f5fe84..8849e215e8 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -23,10 +23,12 @@ use timing; mod boundary; mod globalvar; mod heap; +mod libcall; mod split; use self::globalvar::expand_global_addr; use self::heap::expand_heap_addr; +use self::libcall::expand_as_libcall; /// Legalize `func` for `isa`. /// @@ -88,6 +90,13 @@ pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, is pos.set_position(prev_pos); continue; } + + // We don't have any pattern expansion for this instruction either. + // Try converting it to a library call as a last resort. + if expand_as_libcall(inst, pos.func) { + pos.set_position(prev_pos); + continue; + } } }