Fix all dead-code warnings in cranelift-codegen

This commit is contained in:
bjorn3
2021-06-21 13:11:28 +02:00
parent 3e4167ba95
commit 9e5201d88f
7 changed files with 2 additions and 948 deletions

View File

@@ -4,8 +4,6 @@
//! `TargetIsa::legalize_signature()` method. //! `TargetIsa::legalize_signature()` method.
use crate::ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type}; use crate::ir::{AbiParam, ArgumentExtension, ArgumentLoc, Type};
use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::cmp::Ordering; use core::cmp::Ordering;
/// Legalization action to perform on a single argument or return value when converting a /// 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 the argument to the given location.
Assign(ArgumentLoc), 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. /// Convert the argument, then call again.
/// ///
/// This action can split an integer type into two smaller integer arguments, or it can split a /// 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), 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. /// Common trait for assigning arguments to registers or stack locations.
/// ///
/// This will be implemented by individual ISAs. /// This will be implemented by individual ISAs.
@@ -99,62 +65,6 @@ pub trait ArgAssigner {
fn assign(&mut self, arg: &AbiParam) -> ArgAction; 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<AA: ArgAssigner>(args: &[AbiParam], aa: &mut AA) -> Option<Vec<AbiParam>> {
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 /// 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. /// the next argument is `arg` which has a different value type.
/// ///

View File

@@ -3,11 +3,10 @@
//! This module contains types and functions for working with the encoding tables generated by //! This module contains types and functions for working with the encoding tables generated by
//! `cranelift-codegen/meta/src/gen_encodings.rs`. //! `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::ir::{Function, InstructionData, Opcode, Type};
use crate::isa::{Encoding, Legalize}; use crate::isa::{Encoding, Legalize};
use crate::settings::PredicateView; use crate::settings::PredicateView;
use core::ops::Range;
/// A recipe predicate. /// A recipe predicate.
/// ///
@@ -49,14 +48,6 @@ pub struct Level1Entry<OffT: Into<u32> + Copy> {
pub offset: OffT, pub offset: OffT,
} }
impl<OffT: Into<u32> + Copy> Level1Entry<OffT> {
/// Get the level 2 table range indicated by this entry.
fn range(&self) -> Range<usize> {
let b = self.offset.into() as usize;
b..b + (1 << self.log2len)
}
}
impl<OffT: Into<u32> + Copy> Table<Type> for [Level1Entry<OffT>] { impl<OffT: Into<u32> + Copy> Table<Type> for [Level1Entry<OffT>] {
fn len(&self) -> usize { fn len(&self) -> usize {
self.len() self.len()
@@ -97,68 +88,6 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
} }
} }
/// 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<OffT1>],
level2_table: &'static [Level2Entry<OffT2>],
enclist: &'static [EncListEntry],
legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate],
inst_preds: &'static [InstPredicate],
isa_preds: PredicateView<'a>,
) -> Encodings<'a>
where
OffT1: Into<u32> + Copy,
OffT2: Into<u32> + 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 list entry.
/// ///
/// Encoding lists are represented as sequences of u16 words. /// Encoding lists are represented as sequences of u16 words.

View File

