//! Register diversions. //! //! Normally, a value is assigned to a single register or stack location by the register allocator. //! Sometimes, it is necessary to move register values to a different register in order to satisfy //! instruction constraints. //! //! These register diversions are local to a block. No values can be diverted when entering a new //! block. use crate::fx::FxHashMap; use crate::hash_map::{Entry, Iter}; use crate::ir::{Block, StackSlot, Value, ValueLoc, ValueLocations}; use crate::ir::{InstructionData, Opcode}; use crate::isa::{RegInfo, RegUnit}; use core::fmt; use cranelift_entity::{SparseMap, SparseMapValue}; /// A diversion of a value from its original location to a new register or stack location. /// /// In IR, a diversion is represented by a `regmove` instruction, possibly a chain of them for the /// same value. /// /// When tracking diversions, the `from` field is the original assigned value location, and `to` is /// the current one. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Diversion { /// The original value location. pub from: ValueLoc, /// The current value location. pub to: ValueLoc, } impl Diversion { /// Make a new diversion. pub fn new(from: ValueLoc, to: ValueLoc) -> Self { debug_assert!(from.is_assigned() && to.is_assigned()); Self { from, to } } } /// Keep track of diversions in a block. #[derive(Clone)] pub struct RegDiversions { current: FxHashMap, } /// Keep track of diversions at the entry of block. #[derive(Clone)] struct EntryRegDiversionsValue { key: Block, divert: RegDiversions, } /// Map block to their matching RegDiversions at basic blocks entry. pub struct EntryRegDiversions { map: SparseMap, } impl RegDiversions { /// Create a new empty diversion tracker. pub fn new() -> Self { Self { current: FxHashMap::default(), } } /// Clear the content of the diversions, to reset the state of the compiler. pub fn clear(&mut self) { self.current.clear() } /// Are there any diversions? pub fn is_empty(&self) -> bool { self.current.is_empty() } /// Get the current diversion of `value`, if any. pub fn diversion(&self, value: Value) -> Option<&Diversion> { self.current.get(&value) } /// Get all current diversions. pub fn iter(&self) -> Iter<'_, Value, Diversion> { self.current.iter() } /// Get the current location for `value`. Fall back to the assignment map for non-diverted /// values pub fn get(&self, value: Value, locations: &ValueLocations) -> ValueLoc { match self.diversion(value) { Some(d) => d.to, None => locations[value], } } /// Get the current register location for `value`, or panic if `value` isn't in a register. pub fn reg(&self, value: Value, locations: &ValueLocations) -> RegUnit { self.get(value, locations).unwrap_reg() } /// Get the current stack location for `value`, or panic if `value` isn't in a stack slot. pub fn stack(&self, value: Value, locations: &ValueLocations) -> StackSlot { self.get(value, locations).unwrap_stack() } /// Record any kind of move. /// /// The `from` location must match an existing `to` location, if any. fn divert(&mut self, value: Value, from: ValueLoc, to: ValueLoc) { debug_assert!(from.is_assigned() && to.is_assigned()); match self.current.entry(value) { Entry::Occupied(mut e) => { // TODO: non-lexical lifetimes should allow removal of the scope and early return. { let d = e.get_mut(); debug_assert_eq!(d.to, from, "Bad regmove chain for {}", value); if d.from != to { d.to = to; return; } } e.remove(); } Entry::Vacant(e) => { e.insert(Diversion::new(from, to)); } } } /// Record a register -> register move. pub fn regmove(&mut self, value: Value, from: RegUnit, to: RegUnit) { self.divert(value, ValueLoc::Reg(from), ValueLoc::Reg(to)); } /// Record a register -> stack move. pub fn regspill(&mut self, value: Value, from: RegUnit, to: StackSlot) { self.divert(value, ValueLoc::Reg(from), ValueLoc::Stack(to)); } /// Record a stack -> register move. pub fn regfill(&mut self, value: Value, from: StackSlot, to: RegUnit) { self.divert(value, ValueLoc::Stack(from), ValueLoc::Reg(to)); } /// Apply the effect of `inst`. /// /// If `inst` is a `regmove`, `regfill`, or `regspill` instruction, update the diversions to /// match. pub fn apply(&mut self, inst: &InstructionData) { match *inst { InstructionData::RegMove { opcode: Opcode::Regmove, arg, src, dst, } => self.regmove(arg, src, dst), InstructionData::RegSpill { opcode: Opcode::Regspill, arg, src, dst, } => self.regspill(arg, src, dst), InstructionData::RegFill { opcode: Opcode::Regfill, arg, src, dst, } => self.regfill(arg, src, dst), _ => {} } } /// Drop any recorded move for `value`. /// /// Returns the `to` location of the removed diversion. pub fn remove(&mut self, value: Value) -> Option { self.current.remove(&value).map(|d| d.to) } /// Resets the state of the current diversions to the recorded diversions at the entry of the /// given `block`. The recoded diversions is available after coloring on `func.entry_diversions` /// field. pub fn at_block(&mut self, entry_diversions: &EntryRegDiversions, block: Block) { self.clear(); if let Some(entry_divert) = entry_diversions.map.get(block) { let iter = entry_divert.divert.current.iter(); self.current.extend(iter); } } /// Copy the current state of the diversions, and save it for the entry of the `block` given as /// argument. /// /// Note: This function can only be called once on a `Block` with a given `entry_diversions` /// argument, otherwise it would panic. pub fn save_for_block(&mut self, entry_diversions: &mut EntryRegDiversions, target: Block) { // No need to save anything if there is no diversions to be recorded. if self.is_empty() { return; } debug_assert!(!entry_diversions.map.contains_key(target)); let iter = self.current.iter(); let mut entry_divert = Self::new(); entry_divert.current.extend(iter); entry_diversions.map.insert(EntryRegDiversionsValue { key: target, divert: entry_divert, }); } /// Check that the recorded entry for a given `block` matches what is recorded in the /// `entry_diversions`. pub fn check_block_entry(&self, entry_diversions: &EntryRegDiversions, target: Block) -> bool { let entry_divert = match entry_diversions.map.get(target) { Some(entry_divert) => entry_divert, None => return self.is_empty(), }; if entry_divert.divert.current.len() != self.current.len() { return false; } for (val, _) in entry_divert.divert.current.iter() { if !self.current.contains_key(val) { return false; } } true } /// Return an object that can display the diversions. pub fn display<'a, R: Into>>(&'a self, regs: R) -> DisplayDiversions<'a> { DisplayDiversions(&self, regs.into()) } } impl EntryRegDiversions { /// Create a new empty entry diversion, to associate diversions to each block entry. pub fn new() -> Self { Self { map: SparseMap::new(), } } pub fn clear(&mut self) { self.map.clear(); } } impl Clone for EntryRegDiversions { /// The Clone trait is required by `ir::Function`. fn clone(&self) -> Self { let mut tmp = Self::new(); for v in self.map.values() { tmp.map.insert(v.clone()); } tmp } } /// Implement `SparseMapValue`, as required to make use of a `SparseMap` for mapping the entry /// diversions for each block. impl SparseMapValue for EntryRegDiversionsValue { fn key(&self) -> Block { self.key } } /// Object that displays register diversions. pub struct DisplayDiversions<'a>(&'a RegDiversions, Option<&'a RegInfo>); impl<'a> fmt::Display for DisplayDiversions<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{")?; for (value, div) in self.0.current.iter() { write!( f, " {}: {} -> {}", value, div.from.display(self.1), div.to.display(self.1) )? } write!(f, " }}") } } #[cfg(test)] mod tests { use super::*; use crate::entity::EntityRef; use crate::ir::Value; #[test] fn inserts() { let mut divs = RegDiversions::new(); let v1 = Value::new(1); let v2 = Value::new(2); divs.regmove(v1, 10, 12); assert_eq!( divs.diversion(v1), Some(&Diversion { from: ValueLoc::Reg(10), to: ValueLoc::Reg(12), }) ); assert_eq!(divs.diversion(v2), None); divs.regmove(v1, 12, 11); assert_eq!(divs.diversion(v1).unwrap().to, ValueLoc::Reg(11)); divs.regmove(v1, 11, 10); assert_eq!(divs.diversion(v1), None); } }