diff --git a/cranelift/codegen/src/abi.rs b/cranelift/codegen/src/abi.rs index 883ec546e4..29fdbf4c86 100644 --- a/cranelift/codegen/src/abi.rs +++ b/cranelift/codegen/src/abi.rs @@ -4,8 +4,6 @@ //! `TargetIsa::legalize_signature()` method. use crate::ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type}; -use alloc::borrow::Cow; -use alloc::vec::Vec; use core::cmp::Ordering; /// Legalization action to perform on a single argument or return value when converting a @@ -18,10 +16,6 @@ pub enum ArgAction { /// Assign the argument to the given location. Assign(ArgumentLoc), - /// Assign the argument to the given location and change the type to the specified type. - /// This is used by [`ArgumentPurpose::StructArgument`]. - AssignAndChangeType(ArgumentLoc, Type), - /// Convert the argument, then call again. /// /// This action can split an integer type into two smaller integer arguments, or it can split a @@ -63,34 +57,6 @@ pub enum ValueConversion { Pointer(Type), } -impl ValueConversion { - /// Apply this conversion to a type, return the converted type. - pub fn apply(self, ty: Type) -> Type { - match self { - Self::IntSplit => ty.half_width().expect("Integer type too small to split"), - Self::VectorSplit => ty.half_vector().expect("Not a vector"), - Self::IntBits => Type::int(ty.bits()).expect("Bad integer size"), - Self::Sext(nty) | Self::Uext(nty) | Self::Pointer(nty) => nty, - } - } - - /// Is this a split conversion that results in two arguments? - pub fn is_split(self) -> bool { - match self { - Self::IntSplit | Self::VectorSplit => true, - _ => false, - } - } - - /// Is this a conversion to pointer? - pub fn is_pointer(self) -> bool { - match self { - Self::Pointer(_) => true, - _ => false, - } - } -} - /// Common trait for assigning arguments to registers or stack locations. /// /// This will be implemented by individual ISAs. @@ -99,62 +65,6 @@ pub trait ArgAssigner { fn assign(&mut self, arg: &AbiParam) -> ArgAction; } -/// Legalize the arguments in `args` using the given argument assigner. -/// -/// This function can be used for both arguments and return values. -pub fn legalize_args(args: &[AbiParam], aa: &mut AA) -> Option> { - let mut args = Cow::Borrowed(args); - - // Iterate over the arguments. - // We may need to mutate the vector in place, so don't use a normal iterator, and clone the - // argument to avoid holding a reference. - let mut argno = 0; - while let Some(arg) = args.get(argno).cloned() { - // Leave the pre-assigned arguments alone. - // We'll assume that they don't interfere with our assignments. - if arg.location.is_assigned() { - argno += 1; - continue; - } - - match aa.assign(&arg) { - // Assign argument to a location and move on to the next one. - ArgAction::Assign(loc) => { - args.to_mut()[argno].location = loc; - argno += 1; - } - // Assign argument to a location, change type to the requested one and move on to the - // next one. - ArgAction::AssignAndChangeType(loc, ty) => { - let arg = &mut args.to_mut()[argno]; - arg.location = loc; - arg.value_type = ty; - argno += 1; - } - // Split this argument into two smaller ones. Then revisit both. - ArgAction::Convert(conv) => { - debug_assert!( - !arg.legalized_to_pointer, - "No more conversions allowed after conversion to pointer" - ); - let value_type = conv.apply(arg.value_type); - args.to_mut()[argno].value_type = value_type; - if conv.is_pointer() { - args.to_mut()[argno].legalized_to_pointer = true; - } else if conv.is_split() { - let new_arg = AbiParam { value_type, ..arg }; - args.to_mut().insert(argno + 1, new_arg); - } - } - } - } - - match args { - Cow::Borrowed(_) => None, - Cow::Owned(a) => Some(a), - } -} - /// Determine the right action to take when passing a `have` value type to a call signature where /// the next argument is `arg` which has a different value type. /// diff --git a/cranelift/codegen/src/isa/enc_tables.rs b/cranelift/codegen/src/isa/enc_tables.rs index e21557497e..b18479bee7 100644 --- a/cranelift/codegen/src/isa/enc_tables.rs +++ b/cranelift/codegen/src/isa/enc_tables.rs @@ -3,11 +3,10 @@ //! This module contains types and functions for working with the encoding tables generated by //! `cranelift-codegen/meta/src/gen_encodings.rs`. -use crate::constant_hash::{probe, Table}; +use crate::constant_hash::Table; use crate::ir::{Function, InstructionData, Opcode, Type}; use crate::isa::{Encoding, Legalize}; use crate::settings::PredicateView; -use core::ops::Range; /// A recipe predicate. /// @@ -49,14 +48,6 @@ pub struct Level1Entry + Copy> { pub offset: OffT, } -impl + Copy> Level1Entry { - /// Get the level 2 table range indicated by this entry. - fn range(&self) -> Range { - let b = self.offset.into() as usize; - b..b + (1 << self.log2len) - } -} - impl + Copy> Table for [Level1Entry] { fn len(&self) -> usize { self.len() @@ -97,68 +88,6 @@ impl + Copy> Table for [Level2Entry] { } } -/// Two-level hash table lookup and iterator construction. -/// -/// Given the controlling type variable and instruction opcode, find the corresponding encoding -/// list. -/// -/// Returns an iterator that produces legal encodings for `inst`. -pub fn lookup_enclist<'a, OffT1, OffT2>( - ctrl_typevar: Type, - inst: &'a InstructionData, - func: &'a Function, - level1_table: &'static [Level1Entry], - level2_table: &'static [Level2Entry], - enclist: &'static [EncListEntry], - legalize_actions: &'static [Legalize], - recipe_preds: &'static [RecipePredicate], - inst_preds: &'static [InstPredicate], - isa_preds: PredicateView<'a>, -) -> Encodings<'a> -where - OffT1: Into + Copy, - OffT2: Into + Copy, -{ - let (offset, legalize) = match probe(level1_table, ctrl_typevar, ctrl_typevar.index()) { - Err(l1idx) => { - // No level 1 entry found for the type. - // We have a sentinel entry with the default legalization code. - (!0, level1_table[l1idx].legalize) - } - Ok(l1idx) => { - // We have a valid level 1 entry for this type. - let l1ent = &level1_table[l1idx]; - let offset = match level2_table.get(l1ent.range()) { - Some(l2tab) => { - let opcode = inst.opcode(); - match probe(l2tab, opcode, opcode as usize) { - Ok(l2idx) => l2tab[l2idx].offset.into() as usize, - Err(_) => !0, - } - } - // The l1ent range is invalid. This means that we just have a customized - // legalization code for this type. The level 2 table is empty. - None => !0, - }; - (offset, l1ent.legalize) - } - }; - - // Now we have an offset into `enclist` that is `!0` when no encoding list could be found. - // The default legalization code is always valid. - Encodings::new( - offset, - legalize, - inst, - func, - enclist, - legalize_actions, - recipe_preds, - inst_preds, - isa_preds, - ) -} - /// Encoding list entry. /// /// Encoding lists are represented as sequences of u16 words. diff --git a/cranelift/codegen/src/isa/unwind.rs b/cranelift/codegen/src/isa/unwind.rs index 13397c3266..4dd8ae78dd 100644 --- a/cranelift/codegen/src/isa/unwind.rs +++ b/cranelift/codegen/src/isa/unwind.rs @@ -24,77 +24,6 @@ pub enum UnwindInfo { SystemV(systemv::UnwindInfo), } -/// Intermediate representation for the unwind information -/// generated by a backend. -pub mod input { - use crate::binemit::CodeOffset; - use alloc::vec::Vec; - #[cfg(feature = "enable-serde")] - use serde::{Deserialize, Serialize}; - - /// Elementary operation in the unwind operations. - #[derive(Clone, Debug, PartialEq, Eq)] - #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] - pub enum UnwindCode { - /// Defines that a register is saved at the specified offset. - SaveRegister { - /// The saved register. - reg: Reg, - /// The specified offset relative to the stack pointer. - stack_offset: u32, - }, - /// Defines that a register is as defined before call. - RestoreRegister { - /// The restored register. - reg: Reg, - }, - /// The stack pointer was adjusted to allocate the stack. - StackAlloc { - /// Size to allocate. - size: u32, - }, - /// The stack pointer was adjusted to free the stack. - StackDealloc { - /// Size to deallocate. - size: u32, - }, - /// The alternative register was assigned as frame pointer base. - SetFramePointer { - /// The specified register. - reg: Reg, - }, - /// Restores a frame pointer base to default register. - RestoreFramePointer, - /// Saves the state. - RememberState, - /// Restores the state. - RestoreState, - /// On aarch64 ARMv8.3+ devices, enables or disables pointer authentication. - Aarch64SetPointerAuth { - /// Whether return addresses (hold in LR) contain a pointer-authentication code. - return_addresses: bool, - }, - } - - /// Unwind information as generated by a backend. - #[derive(Clone, Debug, PartialEq, Eq)] - #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] - pub struct UnwindInfo { - /// Size of the prologue. - pub prologue_size: CodeOffset, - /// Unwind codes for prologue. - pub prologue_unwind_codes: Vec<(CodeOffset, UnwindCode)>, - /// Unwind codes for epilogues. - pub epilogues_unwind_codes: Vec)>>, - /// Entire function size. - pub function_size: CodeOffset, - /// Platform word size in bytes. - pub word_size: u8, - /// Initial stack pointer offset. - pub initial_sp_offset: u8, - } -} - /// Unwind pseudoinstruction used in VCode backends: represents that /// at the present location, an action has just been taken. /// diff --git a/cranelift/codegen/src/isa/unwind/systemv.rs b/cranelift/codegen/src/isa/unwind/systemv.rs index da3bfea869..b914f13a75 100644 --- a/cranelift/codegen/src/isa/unwind/systemv.rs +++ b/cranelift/codegen/src/isa/unwind/systemv.rs @@ -1,7 +1,6 @@ //! System V ABI unwind information. use crate::binemit::CodeOffset; -use crate::isa::unwind::input; use crate::isa::unwind::UnwindInst; use crate::result::{CodegenError, CodegenResult}; use alloc::vec::Vec; @@ -259,66 +258,6 @@ pub(crate) fn create_unwind_info_from_insts>( } impl UnwindInfo { - // TODO: remove `build()` below when old backend is removed. The new backend uses a simpler - // approach in `create_unwind_info_from_insts()` above. - - pub(crate) fn build<'b, Reg: PartialEq + Copy>( - unwind: input::UnwindInfo, - map_reg: &'b dyn RegisterMapper, - ) -> CodegenResult { - use input::UnwindCode; - let mut builder = InstructionBuilder::new(unwind.initial_sp_offset, map_reg); - - for (offset, c) in unwind.prologue_unwind_codes.iter().chain( - unwind - .epilogues_unwind_codes - .iter() - .map(|c| c.iter()) - .flatten(), - ) { - match c { - UnwindCode::SaveRegister { reg, stack_offset } => { - builder - .save_reg(*offset, *reg, *stack_offset) - .map_err(CodegenError::RegisterMappingError)?; - } - UnwindCode::StackAlloc { size } => { - builder.adjust_sp_down_imm(*offset, *size as i64); - } - UnwindCode::StackDealloc { size } => { - builder.adjust_sp_up_imm(*offset, *size as i64); - } - UnwindCode::RestoreRegister { reg } => { - builder - .restore_reg(*offset, *reg) - .map_err(CodegenError::RegisterMappingError)?; - } - UnwindCode::SetFramePointer { reg } => { - builder - .set_cfa_reg(*offset, *reg) - .map_err(CodegenError::RegisterMappingError)?; - } - UnwindCode::RestoreFramePointer => { - builder.restore_cfa(*offset); - } - UnwindCode::RememberState => { - builder.remember_state(*offset); - } - UnwindCode::RestoreState => { - builder.restore_state(*offset); - } - UnwindCode::Aarch64SetPointerAuth { return_addresses } => { - builder.set_aarch64_pauth(*offset, *return_addresses); - } - } - } - - let instructions = builder.instructions; - let len = unwind.function_size; - - Ok(Self { instructions, len }) - } - /// Converts the unwind information into a `FrameDescriptionEntry`. pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry { let mut fde = FrameDescriptionEntry::new(address, self.len); @@ -330,145 +269,3 @@ impl UnwindInfo { fde } } - -// TODO: delete the builder below when the old backend is removed. - -struct InstructionBuilder<'a, Reg: PartialEq + Copy> { - sp_offset: i32, - frame_register: Option, - saved_state: Option<(i32, Option)>, - map_reg: &'a dyn RegisterMapper, - instructions: Vec<(u32, CallFrameInstruction)>, -} - -impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> { - fn new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper + 'a)) -> Self { - Self { - sp_offset: sp_offset as i32, // CFA offset starts at the specified offset to account for the return address on stack - saved_state: None, - frame_register: None, - map_reg, - instructions: Vec::new(), - } - } - - fn save_reg( - &mut self, - offset: u32, - reg: Reg, - stack_offset: u32, - ) -> Result<(), RegisterMappingError> { - // Pushes in the prologue are register saves, so record an offset of the save - self.instructions.push(( - offset, - CallFrameInstruction::Offset( - self.map_reg.map(reg)?, - stack_offset as i32 - self.sp_offset, - ), - )); - - Ok(()) - } - - fn adjust_sp_down_imm(&mut self, offset: u32, imm: i64) { - assert!(imm <= core::u32::MAX as i64); - - self.sp_offset += imm as i32; - - // Don't adjust the CFA if we're using a frame pointer - if self.frame_register.is_some() { - return; - } - - self.instructions - .push((offset, CallFrameInstruction::CfaOffset(self.sp_offset))); - } - - fn adjust_sp_up_imm(&mut self, offset: u32, imm: i64) { - assert!(imm <= core::u32::MAX as i64); - - self.sp_offset -= imm as i32; - - // Don't adjust the CFA if we're using a frame pointer - if self.frame_register.is_some() { - return; - } - - let cfa_inst_ofs = { - // Scan to find and merge with CFA instruction with the same offset. - let mut it = self.instructions.iter_mut(); - loop { - match it.next_back() { - Some((i_offset, i)) if *i_offset == offset => { - if let CallFrameInstruction::Cfa(_, o) = i { - break Some(o); - } - } - _ => { - break None; - } - } - } - }; - - if let Some(o) = cfa_inst_ofs { - // Update previous CFA instruction. - *o = self.sp_offset; - } else { - // Add just CFA offset instruction. - self.instructions - .push((offset, CallFrameInstruction::CfaOffset(self.sp_offset))); - } - } - - fn set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { - self.instructions.push(( - offset, - CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?), - )); - self.frame_register = Some(reg); - Ok(()) - } - - fn restore_cfa(&mut self, offset: u32) { - // Restore SP and its offset. - self.instructions.push(( - offset, - CallFrameInstruction::Cfa(self.map_reg.sp(), self.sp_offset), - )); - self.frame_register = None; - } - - fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { - // Pops in the epilogue are register restores, so record a "same value" for the register - self.instructions.push(( - offset, - CallFrameInstruction::SameValue(self.map_reg.map(reg)?), - )); - - Ok(()) - } - - fn remember_state(&mut self, offset: u32) { - self.saved_state = Some((self.sp_offset, self.frame_register)); - - self.instructions - .push((offset, CallFrameInstruction::RememberState)); - } - - fn restore_state(&mut self, offset: u32) { - let (sp_offset, frame_register) = self.saved_state.take().unwrap(); - self.sp_offset = sp_offset; - self.frame_register = frame_register; - - self.instructions - .push((offset, CallFrameInstruction::RestoreState)); - } - - fn set_aarch64_pauth(&mut self, offset: u32, return_addresses: bool) { - self.instructions.push(( - offset, - CallFrameInstruction::Aarch64SetPointerAuth { return_addresses }, - )); - } -} diff --git a/cranelift/codegen/src/isa/unwind/winx64.rs b/cranelift/codegen/src/isa/unwind/winx64.rs index 1c232f6855..68cb38b758 100644 --- a/cranelift/codegen/src/isa/unwind/winx64.rs +++ b/cranelift/codegen/src/isa/unwind/winx64.rs @@ -1,6 +1,5 @@ //! Windows x64 ABI unwind information. -use crate::isa::unwind::input; use crate::result::{CodegenError, CodegenResult}; use alloc::vec::Vec; use log::warn; @@ -259,76 +258,6 @@ impl UnwindInfo { .iter() .fold(0, |nodes, c| nodes + c.node_count()) } - - // TODO: remove `build()` below when old backend is removed. The new backend uses - // a simpler approach in `create_unwind_info_from_insts()` below. - - pub(crate) fn build>( - unwind: input::UnwindInfo, - ) -> CodegenResult { - use crate::isa::unwind::input::UnwindCode as InputUnwindCode; - - let word_size: u32 = unwind.word_size.into(); - let mut unwind_codes = Vec::new(); - for (offset, c) in unwind.prologue_unwind_codes.iter() { - match c { - InputUnwindCode::SaveRegister { reg, stack_offset } => { - let reg = MR::map(*reg); - let offset = ensure_unwind_offset(*offset)?; - match reg { - MappedRegister::Int(reg) => { - // Attempt to convert sequence of the `InputUnwindCode`: - // `StackAlloc { size = word_size }`, `SaveRegister { stack_offset: 0 }` - // to the shorter `UnwindCode::PushRegister`. - let push_reg_sequence = if let Some(UnwindCode::StackAlloc { - instruction_offset: alloc_offset, - size, - }) = unwind_codes.last() - { - *size == word_size && offset == *alloc_offset && *stack_offset == 0 - } else { - false - }; - if push_reg_sequence { - *unwind_codes.last_mut().unwrap() = UnwindCode::PushRegister { - instruction_offset: offset, - reg, - }; - } else { - unwind_codes.push(UnwindCode::SaveReg { - instruction_offset: offset, - reg, - stack_offset: *stack_offset, - }); - } - } - MappedRegister::Xmm(reg) => { - unwind_codes.push(UnwindCode::SaveXmm { - instruction_offset: offset, - reg, - stack_offset: *stack_offset, - }); - } - } - } - InputUnwindCode::StackAlloc { size } => { - unwind_codes.push(UnwindCode::StackAlloc { - instruction_offset: ensure_unwind_offset(*offset)?, - size: *size, - }); - } - _ => {} - } - } - - Ok(Self { - flags: 0, // this assumes cranelift functions have no SEH handlers - prologue_size: ensure_unwind_offset(unwind.prologue_size)?, - frame_register: None, - frame_register_offset: 0, - unwind_codes, - }) - } } const UNWIND_RBP_REG: u8 = 5; diff --git a/cranelift/codegen/src/legalizer/call.rs b/cranelift/codegen/src/legalizer/call.rs deleted file mode 100644 index 4321dbb90b..0000000000 --- a/cranelift/codegen/src/legalizer/call.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Legalization of calls. -//! -//! This module exports the `expand_call` function which transforms a `call` -//! instruction into `func_addr` and `call_indirect` instructions. - -use crate::cursor::{Cursor, FuncCursor}; -use crate::flowgraph::ControlFlowGraph; -use crate::ir::{self, InstBuilder}; -use crate::isa::TargetIsa; - -/// Expand a `call` instruction. This lowers it to a `call_indirect`, which -/// is only done if the ABI doesn't support direct calls. -pub fn expand_call( - inst: ir::Inst, - func: &mut ir::Function, - _cfg: &mut ControlFlowGraph, - isa: &dyn TargetIsa, -) { - // Unpack the instruction. - let (func_ref, old_args) = match func.dfg[inst] { - ir::InstructionData::Call { - opcode, - ref args, - func_ref, - } => { - debug_assert_eq!(opcode, ir::Opcode::Call); - (func_ref, args.clone()) - } - _ => panic!("Wanted call: {}", func.dfg.display_inst(inst, None)), - }; - - let ptr_ty = isa.pointer_type(); - - let sig = func.dfg.ext_funcs[func_ref].signature; - - let callee = { - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - pos.ins().func_addr(ptr_ty, func_ref) - }; - - let mut new_args = ir::ValueList::default(); - new_args.push(callee, &mut func.dfg.value_lists); - for i in 0..old_args.len(&func.dfg.value_lists) { - new_args.push( - old_args.as_slice(&func.dfg.value_lists)[i], - &mut func.dfg.value_lists, - ); - } - - func.dfg - .replace(inst) - .CallIndirect(ir::Opcode::CallIndirect, ptr_ty, sig, new_args); -} diff --git a/cranelift/codegen/src/legalizer/mod.rs b/cranelift/codegen/src/legalizer/mod.rs index 0ba70915a1..a5a248c0e7 100644 --- a/cranelift/codegen/src/legalizer/mod.rs +++ b/cranelift/codegen/src/legalizer/mod.rs @@ -15,7 +15,7 @@ use crate::cursor::{Cursor, FuncCursor}; use crate::flowgraph::ControlFlowGraph; -use crate::ir::types::{I32, I64}; +use crate::ir::types::I32; use crate::ir::{self, InstBuilder, MemFlags}; use crate::isa::TargetIsa; @@ -23,7 +23,6 @@ use crate::timing; use alloc::collections::BTreeSet; mod boundary; -mod call; mod globalvalue; mod heap; mod libcall; @@ -320,12 +319,6 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: } } -// Include legalization patterns that were generated by `gen_legalizer.rs` from the -// `TransformGroup` in `cranelift-codegen/meta/shared/legalize.rs`. -// -// Concretely, this defines private functions `narrow()`, and `expand()`. -include!(concat!(env!("OUT_DIR"), "/legalizer.rs")); - /// Custom expansion for conditional trap instructions. /// TODO: Add CFG support to the Rust DSL patterns so we won't have to do this. fn expand_cond_trap( @@ -403,189 +396,6 @@ fn expand_cond_trap( cfg.recompute_block(pos.func, new_block_trap); } -/// Jump tables. -fn expand_br_table( - inst: ir::Inst, - func: &mut ir::Function, - cfg: &mut ControlFlowGraph, - isa: &dyn TargetIsa, -) { - if isa.flags().enable_jump_tables() { - expand_br_table_jt(inst, func, cfg, isa); - } else { - expand_br_table_conds(inst, func, cfg, isa); - } -} - -/// Expand br_table to jump table. -fn expand_br_table_jt( - inst: ir::Inst, - func: &mut ir::Function, - cfg: &mut ControlFlowGraph, - isa: &dyn TargetIsa, -) { - use crate::ir::condcodes::IntCC; - - let (arg, default_block, table) = match func.dfg[inst] { - ir::InstructionData::BranchTable { - opcode: ir::Opcode::BrTable, - arg, - destination, - table, - } => (arg, destination, table), - _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)), - }; - - // Rewrite: - // - // br_table $idx, default_block, $jt - // - // To: - // - // $oob = ifcmp_imm $idx, len($jt) - // brif uge $oob, default_block - // jump fallthrough_block - // - // fallthrough_block: - // $base = jump_table_base.i64 $jt - // $rel_addr = jump_table_entry.i64 $idx, $base, 4, $jt - // $addr = iadd $base, $rel_addr - // indirect_jump_table_br $addr, $jt - - let block = func.layout.pp_block(inst); - let jump_table_block = func.dfg.make_block(); - - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - - // Bounds check. - let table_size = pos.func.jump_tables[table].len() as i64; - let oob = pos - .ins() - .icmp_imm(IntCC::UnsignedGreaterThanOrEqual, arg, table_size); - - pos.ins().brnz(oob, default_block, &[]); - pos.ins().jump(jump_table_block, &[]); - pos.insert_block(jump_table_block); - - let addr_ty = isa.pointer_type(); - - let arg = if pos.func.dfg.value_type(arg) == addr_ty { - arg - } else { - pos.ins().uextend(addr_ty, arg) - }; - - let base_addr = pos.ins().jump_table_base(addr_ty, table); - let entry = pos - .ins() - .jump_table_entry(arg, base_addr, I32.bytes() as u8, table); - - let addr = pos.ins().iadd(base_addr, entry); - pos.ins().indirect_jump_table_br(addr, table); - - pos.remove_inst(); - cfg.recompute_block(pos.func, block); - cfg.recompute_block(pos.func, jump_table_block); -} - -/// Expand br_table to series of conditionals. -fn expand_br_table_conds( - inst: ir::Inst, - func: &mut ir::Function, - cfg: &mut ControlFlowGraph, - _isa: &dyn TargetIsa, -) { - use crate::ir::condcodes::IntCC; - - let (arg, default_block, table) = match func.dfg[inst] { - ir::InstructionData::BranchTable { - opcode: ir::Opcode::BrTable, - arg, - destination, - table, - } => (arg, destination, table), - _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)), - }; - - let block = func.layout.pp_block(inst); - - // This is a poor man's jump table using just a sequence of conditional branches. - let table_size = func.jump_tables[table].len(); - let mut cond_failed_block = vec![]; - if table_size >= 1 { - cond_failed_block = alloc::vec::Vec::with_capacity(table_size - 1); - for _ in 0..table_size - 1 { - cond_failed_block.push(func.dfg.make_block()); - } - } - - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - - // Ignore the lint for this loop as the range needs to be 0 to table_size - #[allow(clippy::needless_range_loop)] - for i in 0..table_size { - let dest = pos.func.jump_tables[table].as_slice()[i]; - let t = pos.ins().icmp_imm(IntCC::Equal, arg, i as i64); - pos.ins().brnz(t, dest, &[]); - // Jump to the next case. - if i < table_size - 1 { - let block = cond_failed_block[i]; - pos.ins().jump(block, &[]); - pos.insert_block(block); - } - } - - // `br_table` jumps to the default destination if nothing matches - pos.ins().jump(default_block, &[]); - - pos.remove_inst(); - cfg.recompute_block(pos.func, block); - for failed_block in cond_failed_block.into_iter() { - cfg.recompute_block(pos.func, failed_block); - } -} - -/// 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, - _isa: &dyn TargetIsa, -) { - 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_block(tval) - // jump new_block(fval) - // new_block(result): - let old_block = func.layout.pp_block(inst); - let result = func.dfg.first_result(inst); - func.dfg.clear_results(inst); - let new_block = func.dfg.make_block(); - func.dfg.attach_block_param(new_block, result); - - func.dfg.replace(inst).brnz(ctrl, new_block, &[tval]); - let mut pos = FuncCursor::new(func).after_inst(inst); - pos.use_srcloc(inst); - pos.ins().jump(new_block, &[fval]); - pos.insert_block(new_block); - - cfg.recompute_block(pos.func, new_block); - cfg.recompute_block(pos.func, old_block); -} - fn expand_br_icmp( inst: ir::Inst, func: &mut ir::Function, @@ -620,34 +430,6 @@ fn expand_br_icmp( cfg.recompute_block(pos.func, old_block); } -/// Expand illegal `f32const` and `f64const` instructions. -fn expand_fconst( - inst: ir::Inst, - func: &mut ir::Function, - _cfg: &mut ControlFlowGraph, - _isa: &dyn TargetIsa, -) { - let ty = func.dfg.value_type(func.dfg.first_result(inst)); - debug_assert!(!ty.is_vector(), "Only scalar fconst supported: {}", ty); - - // In the future, we may want to generate constant pool entries for these constants, but for - // now use an `iconst` and a bit cast. - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - let ival = match pos.func.dfg[inst] { - ir::InstructionData::UnaryIeee32 { - opcode: ir::Opcode::F32const, - imm, - } => pos.ins().iconst(ir::types::I32, i64::from(imm.bits())), - ir::InstructionData::UnaryIeee64 { - opcode: ir::Opcode::F64const, - imm, - } => pos.ins().iconst(ir::types::I64, imm.bits() as i64), - _ => panic!("Expected fconst: {}", pos.func.dfg.display_inst(inst, None)), - }; - pos.func.dfg.replace(inst).bitcast(ty, ival); -} - /// Expand illegal `stack_load` instructions. fn expand_stack_load( inst: ir::Inst, @@ -713,171 +495,3 @@ fn expand_stack_store( mflags.set_aligned(); pos.func.dfg.replace(inst).store(mflags, val, addr, 0); } - -/// Split a load into two parts before `iconcat`ing the result together. -fn narrow_load( - inst: ir::Inst, - func: &mut ir::Function, - _cfg: &mut ControlFlowGraph, - isa: &dyn TargetIsa, -) { - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - - let (ptr, offset, flags) = match pos.func.dfg[inst] { - ir::InstructionData::Load { - opcode: ir::Opcode::Load, - arg, - offset, - flags, - } => (arg, offset, flags), - _ => panic!("Expected load: {}", pos.func.dfg.display_inst(inst, None)), - }; - - let res_ty = pos.func.dfg.ctrl_typevar(inst); - let small_ty = res_ty.half_width().expect("Can't narrow load"); - - let al = pos.ins().load(small_ty, flags, ptr, offset); - let ah = pos.ins().load( - small_ty, - flags, - ptr, - offset.try_add_i64(8).expect("load offset overflow"), - ); - let (al, ah) = match flags.endianness(isa.endianness()) { - ir::Endianness::Little => (al, ah), - ir::Endianness::Big => (ah, al), - }; - pos.func.dfg.replace(inst).iconcat(al, ah); -} - -/// Split a store into two parts after `isplit`ing the value. -fn narrow_store( - inst: ir::Inst, - func: &mut ir::Function, - _cfg: &mut ControlFlowGraph, - isa: &dyn TargetIsa, -) { - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - - let (val, ptr, offset, flags) = match pos.func.dfg[inst] { - ir::InstructionData::Store { - opcode: ir::Opcode::Store, - args, - offset, - flags, - } => (args[0], args[1], offset, flags), - _ => panic!("Expected store: {}", pos.func.dfg.display_inst(inst, None)), - }; - - let (al, ah) = pos.ins().isplit(val); - let (al, ah) = match flags.endianness(isa.endianness()) { - ir::Endianness::Little => (al, ah), - ir::Endianness::Big => (ah, al), - }; - pos.ins().store(flags, al, ptr, offset); - pos.ins().store( - flags, - ah, - ptr, - offset.try_add_i64(8).expect("store offset overflow"), - ); - pos.remove_inst(); -} - -/// Expands an illegal iconst value by splitting it into two. -fn narrow_iconst( - inst: ir::Inst, - func: &mut ir::Function, - _cfg: &mut ControlFlowGraph, - isa: &dyn TargetIsa, -) { - let imm: i64 = if let ir::InstructionData::UnaryImm { - opcode: ir::Opcode::Iconst, - imm, - } = &func.dfg[inst] - { - (*imm).into() - } else { - panic!("unexpected instruction in narrow_iconst"); - }; - - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - - let ty = pos.func.dfg.ctrl_typevar(inst); - if isa.pointer_bits() == 32 && ty == I64 { - let low = pos.ins().iconst(I32, imm & 0xffffffff); - let high = pos.ins().iconst(I32, imm >> 32); - // The instruction has as many results as iconcat, so no need to replace them. - pos.func.dfg.replace(inst).iconcat(low, high); - return; - } - - unimplemented!("missing encoding or legalization for iconst.{:?}", ty); -} - -fn narrow_icmp_imm( - inst: ir::Inst, - func: &mut ir::Function, - _cfg: &mut ControlFlowGraph, - _isa: &dyn TargetIsa, -) { - use crate::ir::condcodes::{CondCode, IntCC}; - - let (arg, cond, imm): (ir::Value, IntCC, i64) = match func.dfg[inst] { - ir::InstructionData::IntCompareImm { - opcode: ir::Opcode::IcmpImm, - arg, - cond, - imm, - } => (arg, cond, imm.into()), - _ => panic!("unexpected instruction in narrow_icmp_imm"), - }; - - let mut pos = FuncCursor::new(func).at_inst(inst); - pos.use_srcloc(inst); - - let ty = pos.func.dfg.ctrl_typevar(inst); - let ty_half = ty.half_width().unwrap(); - - let mask = ((1u128 << ty_half.bits()) - 1) as i64; - let imm_low = pos.ins().iconst(ty_half, imm & mask); - let imm_high = pos.ins().iconst( - ty_half, - imm.checked_shr(ty_half.bits().into()).unwrap_or(0) & mask, - ); - let (arg_low, arg_high) = pos.ins().isplit(arg); - - match cond { - IntCC::Equal => { - let res_low = pos.ins().icmp(cond, arg_low, imm_low); - let res_high = pos.ins().icmp(cond, arg_high, imm_high); - pos.func.dfg.replace(inst).band(res_low, res_high); - } - IntCC::NotEqual => { - let res_low = pos.ins().icmp(cond, arg_low, imm_low); - let res_high = pos.ins().icmp(cond, arg_high, imm_high); - pos.func.dfg.replace(inst).bor(res_low, res_high); - } - IntCC::SignedGreaterThan - | IntCC::SignedGreaterThanOrEqual - | IntCC::SignedLessThan - | IntCC::SignedLessThanOrEqual - | IntCC::UnsignedGreaterThan - | IntCC::UnsignedGreaterThanOrEqual - | IntCC::UnsignedLessThan - | IntCC::UnsignedLessThanOrEqual => { - let b1 = pos.ins().icmp(cond.without_equal(), arg_high, imm_high); - let b2 = pos - .ins() - .icmp(cond.inverse().without_equal(), arg_high, imm_high); - let b3 = pos.ins().icmp(cond.unsigned(), arg_low, imm_low); - let c1 = pos.ins().bnot(b2); - let c2 = pos.ins().band(c1, b3); - pos.func.dfg.replace(inst).bor(b1, c2); - } - _ => unimplemented!("missing legalization for condition {:?}", cond), - } -}