@@ -24,77 +24,6 @@ pub enum UnwindInfo {
SystemV(systemv::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<Reg> {
/// 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<Reg> {
/// Size of the prologue.
pub prologue_size: CodeOffset,
/// Unwind codes for prologue.
pub prologue_unwind_codes: Vec<(CodeOffset, UnwindCode<Reg>)>,
/// Unwind codes for epilogues.
pub epilogues_unwind_codes: Vec<Vec<(CodeOffset, UnwindCode<Reg>)>>,
/// 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 /// Unwind pseudoinstruction used in VCode backends: represents that
/// at the present location, an action has just been taken. /// at the present location, an action has just been taken.
/// ///

View File

@@ -1,7 +1,6 @@
//! System V ABI unwind information. //! System V ABI unwind information.
use crate::binemit::CodeOffset; use crate::binemit::CodeOffset;
use crate::isa::unwind::input;
use crate::isa::unwind::UnwindInst; use crate::isa::unwind::UnwindInst;
use crate::result::{CodegenError, CodegenResult}; use crate::result::{CodegenError, CodegenResult};
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -259,66 +258,6 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
} }
impl UnwindInfo { 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<Reg>,
map_reg: &'b dyn RegisterMapper<Reg>,
) -> CodegenResult<Self> {
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`. /// Converts the unwind information into a `FrameDescriptionEntry`.
pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry { pub fn to_fde(&self, address: Address) -> gimli::write::FrameDescriptionEntry {
let mut fde = FrameDescriptionEntry::new(address, self.len); let mut fde = FrameDescriptionEntry::new(address, self.len);
@@ -330,145 +269,3 @@ impl UnwindInfo {
fde fde
} }
} }
// TODO: delete the builder below when the old backend is removed.
struct InstructionBuilder<'a, Reg: PartialEq + Copy> {
sp_offset: i32,
frame_register: Option<Reg>,
saved_state: Option<(i32, Option<Reg>)>,
map_reg: &'a dyn RegisterMapper<Reg>,
instructions: Vec<(u32, CallFrameInstruction)>,
}
impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
fn new(sp_offset: u8, map_reg: &'a (dyn RegisterMapper<Reg> + '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 },
));
}
}

View File

@@ -1,6 +1,5 @@
//! Windows x64 ABI unwind information. //! Windows x64 ABI unwind information.
use crate::isa::unwind::input;
use crate::result::{CodegenError, CodegenResult}; use crate::result::{CodegenError, CodegenResult};
use alloc::vec::Vec; use alloc::vec::Vec;
use log::warn; use log::warn;
@@ -259,76 +258,6 @@ impl UnwindInfo {
.iter() .iter()
.fold(0, |nodes, c| nodes + c.node_count()) .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<Reg: PartialEq + Copy + std::fmt::Debug, MR: RegisterMapper<Reg>>(
unwind: input::UnwindInfo<Reg>,
) -> CodegenResult<Self> {
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; const UNWIND_RBP_REG: u8 = 5;

View File

@@ -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);
}

View File

@@ -15,7 +15,7 @@
use crate::cursor::{Cursor, FuncCursor}; use crate::cursor::{Cursor, FuncCursor};
use crate::flowgraph::ControlFlowGraph; use crate::flowgraph::ControlFlowGraph;
use crate::ir::types::{I32, I64}; use crate::ir::types::I32;
use crate::ir::{self, InstBuilder, MemFlags}; use crate::ir::{self, InstBuilder, MemFlags};
use crate::isa::TargetIsa; use crate::isa::TargetIsa;
@@ -23,7 +23,6 @@ use crate::timing;
use alloc::collections::BTreeSet; use alloc::collections::BTreeSet;
mod boundary; mod boundary;
mod call;
mod globalvalue; mod globalvalue;
mod heap; mod heap;
mod libcall; 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. /// Custom expansion for conditional trap instructions.
/// TODO: Add CFG support to the Rust DSL patterns so we won't have to do this. /// TODO: Add CFG support to the Rust DSL patterns so we won't have to do this.
fn expand_cond_trap( fn expand_cond_trap(
@@ -403,189 +396,6 @@ fn expand_cond_trap(
cfg.recompute_block(pos.func, new_block_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( fn expand_br_icmp(
inst: ir::Inst, inst: ir::Inst,
func: &mut ir::Function, func: &mut ir::Function,
@@ -620,34 +430,6 @@ fn expand_br_icmp(
cfg.recompute_block(pos.func, old_block); 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. /// Expand illegal `stack_load` instructions.
fn expand_stack_load( fn expand_stack_load(
inst: ir::Inst, inst: ir::Inst,
@@ -713,171 +495,3 @@ fn expand_stack_store(
mflags.set_aligned(); mflags.set_aligned();
pos.func.dfg.replace(inst).store(mflags, val, addr, 0); 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),
}
}