Path: blob/main/cranelift/codegen/src/ir/dfg.rs
1693 views
//! Data flow graph tracking Instructions, Values, and blocks.12use crate::entity::{self, PrimaryMap, SecondaryMap};3use crate::ir;4use crate::ir::builder::ReplaceBuilder;5use crate::ir::dynamic_type::{DynamicTypeData, DynamicTypes};6use crate::ir::instructions::{CallInfo, InstructionData};7use crate::ir::pcc::Fact;8use crate::ir::user_stack_maps::{UserStackMapEntry, UserStackMapEntryVec};9use crate::ir::{10Block, BlockArg, BlockCall, ConstantData, ConstantPool, DynamicType, ExceptionTables,11ExtFuncData, FuncRef, Immediate, Inst, JumpTables, RelSourceLoc, SigRef, Signature, Type,12Value, ValueLabelAssignments, ValueList, ValueListPool, types,13};14use crate::packed_option::ReservedValue;15use crate::write::write_operands;16use core::fmt;17use core::iter;18use core::mem;19use core::ops::{Index, IndexMut};20use core::u16;2122use alloc::collections::BTreeMap;23#[cfg(feature = "enable-serde")]24use serde_derive::{Deserialize, Serialize};25use smallvec::SmallVec;2627/// Storage for instructions within the DFG.28#[derive(Clone, PartialEq, Hash)]29#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]30pub struct Insts(PrimaryMap<Inst, InstructionData>);3132/// Allow immutable access to instructions via indexing.33impl Index<Inst> for Insts {34type Output = InstructionData;3536fn index(&self, inst: Inst) -> &InstructionData {37self.0.index(inst)38}39}4041/// Allow mutable access to instructions via indexing.42impl IndexMut<Inst> for Insts {43fn index_mut(&mut self, inst: Inst) -> &mut InstructionData {44self.0.index_mut(inst)45}46}4748/// Storage for basic blocks within the DFG.49#[derive(Clone, PartialEq, Hash)]50#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]51pub struct Blocks(PrimaryMap<Block, BlockData>);5253impl Blocks {54/// Create a new basic block.55pub fn add(&mut self) -> Block {56self.0.push(BlockData::new())57}5859/// Get the total number of basic blocks created in this function, whether they are60/// currently inserted in the layout or not.61///62/// This is intended for use with `SecondaryMap::with_capacity`.63pub fn len(&self) -> usize {64self.0.len()65}6667/// Reserves capacity for at least `additional` more elements to be68/// inserted.69pub fn reserve(&mut self, additional: usize) {70self.0.reserve(additional);71}7273/// Returns `true` if the given block reference is valid.74pub fn is_valid(&self, block: Block) -> bool {75self.0.is_valid(block)76}7778/// Iterate over all blocks, regardless whether a block is actually inserted79/// in the layout or not.80///81/// Iterates in creation order, not layout order.82pub fn iter(&self) -> impl Iterator<Item = Block> {83self.0.keys()84}85}8687impl Index<Block> for Blocks {88type Output = BlockData;8990fn index(&self, block: Block) -> &BlockData {91&self.0[block]92}93}9495impl IndexMut<Block> for Blocks {96fn index_mut(&mut self, block: Block) -> &mut BlockData {97&mut self.0[block]98}99}100101/// A data flow graph defines all instructions and basic blocks in a function as well as102/// the data flow dependencies between them. The DFG also tracks values which can be either103/// instruction results or block parameters.104///105/// The layout of blocks in the function and of instructions in each block is recorded by the106/// `Layout` data structure which forms the other half of the function representation.107///108#[derive(Clone, PartialEq, Hash)]109#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]110pub struct DataFlowGraph {111/// Data about all of the instructions in the function, including opcodes and operands.112/// The instructions in this map are not in program order. That is tracked by `Layout`, along113/// with the block containing each instruction.114pub insts: Insts,115116/// List of result values for each instruction.117///118/// This map gets resized automatically by `make_inst()` so it is always in sync with the119/// primary `insts` map.120results: SecondaryMap<Inst, ValueList>,121122/// User-defined stack maps.123user_stack_maps: alloc::collections::BTreeMap<Inst, UserStackMapEntryVec>,124125/// basic blocks in the function and their parameters.126///127/// This map is not in program order. That is handled by `Layout`, and so is the sequence of128/// instructions contained in each block.129pub blocks: Blocks,130131/// Dynamic types created.132pub dynamic_types: DynamicTypes,133134/// Memory pool of value lists.135///136/// The `ValueList` references into this pool appear in many places:137///138/// - Instructions in `insts` that don't have room for their entire argument list inline.139/// - Instruction result values in `results`.140/// - block parameters in `blocks`.141pub value_lists: ValueListPool,142143/// Primary value table with entries for all values.144values: PrimaryMap<Value, ValueDataPacked>,145146/// Facts: proof-carrying-code assertions about values.147pub facts: SecondaryMap<Value, Option<Fact>>,148149/// Function signature table. These signatures are referenced by indirect call instructions as150/// well as the external function references.151pub signatures: PrimaryMap<SigRef, Signature>,152153/// External function references. These are functions that can be called directly.154pub ext_funcs: PrimaryMap<FuncRef, ExtFuncData>,155156/// Saves Value labels.157pub values_labels: Option<BTreeMap<Value, ValueLabelAssignments>>,158159/// Constants used within the function.160pub constants: ConstantPool,161162/// Stores large immediates that otherwise will not fit on InstructionData.163pub immediates: PrimaryMap<Immediate, ConstantData>,164165/// Jump tables used in this function.166pub jump_tables: JumpTables,167168/// Exception tables used in this function.169pub exception_tables: ExceptionTables,170}171172impl DataFlowGraph {173/// Create a new empty `DataFlowGraph`.174pub fn new() -> Self {175Self {176insts: Insts(PrimaryMap::new()),177results: SecondaryMap::new(),178user_stack_maps: alloc::collections::BTreeMap::new(),179blocks: Blocks(PrimaryMap::new()),180dynamic_types: DynamicTypes::new(),181value_lists: ValueListPool::new(),182values: PrimaryMap::new(),183facts: SecondaryMap::new(),184signatures: PrimaryMap::new(),185ext_funcs: PrimaryMap::new(),186values_labels: None,187constants: ConstantPool::new(),188immediates: PrimaryMap::new(),189jump_tables: JumpTables::new(),190exception_tables: ExceptionTables::new(),191}192}193194/// Clear everything.195pub fn clear(&mut self) {196self.insts.0.clear();197self.results.clear();198self.user_stack_maps.clear();199self.blocks.0.clear();200self.dynamic_types.clear();201self.value_lists.clear();202self.values.clear();203self.signatures.clear();204self.ext_funcs.clear();205self.values_labels = None;206self.constants.clear();207self.immediates.clear();208self.jump_tables.clear();209self.facts.clear();210}211212/// Get the total number of instructions created in this function, whether they are currently213/// inserted in the layout or not.214///215/// This is intended for use with `SecondaryMap::with_capacity`.216pub fn num_insts(&self) -> usize {217self.insts.0.len()218}219220/// Returns `true` if the given instruction reference is valid.221pub fn inst_is_valid(&self, inst: Inst) -> bool {222self.insts.0.is_valid(inst)223}224225/// Get the total number of basic blocks created in this function, whether they are226/// currently inserted in the layout or not.227///228/// This is intended for use with `SecondaryMap::with_capacity`.229pub fn num_blocks(&self) -> usize {230self.blocks.len()231}232233/// Returns `true` if the given block reference is valid.234pub fn block_is_valid(&self, block: Block) -> bool {235self.blocks.is_valid(block)236}237238/// Make a BlockCall, bundling together the block and its arguments.239pub fn block_call<'a>(240&mut self,241block: Block,242args: impl IntoIterator<Item = &'a BlockArg>,243) -> BlockCall {244BlockCall::new(block, args.into_iter().copied(), &mut self.value_lists)245}246247/// Get the total number of values.248pub fn num_values(&self) -> usize {249self.values.len()250}251252/// Get an iterator over all values and their definitions.253pub fn values_and_defs(&self) -> impl Iterator<Item = (Value, ValueDef)> + '_ {254self.values().map(|value| (value, self.value_def(value)))255}256257/// Starts collection of debug information.258pub fn collect_debug_info(&mut self) {259if self.values_labels.is_none() {260self.values_labels = Some(Default::default());261}262}263264/// Inserts a `ValueLabelAssignments::Alias` for `to_alias` if debug info265/// collection is enabled.266pub fn add_value_label_alias(&mut self, to_alias: Value, from: RelSourceLoc, value: Value) {267if let Some(values_labels) = self.values_labels.as_mut() {268values_labels.insert(to_alias, ir::ValueLabelAssignments::Alias { from, value });269}270}271}272273/// Resolve value aliases.274///275/// Find the original SSA value that `value` aliases, or None if an276/// alias cycle is detected.277fn maybe_resolve_aliases(278values: &PrimaryMap<Value, ValueDataPacked>,279value: Value,280) -> Option<Value> {281let mut v = value;282283// Note that values may be empty here.284for _ in 0..=values.len() {285if let ValueData::Alias { original, .. } = ValueData::from(values[v]) {286v = original;287} else {288return Some(v);289}290}291292None293}294295/// Resolve value aliases.296///297/// Find the original SSA value that `value` aliases.298fn resolve_aliases(values: &PrimaryMap<Value, ValueDataPacked>, value: Value) -> Value {299if let Some(v) = maybe_resolve_aliases(values, value) {300v301} else {302panic!("Value alias loop detected for {value}");303}304}305306/// Iterator over all Values in a DFG.307pub struct Values<'a> {308inner: entity::Iter<'a, Value, ValueDataPacked>,309}310311/// Check for non-values.312fn valid_valuedata(data: ValueDataPacked) -> bool {313let data = ValueData::from(data);314if let ValueData::Alias {315ty: types::INVALID,316original,317} = data318{319if original == Value::reserved_value() {320return false;321}322}323true324}325326impl<'a> Iterator for Values<'a> {327type Item = Value;328329fn next(&mut self) -> Option<Self::Item> {330self.inner331.by_ref()332.find(|kv| valid_valuedata(*kv.1))333.map(|kv| kv.0)334}335336fn size_hint(&self) -> (usize, Option<usize>) {337self.inner.size_hint()338}339}340341impl ExactSizeIterator for Values<'_> {342fn len(&self) -> usize {343self.inner.len()344}345}346347/// Handling values.348///349/// Values are either block parameters or instruction results.350impl DataFlowGraph {351/// Allocate an extended value entry.352fn make_value(&mut self, data: ValueData) -> Value {353self.values.push(data.into())354}355356/// The number of values defined in this DFG.357pub fn len_values(&self) -> usize {358self.values.len()359}360361/// Get an iterator over all values.362pub fn values<'a>(&'a self) -> Values<'a> {363Values {364inner: self.values.iter(),365}366}367368/// Check if a value reference is valid.369pub fn value_is_valid(&self, v: Value) -> bool {370self.values.is_valid(v)371}372373/// Check whether a value is valid and not an alias.374pub fn value_is_real(&self, value: Value) -> bool {375// Deleted or unused values are also stored as aliases so this excludes376// those as well.377self.value_is_valid(value) && !matches!(self.values[value].into(), ValueData::Alias { .. })378}379380/// Get the type of a value.381pub fn value_type(&self, v: Value) -> Type {382self.values[v].ty()383}384385/// Get the definition of a value.386///387/// This is either the instruction that defined it or the Block that has the value as an388/// parameter.389pub fn value_def(&self, v: Value) -> ValueDef {390match ValueData::from(self.values[v]) {391ValueData::Inst { inst, num, .. } => ValueDef::Result(inst, num as usize),392ValueData::Param { block, num, .. } => ValueDef::Param(block, num as usize),393ValueData::Alias { original, .. } => {394// Make sure we only recurse one level. `resolve_aliases` has safeguards to395// detect alias loops without overrunning the stack.396self.value_def(self.resolve_aliases(original))397}398ValueData::Union { x, y, .. } => ValueDef::Union(x, y),399}400}401402/// Determine if `v` is an attached instruction result / block parameter.403///404/// An attached value can't be attached to something else without first being detached.405///406/// Value aliases are not considered to be attached to anything. Use `resolve_aliases()` to407/// determine if the original aliased value is attached.408pub fn value_is_attached(&self, v: Value) -> bool {409use self::ValueData::*;410match ValueData::from(self.values[v]) {411Inst { inst, num, .. } => Some(&v) == self.inst_results(inst).get(num as usize),412Param { block, num, .. } => Some(&v) == self.block_params(block).get(num as usize),413Alias { .. } => false,414Union { .. } => false,415}416}417418/// Resolve value aliases.419///420/// Find the original SSA value that `value` aliases.421pub fn resolve_aliases(&self, value: Value) -> Value {422resolve_aliases(&self.values, value)423}424425/// Replace all uses of value aliases with their resolved values, and delete426/// the aliases.427pub fn resolve_all_aliases(&mut self) {428let invalid_value = ValueDataPacked::from(ValueData::Alias {429ty: types::INVALID,430original: Value::reserved_value(),431});432433// Rewrite each chain of aliases. Update every alias along the chain434// into an alias directly to the final value. Due to updating every435// alias that it looks at, this loop runs in time linear in the number436// of values.437for mut src in self.values.keys() {438let value_data = self.values[src];439if value_data == invalid_value {440continue;441}442if let ValueData::Alias { mut original, .. } = value_data.into() {443// We don't use the type after this, we just need some place to444// store the resolved aliases temporarily.445let resolved = ValueDataPacked::from(ValueData::Alias {446ty: types::INVALID,447original: resolve_aliases(&self.values, original),448});449// Walk the chain again, splatting the new alias everywhere.450// resolve_aliases panics if there's an alias cycle, so we don't451// need to guard against cycles here.452loop {453self.values[src] = resolved;454src = original;455if let ValueData::Alias { original: next, .. } = self.values[src].into() {456original = next;457} else {458break;459}460}461}462}463464// Now aliases don't point to other aliases, so we can replace any use465// of an alias with the final value in constant time.466467// Rewrite InstructionData in `self.insts`.468for inst in self.insts.0.values_mut() {469inst.map_values(470&mut self.value_lists,471&mut self.jump_tables,472&mut self.exception_tables,473|arg| {474if let ValueData::Alias { original, .. } = self.values[arg].into() {475original476} else {477arg478}479},480);481}482483// - `results` and block-params in `blocks` are not aliases, by484// definition.485// - `dynamic_types` has no values.486// - `value_lists` can only be accessed via references from elsewhere.487// - `values` only has value references in aliases (which we've488// removed), and unions (but the egraph pass ensures there are no489// aliases before creating unions).490491// Merge `facts` from any alias onto the aliased value. Note that if492// there was a chain of aliases, at this point every alias that was in493// the chain points to the same final value, so their facts will all be494// merged together.495for value in self.facts.keys() {496if let ValueData::Alias { original, .. } = self.values[value].into() {497if let Some(new_fact) = self.facts[value].take() {498match &mut self.facts[original] {499Some(old_fact) => *old_fact = Fact::intersect(old_fact, &new_fact),500old_fact => *old_fact = Some(new_fact),501}502}503}504}505506// - `signatures` and `ext_funcs` have no values.507508if let Some(values_labels) = &mut self.values_labels {509// Debug info is best-effort. If any is attached to value aliases,510// just discard it.511values_labels.retain(|&k, _| !matches!(self.values[k].into(), ValueData::Alias { .. }));512513// If debug-info says a value should have the same labels as another514// value, then make sure that target is not a value alias.515for value_label in values_labels.values_mut() {516if let ValueLabelAssignments::Alias { value, .. } = value_label {517if let ValueData::Alias { original, .. } = self.values[*value].into() {518*value = original;519}520}521}522}523524// - `constants` and `immediates` have no values.525// - `jump_tables` is updated together with instruction-data above.526527// Delete all aliases now that there are no uses left.528for value in self.values.values_mut() {529if let ValueData::Alias { .. } = ValueData::from(*value) {530*value = invalid_value;531}532}533}534535/// Turn a value into an alias of another.536///537/// Change the `dest` value to behave as an alias of `src`. This means that all uses of `dest`538/// will behave as if they used that value `src`.539///540/// The `dest` value can't be attached to an instruction or block.541pub fn change_to_alias(&mut self, dest: Value, src: Value) {542debug_assert!(!self.value_is_attached(dest));543// Try to create short alias chains by finding the original source value.544// This also avoids the creation of loops.545let original = self.resolve_aliases(src);546debug_assert_ne!(547dest, original,548"Aliasing {dest} to {src} would create a loop"549);550let ty = self.value_type(original);551debug_assert_eq!(552self.value_type(dest),553ty,554"Aliasing {} to {} would change its type {} to {}",555dest,556src,557self.value_type(dest),558ty559);560debug_assert_ne!(ty, types::INVALID);561562self.values[dest] = ValueData::Alias { ty, original }.into();563}564565/// Replace the results of one instruction with aliases to the results of another.566///567/// Change all the results of `dest_inst` to behave as aliases of568/// corresponding results of `src_inst`, as if calling change_to_alias for569/// each.570///571/// After calling this instruction, `dest_inst` will have had its results572/// cleared, so it likely needs to be removed from the graph.573///574pub fn replace_with_aliases(&mut self, dest_inst: Inst, original_inst: Inst) {575debug_assert_ne!(576dest_inst, original_inst,577"Replacing {dest_inst} with itself would create a loop"578);579580let dest_results = self.results[dest_inst].as_slice(&self.value_lists);581let original_results = self.results[original_inst].as_slice(&self.value_lists);582583debug_assert_eq!(584dest_results.len(),585original_results.len(),586"Replacing {dest_inst} with {original_inst} would produce a different number of results."587);588589for (&dest, &original) in dest_results.iter().zip(original_results) {590let ty = self.value_type(original);591debug_assert_eq!(592self.value_type(dest),593ty,594"Aliasing {} to {} would change its type {} to {}",595dest,596original,597self.value_type(dest),598ty599);600debug_assert_ne!(ty, types::INVALID);601602self.values[dest] = ValueData::Alias { ty, original }.into();603}604605self.clear_results(dest_inst);606}607608/// Get the stack map entries associated with the given instruction.609pub fn user_stack_map_entries(&self, inst: Inst) -> Option<&[UserStackMapEntry]> {610self.user_stack_maps.get(&inst).map(|es| &**es)611}612613/// Append a new stack map entry for the given call instruction.614///615/// # Panics616///617/// Panics if the given instruction is not a (non-tail) call instruction.618pub fn append_user_stack_map_entry(&mut self, inst: Inst, entry: UserStackMapEntry) {619let opcode = self.insts[inst].opcode();620assert!(opcode.is_safepoint());621self.user_stack_maps.entry(inst).or_default().push(entry);622}623624/// Append multiple stack map entries for the given call instruction.625///626/// # Panics627///628/// Panics if the given instruction is not a (non-tail) call instruction.629pub fn append_user_stack_map_entries(630&mut self,631inst: Inst,632entries: impl IntoIterator<Item = UserStackMapEntry>,633) {634for entry in entries {635self.append_user_stack_map_entry(inst, entry);636}637}638639/// Take the stack map entries for a given instruction, leaving the640/// instruction without stack maps.641pub(crate) fn take_user_stack_map_entries(642&mut self,643inst: Inst,644) -> Option<UserStackMapEntryVec> {645self.user_stack_maps.remove(&inst)646}647}648649/// Where did a value come from?650#[derive(Clone, Copy, Debug, PartialEq, Eq)]651pub enum ValueDef {652/// Value is the n'th result of an instruction.653Result(Inst, usize),654/// Value is the n'th parameter to a block.655Param(Block, usize),656/// Value is a union of two other values.657Union(Value, Value),658}659660impl ValueDef {661/// Unwrap the instruction where the value was defined, or panic.662pub fn unwrap_inst(&self) -> Inst {663self.inst().expect("Value is not an instruction result")664}665666/// Get the instruction where the value was defined, if any.667pub fn inst(&self) -> Option<Inst> {668match *self {669Self::Result(inst, _) => Some(inst),670_ => None,671}672}673674/// Unwrap the block there the parameter is defined, or panic.675pub fn unwrap_block(&self) -> Block {676match *self {677Self::Param(block, _) => block,678_ => panic!("Value is not a block parameter"),679}680}681682/// Get the number component of this definition.683///684/// When multiple values are defined at the same program point, this indicates the index of685/// this value.686pub fn num(self) -> usize {687match self {688Self::Result(_, n) | Self::Param(_, n) => n,689Self::Union(_, _) => 0,690}691}692}693694/// Internal table storage for extended values.695#[derive(Clone, Debug, PartialEq, Hash)]696#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]697enum ValueData {698/// Value is defined by an instruction.699Inst { ty: Type, num: u16, inst: Inst },700701/// Value is a block parameter.702Param { ty: Type, num: u16, block: Block },703704/// Value is an alias of another value.705/// An alias value can't be linked as an instruction result or block parameter. It is used as a706/// placeholder when the original instruction or block has been rewritten or modified.707Alias { ty: Type, original: Value },708709/// Union is a "fork" in representation: the value can be710/// represented as either of the values named here. This is used711/// for aegraph (acyclic egraph) representation in the DFG.712Union { ty: Type, x: Value, y: Value },713}714715/// Bit-packed version of ValueData, for efficiency.716///717/// Layout:718///719/// ```plain720/// | tag:2 | type:14 | x:24 | y:24 |721///722/// Inst 00 ty inst output inst index723/// Param 01 ty blockparam num block index724/// Alias 10 ty 0 value index725/// Union 11 ty first value second value726/// ```727#[derive(Clone, Copy, Debug, PartialEq, Hash)]728#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]729struct ValueDataPacked(u64);730731/// Encodes a value in 0..2^32 into 0..2^n, where n is less than 32732/// (and is implied by `mask`), by translating 2^32-1 (0xffffffff)733/// into 2^n-1 and panic'ing on 2^n..2^32-1.734fn encode_narrow_field(x: u32, bits: u8) -> u32 {735let max = (1 << bits) - 1;736if x == 0xffff_ffff {737max738} else {739debug_assert!(740x < max,741"{x} does not fit into {bits} bits (must be less than {max} to \742allow for a 0xffffffff sentinel)"743);744x745}746}747748/// The inverse of the above `encode_narrow_field`: unpacks 2^n-1 into749/// 2^32-1.750fn decode_narrow_field(x: u32, bits: u8) -> u32 {751if x == (1 << bits) - 1 { 0xffff_ffff } else { x }752}753754impl ValueDataPacked {755const Y_SHIFT: u8 = 0;756const Y_BITS: u8 = 24;757const X_SHIFT: u8 = Self::Y_SHIFT + Self::Y_BITS;758const X_BITS: u8 = 24;759const TYPE_SHIFT: u8 = Self::X_SHIFT + Self::X_BITS;760const TYPE_BITS: u8 = 14;761const TAG_SHIFT: u8 = Self::TYPE_SHIFT + Self::TYPE_BITS;762const TAG_BITS: u8 = 2;763764const TAG_INST: u64 = 0;765const TAG_PARAM: u64 = 1;766const TAG_ALIAS: u64 = 2;767const TAG_UNION: u64 = 3;768769fn make(tag: u64, ty: Type, x: u32, y: u32) -> ValueDataPacked {770debug_assert!(tag < (1 << Self::TAG_BITS));771debug_assert!(ty.repr() < (1 << Self::TYPE_BITS));772773let x = encode_narrow_field(x, Self::X_BITS);774let y = encode_narrow_field(y, Self::Y_BITS);775776ValueDataPacked(777(tag << Self::TAG_SHIFT)778| ((ty.repr() as u64) << Self::TYPE_SHIFT)779| ((x as u64) << Self::X_SHIFT)780| ((y as u64) << Self::Y_SHIFT),781)782}783784#[inline(always)]785fn field(self, shift: u8, bits: u8) -> u64 {786(self.0 >> shift) & ((1 << bits) - 1)787}788789#[inline(always)]790fn ty(self) -> Type {791let ty = self.field(ValueDataPacked::TYPE_SHIFT, ValueDataPacked::TYPE_BITS) as u16;792Type::from_repr(ty)793}794795#[inline(always)]796fn set_type(&mut self, ty: Type) {797self.0 &= !(((1 << Self::TYPE_BITS) - 1) << Self::TYPE_SHIFT);798self.0 |= (ty.repr() as u64) << Self::TYPE_SHIFT;799}800}801802impl From<ValueData> for ValueDataPacked {803fn from(data: ValueData) -> Self {804match data {805ValueData::Inst { ty, num, inst } => {806Self::make(Self::TAG_INST, ty, num.into(), inst.as_bits())807}808ValueData::Param { ty, num, block } => {809Self::make(Self::TAG_PARAM, ty, num.into(), block.as_bits())810}811ValueData::Alias { ty, original } => {812Self::make(Self::TAG_ALIAS, ty, 0, original.as_bits())813}814ValueData::Union { ty, x, y } => {815Self::make(Self::TAG_UNION, ty, x.as_bits(), y.as_bits())816}817}818}819}820821impl From<ValueDataPacked> for ValueData {822fn from(data: ValueDataPacked) -> Self {823let tag = data.field(ValueDataPacked::TAG_SHIFT, ValueDataPacked::TAG_BITS);824let ty = u16::try_from(data.field(ValueDataPacked::TYPE_SHIFT, ValueDataPacked::TYPE_BITS))825.expect("Mask should ensure result fits in a u16");826let x = u32::try_from(data.field(ValueDataPacked::X_SHIFT, ValueDataPacked::X_BITS))827.expect("Mask should ensure result fits in a u32");828let y = u32::try_from(data.field(ValueDataPacked::Y_SHIFT, ValueDataPacked::Y_BITS))829.expect("Mask should ensure result fits in a u32");830831let ty = Type::from_repr(ty);832match tag {833ValueDataPacked::TAG_INST => ValueData::Inst {834ty,835num: u16::try_from(x).expect("Inst result num should fit in u16"),836inst: Inst::from_bits(decode_narrow_field(y, ValueDataPacked::Y_BITS)),837},838ValueDataPacked::TAG_PARAM => ValueData::Param {839ty,840num: u16::try_from(x).expect("Blockparam index should fit in u16"),841block: Block::from_bits(decode_narrow_field(y, ValueDataPacked::Y_BITS)),842},843ValueDataPacked::TAG_ALIAS => ValueData::Alias {844ty,845original: Value::from_bits(decode_narrow_field(y, ValueDataPacked::Y_BITS)),846},847ValueDataPacked::TAG_UNION => ValueData::Union {848ty,849x: Value::from_bits(decode_narrow_field(x, ValueDataPacked::X_BITS)),850y: Value::from_bits(decode_narrow_field(y, ValueDataPacked::Y_BITS)),851},852_ => panic!("Invalid tag {} in ValueDataPacked 0x{:x}", tag, data.0),853}854}855}856857/// Instructions.858///859impl DataFlowGraph {860/// Create a new instruction.861///862/// The type of the first result is indicated by `data.ty`. If the863/// instruction produces multiple results, also call864/// `make_inst_results` to allocate value table entries. (It is865/// always safe to call `make_inst_results`, regardless of how866/// many results the instruction has.)867pub fn make_inst(&mut self, data: InstructionData) -> Inst {868let n = self.num_insts() + 1;869self.results.resize(n);870self.insts.0.push(data)871}872873/// Declares a dynamic vector type874pub fn make_dynamic_ty(&mut self, data: DynamicTypeData) -> DynamicType {875self.dynamic_types.push(data)876}877878/// Returns an object that displays `inst`.879pub fn display_inst<'a>(&'a self, inst: Inst) -> DisplayInst<'a> {880DisplayInst(self, inst)881}882883/// Returns an object that displays the given `value`'s defining instruction.884///885/// Panics if the value is not defined by an instruction (i.e. it is a basic886/// block argument).887pub fn display_value_inst(&self, value: Value) -> DisplayInst<'_> {888match self.value_def(value) {889ir::ValueDef::Result(inst, _) => self.display_inst(inst),890ir::ValueDef::Param(_, _) => panic!("value is not defined by an instruction"),891ir::ValueDef::Union(_, _) => panic!("value is a union of two other values"),892}893}894895/// Construct a read-only visitor context for the values of this instruction.896pub fn inst_values<'dfg>(897&'dfg self,898inst: Inst,899) -> impl DoubleEndedIterator<Item = Value> + 'dfg {900self.inst_args(inst)901.iter()902.copied()903.chain(904self.insts[inst]905.branch_destination(&self.jump_tables, &self.exception_tables)906.into_iter()907.flat_map(|branch| {908branch909.args(&self.value_lists)910.filter_map(|arg| arg.as_value())911}),912)913.chain(914self.insts[inst]915.exception_table()916.into_iter()917.flat_map(|et| self.exception_tables[et].contexts()),918)919}920921/// Map a function over the values of the instruction.922pub fn map_inst_values<F>(&mut self, inst: Inst, body: F)923where924F: FnMut(Value) -> Value,925{926self.insts[inst].map_values(927&mut self.value_lists,928&mut self.jump_tables,929&mut self.exception_tables,930body,931);932}933934/// Overwrite the instruction's value references with values from the iterator.935/// NOTE: the iterator provided is expected to yield at least as many values as the instruction936/// currently has.937pub fn overwrite_inst_values<I>(&mut self, inst: Inst, mut values: I)938where939I: Iterator<Item = Value>,940{941self.insts[inst].map_values(942&mut self.value_lists,943&mut self.jump_tables,944&mut self.exception_tables,945|_| values.next().unwrap(),946);947}948949/// Get all value arguments on `inst` as a slice.950pub fn inst_args(&self, inst: Inst) -> &[Value] {951self.insts[inst].arguments(&self.value_lists)952}953954/// Get all value arguments on `inst` as a mutable slice.955pub fn inst_args_mut(&mut self, inst: Inst) -> &mut [Value] {956self.insts[inst].arguments_mut(&mut self.value_lists)957}958959/// Get the fixed value arguments on `inst` as a slice.960pub fn inst_fixed_args(&self, inst: Inst) -> &[Value] {961let num_fixed_args = self.insts[inst]962.opcode()963.constraints()964.num_fixed_value_arguments();965&self.inst_args(inst)[..num_fixed_args]966}967968/// Get the fixed value arguments on `inst` as a mutable slice.969pub fn inst_fixed_args_mut(&mut self, inst: Inst) -> &mut [Value] {970let num_fixed_args = self.insts[inst]971.opcode()972.constraints()973.num_fixed_value_arguments();974&mut self.inst_args_mut(inst)[..num_fixed_args]975}976977/// Get the variable value arguments on `inst` as a slice.978pub fn inst_variable_args(&self, inst: Inst) -> &[Value] {979let num_fixed_args = self.insts[inst]980.opcode()981.constraints()982.num_fixed_value_arguments();983&self.inst_args(inst)[num_fixed_args..]984}985986/// Get the variable value arguments on `inst` as a mutable slice.987pub fn inst_variable_args_mut(&mut self, inst: Inst) -> &mut [Value] {988let num_fixed_args = self.insts[inst]989.opcode()990.constraints()991.num_fixed_value_arguments();992&mut self.inst_args_mut(inst)[num_fixed_args..]993}994995/// Create result values for an instruction that produces multiple results.996///997/// Instructions that produce no result values only need to be created with `make_inst`,998/// otherwise call `make_inst_results` to allocate value table entries for the results.999///1000/// The result value types are determined from the instruction's value type constraints and the1001/// provided `ctrl_typevar` type for polymorphic instructions. For non-polymorphic1002/// instructions, `ctrl_typevar` is ignored, and `INVALID` can be used.1003///1004/// The type of the first result value is also set, even if it was already set in the1005/// `InstructionData` passed to `make_inst`. If this function is called with a single-result1006/// instruction, that is the only effect.1007pub fn make_inst_results(&mut self, inst: Inst, ctrl_typevar: Type) -> usize {1008self.make_inst_results_reusing(inst, ctrl_typevar, iter::empty())1009}10101011/// Create result values for `inst`, reusing the provided detached values.1012///1013/// Create a new set of result values for `inst` using `ctrl_typevar` to determine the result1014/// types. Any values provided by `reuse` will be reused. When `reuse` is exhausted or when it1015/// produces `None`, a new value is created.1016pub fn make_inst_results_reusing<I>(1017&mut self,1018inst: Inst,1019ctrl_typevar: Type,1020reuse: I,1021) -> usize1022where1023I: Iterator<Item = Option<Value>>,1024{1025self.clear_results(inst);10261027let mut reuse = reuse.fuse();1028let result_tys: SmallVec<[_; 16]> = self.inst_result_types(inst, ctrl_typevar).collect();10291030for (expected, &ty) in result_tys.iter().enumerate() {1031let num = u16::try_from(expected).expect("Result value index should fit in u16");1032let value_data = ValueData::Inst { ty, num, inst };1033let v = if let Some(Some(v)) = reuse.next() {1034debug_assert_eq!(self.value_type(v), ty, "Reused {ty} is wrong type");1035debug_assert!(!self.value_is_attached(v));1036self.values[v] = value_data.into();1037v1038} else {1039self.make_value(value_data)1040};1041let actual = self.results[inst].push(v, &mut self.value_lists);1042debug_assert_eq!(expected, actual);1043}10441045result_tys.len()1046}10471048/// Create a `ReplaceBuilder` that will replace `inst` with a new instruction in place.1049pub fn replace(&mut self, inst: Inst) -> ReplaceBuilder<'_> {1050ReplaceBuilder::new(self, inst)1051}10521053/// Clear the list of result values from `inst`.1054///1055/// This leaves `inst` without any result values. New result values can be created by calling1056/// `make_inst_results` or by using a `replace(inst)` builder.1057pub fn clear_results(&mut self, inst: Inst) {1058self.results[inst].clear(&mut self.value_lists)1059}10601061/// Replace an instruction result with a new value of type `new_type`.1062///1063/// The `old_value` must be an attached instruction result.1064///1065/// The old value is left detached, so it should probably be changed into something else.1066///1067/// Returns the new value.1068pub fn replace_result(&mut self, old_value: Value, new_type: Type) -> Value {1069let (num, inst) = match ValueData::from(self.values[old_value]) {1070ValueData::Inst { num, inst, .. } => (num, inst),1071_ => panic!("{old_value} is not an instruction result value"),1072};1073let new_value = self.make_value(ValueData::Inst {1074ty: new_type,1075num,1076inst,1077});1078let num = num as usize;1079let attached = mem::replace(1080self.results[inst]1081.get_mut(num, &mut self.value_lists)1082.expect("Replacing detached result"),1083new_value,1084);1085debug_assert_eq!(1086attached,1087old_value,1088"{} wasn't detached from {}",1089old_value,1090self.display_inst(inst)1091);1092new_value1093}10941095/// Clone an instruction, attaching new result `Value`s and1096/// returning them.1097pub fn clone_inst(&mut self, inst: Inst) -> Inst {1098// First, add a clone of the InstructionData.1099let inst_data = self.insts[inst];1100// If the `inst_data` has a reference to a ValueList, clone it1101// as well, because we can't share these (otherwise mutating1102// one would affect the other).1103let inst_data = inst_data.deep_clone(&mut self.value_lists);1104let new_inst = self.make_inst(inst_data);1105// Get the controlling type variable.1106let ctrl_typevar = self.ctrl_typevar(inst);1107// Create new result values.1108let num_results = self.make_inst_results(new_inst, ctrl_typevar);1109// Copy over PCC facts, if any.1110for i in 0..num_results {1111let old_result = self.inst_results(inst)[i];1112let new_result = self.inst_results(new_inst)[i];1113self.facts[new_result] = self.facts[old_result].clone();1114}1115new_inst1116}11171118/// Get the first result of an instruction.1119///1120/// This function panics if the instruction doesn't have any result.1121pub fn first_result(&self, inst: Inst) -> Value {1122self.results[inst]1123.first(&self.value_lists)1124.unwrap_or_else(|| panic!("{inst} has no results"))1125}11261127/// Test if `inst` has any result values currently.1128pub fn has_results(&self, inst: Inst) -> bool {1129!self.results[inst].is_empty()1130}11311132/// Return all the results of an instruction.1133pub fn inst_results(&self, inst: Inst) -> &[Value] {1134self.results[inst].as_slice(&self.value_lists)1135}11361137/// Return all the results of an instruction as ValueList.1138pub fn inst_results_list(&self, inst: Inst) -> ValueList {1139self.results[inst]1140}11411142/// Create a union of two values.1143pub fn union(&mut self, x: Value, y: Value) -> Value {1144// Get the type.1145let ty = self.value_type(x);1146debug_assert_eq!(ty, self.value_type(y));1147self.make_value(ValueData::Union { ty, x, y })1148}11491150/// Get the call signature of a direct or indirect call instruction.1151/// Returns `None` if `inst` is not a call instruction.1152pub fn call_signature(&self, inst: Inst) -> Option<SigRef> {1153match self.insts[inst].analyze_call(&self.value_lists, &self.exception_tables) {1154CallInfo::NotACall => None,1155CallInfo::Direct(f, _) => Some(self.ext_funcs[f].signature),1156CallInfo::DirectWithSig(_, s, _) => Some(s),1157CallInfo::Indirect(s, _) => Some(s),1158}1159}11601161/// Like `call_signature` but returns none for tail call1162/// instructions and try-call (exception-handling invoke)1163/// instructions.1164fn non_tail_call_or_try_call_signature(&self, inst: Inst) -> Option<SigRef> {1165let sig = self.call_signature(inst)?;1166match self.insts[inst].opcode() {1167ir::Opcode::ReturnCall | ir::Opcode::ReturnCallIndirect => None,1168ir::Opcode::TryCall | ir::Opcode::TryCallIndirect => None,1169_ => Some(sig),1170}1171}11721173// Only for use by the verifier. Everyone else should just use1174// `dfg.inst_results(inst).len()`.1175pub(crate) fn num_expected_results_for_verifier(&self, inst: Inst) -> usize {1176match self.non_tail_call_or_try_call_signature(inst) {1177Some(sig) => self.signatures[sig].returns.len(),1178None => {1179let constraints = self.insts[inst].opcode().constraints();1180constraints.num_fixed_results()1181}1182}1183}11841185/// Get the result types of the given instruction.1186pub fn inst_result_types<'a>(1187&'a self,1188inst: Inst,1189ctrl_typevar: Type,1190) -> impl iter::ExactSizeIterator<Item = Type> + 'a {1191return match self.non_tail_call_or_try_call_signature(inst) {1192Some(sig) => InstResultTypes::Signature(self, sig, 0),1193None => {1194let constraints = self.insts[inst].opcode().constraints();1195InstResultTypes::Constraints(constraints, ctrl_typevar, 0)1196}1197};11981199enum InstResultTypes<'a> {1200Signature(&'a DataFlowGraph, SigRef, usize),1201Constraints(ir::instructions::OpcodeConstraints, Type, usize),1202}12031204impl Iterator for InstResultTypes<'_> {1205type Item = Type;12061207fn next(&mut self) -> Option<Type> {1208match self {1209InstResultTypes::Signature(dfg, sig, i) => {1210let param = dfg.signatures[*sig].returns.get(*i)?;1211*i += 1;1212Some(param.value_type)1213}1214InstResultTypes::Constraints(constraints, ctrl_ty, i) => {1215if *i < constraints.num_fixed_results() {1216let ty = constraints.result_type(*i, *ctrl_ty);1217*i += 1;1218Some(ty)1219} else {1220None1221}1222}1223}1224}12251226fn size_hint(&self) -> (usize, Option<usize>) {1227let len = match self {1228InstResultTypes::Signature(dfg, sig, i) => {1229dfg.signatures[*sig].returns.len() - *i1230}1231InstResultTypes::Constraints(constraints, _, i) => {1232constraints.num_fixed_results() - *i1233}1234};1235(len, Some(len))1236}1237}12381239impl ExactSizeIterator for InstResultTypes<'_> {}1240}12411242/// Compute the type of an instruction result from opcode constraints and call signatures.1243///1244/// This computes the same sequence of result types that `make_inst_results()` above would1245/// assign to the created result values, but it does not depend on `make_inst_results()` being1246/// called first.1247///1248/// Returns `None` if asked about a result index that is too large.1249pub fn compute_result_type(1250&self,1251inst: Inst,1252result_idx: usize,1253ctrl_typevar: Type,1254) -> Option<Type> {1255self.inst_result_types(inst, ctrl_typevar).nth(result_idx)1256}12571258/// Get the controlling type variable, or `INVALID` if `inst` isn't polymorphic.1259pub fn ctrl_typevar(&self, inst: Inst) -> Type {1260let constraints = self.insts[inst].opcode().constraints();12611262if !constraints.is_polymorphic() {1263types::INVALID1264} else if constraints.requires_typevar_operand() {1265// Not all instruction formats have a designated operand, but in that case1266// `requires_typevar_operand()` should never be true.1267self.value_type(1268self.insts[inst]1269.typevar_operand(&self.value_lists)1270.unwrap_or_else(|| {1271panic!(1272"Instruction format for {:?} doesn't have a designated operand",1273self.insts[inst]1274)1275}),1276)1277} else {1278self.value_type(self.first_result(inst))1279}1280}1281}12821283/// basic blocks.1284impl DataFlowGraph {1285/// Create a new basic block.1286pub fn make_block(&mut self) -> Block {1287self.blocks.add()1288}12891290/// Get the number of parameters on `block`.1291pub fn num_block_params(&self, block: Block) -> usize {1292self.blocks[block].params(&self.value_lists).len()1293}12941295/// Get the parameters on `block`.1296pub fn block_params(&self, block: Block) -> &[Value] {1297self.blocks[block].params(&self.value_lists)1298}12991300/// Get the types of the parameters on `block`.1301pub fn block_param_types(&self, block: Block) -> impl Iterator<Item = Type> + '_ {1302self.block_params(block).iter().map(|&v| self.value_type(v))1303}13041305/// Append a parameter with type `ty` to `block`.1306pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {1307let param = self.values.next_key();1308let num = self.blocks[block].params.push(param, &mut self.value_lists);1309debug_assert!(num <= u16::MAX as usize, "Too many parameters on block");1310self.make_value(ValueData::Param {1311ty,1312num: num as u16,1313block,1314})1315}13161317/// Removes `val` from `block`'s parameters by swapping it with the last parameter on `block`.1318/// Returns the position of `val` before removal.1319///1320/// *Important*: to ensure O(1) deletion, this method swaps the removed parameter with the1321/// last `block` parameter. This can disrupt all the branch instructions jumping to this1322/// `block` for which you have to change the branch argument order if necessary.1323///1324/// Panics if `val` is not a block parameter.1325pub fn swap_remove_block_param(&mut self, val: Value) -> usize {1326let (block, num) =1327if let ValueData::Param { num, block, .. } = ValueData::from(self.values[val]) {1328(block, num)1329} else {1330panic!("{val} must be a block parameter");1331};1332self.blocks[block]1333.params1334.swap_remove(num as usize, &mut self.value_lists);1335if let Some(last_arg_val) = self.blocks[block]1336.params1337.get(num as usize, &self.value_lists)1338{1339// We update the position of the old last arg.1340let mut last_arg_data = ValueData::from(self.values[last_arg_val]);1341if let ValueData::Param { num: old_num, .. } = &mut last_arg_data {1342*old_num = num;1343self.values[last_arg_val] = last_arg_data.into();1344} else {1345panic!("{last_arg_val} should be a Block parameter");1346}1347}1348num as usize1349}13501351/// Removes `val` from `block`'s parameters by a standard linear time list removal which1352/// preserves ordering. Also updates the values' data.1353pub fn remove_block_param(&mut self, val: Value) {1354let (block, num) =1355if let ValueData::Param { num, block, .. } = ValueData::from(self.values[val]) {1356(block, num)1357} else {1358panic!("{val} must be a block parameter");1359};1360self.blocks[block]1361.params1362.remove(num as usize, &mut self.value_lists);1363for index in num..(self.num_block_params(block) as u16) {1364let packed = &mut self.values[self.blocks[block]1365.params1366.get(index as usize, &self.value_lists)1367.unwrap()];1368let mut data = ValueData::from(*packed);1369match &mut data {1370ValueData::Param { num, .. } => {1371*num -= 1;1372*packed = data.into();1373}1374_ => panic!(1375"{} must be a block parameter",1376self.blocks[block]1377.params1378.get(index as usize, &self.value_lists)1379.unwrap()1380),1381}1382}1383}13841385/// Append an existing value to `block`'s parameters.1386///1387/// The appended value can't already be attached to something else.1388///1389/// In almost all cases, you should be using `append_block_param()` instead of this method.1390pub fn attach_block_param(&mut self, block: Block, param: Value) {1391debug_assert!(!self.value_is_attached(param));1392let num = self.blocks[block].params.push(param, &mut self.value_lists);1393debug_assert!(num <= u16::MAX as usize, "Too many parameters on block");1394let ty = self.value_type(param);1395self.values[param] = ValueData::Param {1396ty,1397num: num as u16,1398block,1399}1400.into();1401}14021403/// Replace a block parameter with a new value of type `ty`.1404///1405/// The `old_value` must be an attached block parameter. It is removed from its place in the list1406/// of parameters and replaced by a new value of type `new_type`. The new value gets the same1407/// position in the list, and other parameters are not disturbed.1408///1409/// The old value is left detached, so it should probably be changed into something else.1410///1411/// Returns the new value.1412pub fn replace_block_param(&mut self, old_value: Value, new_type: Type) -> Value {1413// Create new value identical to the old one except for the type.1414let (block, num) =1415if let ValueData::Param { num, block, .. } = ValueData::from(self.values[old_value]) {1416(block, num)1417} else {1418panic!("{old_value} must be a block parameter");1419};1420let new_arg = self.make_value(ValueData::Param {1421ty: new_type,1422num,1423block,1424});14251426self.blocks[block]1427.params1428.as_mut_slice(&mut self.value_lists)[num as usize] = new_arg;1429new_arg1430}14311432/// Detach all the parameters from `block` and return them as a `ValueList`.1433///1434/// This is a quite low-level operation. Sensible things to do with the detached block parameters1435/// is to put them back on the same block with `attach_block_param()` or change them into aliases1436/// with `change_to_alias()`.1437pub fn detach_block_params(&mut self, block: Block) -> ValueList {1438self.blocks[block].params.take()1439}14401441/// Detach all of an instruction's result values.1442///1443/// This is a quite low-level operation. A sensible thing to do with the1444/// detached results is to change them into aliases with1445/// `change_to_alias()`.1446pub fn detach_inst_results(&mut self, inst: Inst) {1447self.results[inst].clear(&mut self.value_lists);1448}14491450/// Merge the facts for two values. If both values have facts and1451/// they differ, both values get a special "conflict" fact that is1452/// never satisfied.1453pub fn merge_facts(&mut self, a: Value, b: Value) {1454let a = self.resolve_aliases(a);1455let b = self.resolve_aliases(b);1456match (&self.facts[a], &self.facts[b]) {1457(Some(a), Some(b)) if a == b => { /* nothing */ }1458(None, None) => { /* nothing */ }1459(Some(a), None) => {1460self.facts[b] = Some(a.clone());1461}1462(None, Some(b)) => {1463self.facts[a] = Some(b.clone());1464}1465(Some(a_fact), Some(b_fact)) => {1466assert_eq!(self.value_type(a), self.value_type(b));1467let merged = Fact::intersect(a_fact, b_fact);1468crate::trace!(1469"facts merge on {} and {}: {:?}, {:?} -> {:?}",1470a,1471b,1472a_fact,1473b_fact,1474merged,1475);1476self.facts[a] = Some(merged.clone());1477self.facts[b] = Some(merged);1478}1479}1480}1481}14821483/// Contents of a basic block.1484///1485/// Parameters on a basic block are values that dominate everything in the block. All1486/// branches to this block must provide matching arguments, and the arguments to the entry block must1487/// match the function arguments.1488#[derive(Clone, PartialEq, Hash)]1489#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]1490pub struct BlockData {1491/// List of parameters to this block.1492params: ValueList,1493}14941495impl BlockData {1496fn new() -> Self {1497Self {1498params: ValueList::new(),1499}1500}15011502/// Get the parameters on `block`.1503pub fn params<'a>(&self, pool: &'a ValueListPool) -> &'a [Value] {1504self.params.as_slice(pool)1505}1506}15071508/// Object that can display an instruction.1509pub struct DisplayInst<'a>(&'a DataFlowGraph, Inst);15101511impl<'a> fmt::Display for DisplayInst<'a> {1512fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {1513let dfg = self.0;1514let inst = self.1;15151516if let Some((first, rest)) = dfg.inst_results(inst).split_first() {1517write!(f, "{first}")?;1518for v in rest {1519write!(f, ", {v}")?;1520}1521write!(f, " = ")?;1522}15231524let typevar = dfg.ctrl_typevar(inst);1525if typevar.is_invalid() {1526write!(f, "{}", dfg.insts[inst].opcode())?;1527} else {1528write!(f, "{}.{}", dfg.insts[inst].opcode(), typevar)?;1529}1530write_operands(f, dfg, inst)1531}1532}15331534/// Parser routines. These routines should not be used outside the parser.1535impl DataFlowGraph {1536/// Set the type of a value. This is only for use in the parser, which needs1537/// to create invalid values for index padding which may be reassigned later.1538#[cold]1539fn set_value_type_for_parser(&mut self, v: Value, t: Type) {1540assert_eq!(1541self.value_type(v),1542types::INVALID,1543"this function is only for assigning types to previously invalid values"1544);1545self.values[v].set_type(t);1546}15471548/// Check that the given concrete `Type` has been defined in the function.1549pub fn check_dynamic_type(&mut self, ty: Type) -> Option<Type> {1550debug_assert!(ty.is_dynamic_vector());1551if self1552.dynamic_types1553.values()1554.any(|dyn_ty_data| dyn_ty_data.concrete().unwrap() == ty)1555{1556Some(ty)1557} else {1558None1559}1560}15611562/// Create result values for `inst`, reusing the provided detached values.1563/// This is similar to `make_inst_results_reusing` except it's only for use1564/// in the parser, which needs to reuse previously invalid values.1565#[cold]1566pub fn make_inst_results_for_parser(1567&mut self,1568inst: Inst,1569ctrl_typevar: Type,1570reuse: &[Value],1571) -> usize {1572let mut reuse_iter = reuse.iter().copied();1573let result_tys: SmallVec<[_; 16]> = self.inst_result_types(inst, ctrl_typevar).collect();1574for ty in result_tys {1575if ty.is_dynamic_vector() {1576self.check_dynamic_type(ty)1577.unwrap_or_else(|| panic!("Use of undeclared dynamic type: {ty}"));1578}1579if let Some(v) = reuse_iter.next() {1580self.set_value_type_for_parser(v, ty);1581}1582}15831584self.make_inst_results_reusing(inst, ctrl_typevar, reuse.iter().map(|x| Some(*x)))1585}15861587/// Similar to `append_block_param`, append a parameter with type `ty` to1588/// `block`, but using value `val`. This is only for use by the parser to1589/// create parameters with specific values.1590#[cold]1591pub fn append_block_param_for_parser(&mut self, block: Block, ty: Type, val: Value) {1592let num = self.blocks[block].params.push(val, &mut self.value_lists);1593assert!(num <= u16::MAX as usize, "Too many parameters on block");1594self.values[val] = ValueData::Param {1595ty,1596num: num as u16,1597block,1598}1599.into();1600}16011602/// Create a new value alias. This is only for use by the parser to create1603/// aliases with specific values, and the printer for testing.1604#[cold]1605pub fn make_value_alias_for_serialization(&mut self, src: Value, dest: Value) {1606assert_ne!(src, Value::reserved_value());1607assert_ne!(dest, Value::reserved_value());16081609let ty = if self.values.is_valid(src) {1610self.value_type(src)1611} else {1612// As a special case, if we can't resolve the aliasee yet, use INVALID1613// temporarily. It will be resolved later in parsing.1614types::INVALID1615};1616let data = ValueData::Alias { ty, original: src };1617self.values[dest] = data.into();1618}16191620/// If `v` is already defined as an alias, return its destination value.1621/// Otherwise return None. This allows the parser to coalesce identical1622/// alias definitions, and the printer to identify an alias's immediate target.1623#[cold]1624pub fn value_alias_dest_for_serialization(&self, v: Value) -> Option<Value> {1625if let ValueData::Alias { original, .. } = ValueData::from(self.values[v]) {1626Some(original)1627} else {1628None1629}1630}16311632/// Compute the type of an alias. This is only for use in the parser.1633/// Returns false if an alias cycle was encountered.1634#[cold]1635pub fn set_alias_type_for_parser(&mut self, v: Value) -> bool {1636if let Some(resolved) = maybe_resolve_aliases(&self.values, v) {1637let old_ty = self.value_type(v);1638let new_ty = self.value_type(resolved);1639if old_ty == types::INVALID {1640self.set_value_type_for_parser(v, new_ty);1641} else {1642assert_eq!(old_ty, new_ty);1643}1644true1645} else {1646false1647}1648}16491650/// Create an invalid value, to pad the index space. This is only for use by1651/// the parser to pad out the value index space.1652#[cold]1653pub fn make_invalid_value_for_parser(&mut self) {1654let data = ValueData::Alias {1655ty: types::INVALID,1656original: Value::reserved_value(),1657};1658self.make_value(data);1659}16601661/// Check if a value reference is valid, while being aware of aliases which1662/// may be unresolved while parsing.1663#[cold]1664pub fn value_is_valid_for_parser(&self, v: Value) -> bool {1665if !self.value_is_valid(v) {1666return false;1667}1668if let ValueData::Alias { ty, .. } = ValueData::from(self.values[v]) {1669ty != types::INVALID1670} else {1671true1672}1673}1674}16751676#[cfg(test)]1677mod tests {1678use super::*;1679use crate::cursor::{Cursor, FuncCursor};1680use crate::ir::{Function, Opcode, TrapCode};1681use alloc::string::ToString;16821683#[test]1684fn make_inst() {1685let mut dfg = DataFlowGraph::new();16861687let idata = InstructionData::UnaryImm {1688opcode: Opcode::Iconst,1689imm: 0.into(),1690};1691let inst = dfg.make_inst(idata);16921693dfg.make_inst_results(inst, types::I32);1694assert_eq!(inst.to_string(), "inst0");1695assert_eq!(dfg.display_inst(inst).to_string(), "v0 = iconst.i32 0");16961697// Immutable reference resolution.1698{1699let immdfg = &dfg;1700let ins = &immdfg.insts[inst];1701assert_eq!(ins.opcode(), Opcode::Iconst);1702}17031704// Results.1705let val = dfg.first_result(inst);1706assert_eq!(dfg.inst_results(inst), &[val]);17071708assert_eq!(dfg.value_def(val), ValueDef::Result(inst, 0));1709assert_eq!(dfg.value_type(val), types::I32);17101711// Replacing results.1712assert!(dfg.value_is_attached(val));1713let v2 = dfg.replace_result(val, types::F64);1714assert!(!dfg.value_is_attached(val));1715assert!(dfg.value_is_attached(v2));1716assert_eq!(dfg.inst_results(inst), &[v2]);1717assert_eq!(dfg.value_def(v2), ValueDef::Result(inst, 0));1718assert_eq!(dfg.value_type(v2), types::F64);1719}17201721#[test]1722fn no_results() {1723let mut dfg = DataFlowGraph::new();17241725let idata = InstructionData::Trap {1726opcode: Opcode::Trap,1727code: TrapCode::unwrap_user(1),1728};1729let inst = dfg.make_inst(idata);1730assert_eq!(dfg.display_inst(inst).to_string(), "trap user1");17311732// Result slice should be empty.1733assert_eq!(dfg.inst_results(inst), &[]);1734}17351736#[test]1737fn block() {1738let mut dfg = DataFlowGraph::new();17391740let block = dfg.make_block();1741assert_eq!(block.to_string(), "block0");1742assert_eq!(dfg.num_block_params(block), 0);1743assert_eq!(dfg.block_params(block), &[]);1744assert!(dfg.detach_block_params(block).is_empty());1745assert_eq!(dfg.num_block_params(block), 0);1746assert_eq!(dfg.block_params(block), &[]);17471748let arg1 = dfg.append_block_param(block, types::F32);1749assert_eq!(arg1.to_string(), "v0");1750assert_eq!(dfg.num_block_params(block), 1);1751assert_eq!(dfg.block_params(block), &[arg1]);17521753let arg2 = dfg.append_block_param(block, types::I16);1754assert_eq!(arg2.to_string(), "v1");1755assert_eq!(dfg.num_block_params(block), 2);1756assert_eq!(dfg.block_params(block), &[arg1, arg2]);17571758assert_eq!(dfg.value_def(arg1), ValueDef::Param(block, 0));1759assert_eq!(dfg.value_def(arg2), ValueDef::Param(block, 1));1760assert_eq!(dfg.value_type(arg1), types::F32);1761assert_eq!(dfg.value_type(arg2), types::I16);17621763// Swap the two block parameters.1764let vlist = dfg.detach_block_params(block);1765assert_eq!(dfg.num_block_params(block), 0);1766assert_eq!(dfg.block_params(block), &[]);1767assert_eq!(vlist.as_slice(&dfg.value_lists), &[arg1, arg2]);1768dfg.attach_block_param(block, arg2);1769let arg3 = dfg.append_block_param(block, types::I32);1770dfg.attach_block_param(block, arg1);1771assert_eq!(dfg.block_params(block), &[arg2, arg3, arg1]);1772}17731774#[test]1775fn replace_block_params() {1776let mut dfg = DataFlowGraph::new();17771778let block = dfg.make_block();1779let arg1 = dfg.append_block_param(block, types::F32);17801781let new1 = dfg.replace_block_param(arg1, types::I64);1782assert_eq!(dfg.value_type(arg1), types::F32);1783assert_eq!(dfg.value_type(new1), types::I64);1784assert_eq!(dfg.block_params(block), &[new1]);17851786dfg.attach_block_param(block, arg1);1787assert_eq!(dfg.block_params(block), &[new1, arg1]);17881789let new2 = dfg.replace_block_param(arg1, types::I8);1790assert_eq!(dfg.value_type(arg1), types::F32);1791assert_eq!(dfg.value_type(new2), types::I8);1792assert_eq!(dfg.block_params(block), &[new1, new2]);17931794dfg.attach_block_param(block, arg1);1795assert_eq!(dfg.block_params(block), &[new1, new2, arg1]);17961797let new3 = dfg.replace_block_param(new2, types::I16);1798assert_eq!(dfg.value_type(new1), types::I64);1799assert_eq!(dfg.value_type(new2), types::I8);1800assert_eq!(dfg.value_type(new3), types::I16);1801assert_eq!(dfg.block_params(block), &[new1, new3, arg1]);1802}18031804#[test]1805fn swap_remove_block_params() {1806let mut dfg = DataFlowGraph::new();18071808let block = dfg.make_block();1809let arg1 = dfg.append_block_param(block, types::F32);1810let arg2 = dfg.append_block_param(block, types::F32);1811let arg3 = dfg.append_block_param(block, types::F32);1812assert_eq!(dfg.block_params(block), &[arg1, arg2, arg3]);18131814dfg.swap_remove_block_param(arg1);1815assert_eq!(dfg.value_is_attached(arg1), false);1816assert_eq!(dfg.value_is_attached(arg2), true);1817assert_eq!(dfg.value_is_attached(arg3), true);1818assert_eq!(dfg.block_params(block), &[arg3, arg2]);1819dfg.swap_remove_block_param(arg2);1820assert_eq!(dfg.value_is_attached(arg2), false);1821assert_eq!(dfg.value_is_attached(arg3), true);1822assert_eq!(dfg.block_params(block), &[arg3]);1823dfg.swap_remove_block_param(arg3);1824assert_eq!(dfg.value_is_attached(arg3), false);1825assert_eq!(dfg.block_params(block), &[]);1826}18271828#[test]1829fn aliases() {1830use crate::ir::InstBuilder;1831use crate::ir::condcodes::IntCC;18321833let mut func = Function::new();1834let block0 = func.dfg.make_block();1835let mut pos = FuncCursor::new(&mut func);1836pos.insert_block(block0);18371838// Build a little test program.1839let v1 = pos.ins().iconst(types::I32, 42);18401841// Make sure we can resolve value aliases even when values is empty.1842assert_eq!(pos.func.dfg.resolve_aliases(v1), v1);18431844let arg0 = pos.func.dfg.append_block_param(block0, types::I32);1845let (s, c) = pos.ins().uadd_overflow(v1, arg0);1846let iadd = match pos.func.dfg.value_def(s) {1847ValueDef::Result(i, 0) => i,1848_ => panic!(),1849};18501851// Remove `c` from the result list.1852pos.func.stencil.dfg.results[iadd].remove(1, &mut pos.func.stencil.dfg.value_lists);18531854// Replace `uadd_overflow` with a normal `iadd` and an `icmp`.1855pos.func.dfg.replace(iadd).iadd(v1, arg0);1856let c2 = pos.ins().icmp(IntCC::Equal, s, v1);1857pos.func.dfg.change_to_alias(c, c2);18581859assert_eq!(pos.func.dfg.resolve_aliases(c2), c2);1860assert_eq!(pos.func.dfg.resolve_aliases(c), c2);1861}18621863#[test]1864fn cloning() {1865use crate::ir::InstBuilder;18661867let mut func = Function::new();1868let mut sig = Signature::new(crate::isa::CallConv::SystemV);1869sig.params.push(ir::AbiParam::new(types::I32));1870let sig = func.import_signature(sig);1871let block0 = func.dfg.make_block();1872let mut pos = FuncCursor::new(&mut func);1873pos.insert_block(block0);1874let v1 = pos.ins().iconst(types::I32, 0);1875let v2 = pos.ins().iconst(types::I32, 1);1876let call_inst = pos.ins().call_indirect(sig, v1, &[v1]);1877let func = pos.func;18781879let call_inst_dup = func.dfg.clone_inst(call_inst);1880func.dfg.inst_args_mut(call_inst)[0] = v2;1881assert_eq!(v1, func.dfg.inst_args(call_inst_dup)[0]);1882}1883}188418851886