Path: blob/main/cranelift/codegen/src/ir/exception_table.rs
1693 views
//! Exception tables: catch handlers on `try_call` instructions.1//!2//! An exception table describes where execution flows after returning3//! from `try_call`. It contains both the "normal" destination -- the4//! block to branch to when the function returns without throwing an5//! exception -- and any "catch" destinations associated with6//! particular exception tags. Each target indicates the arguments to7//! pass to the block that receives control.8//!9//! Like other side-tables (e.g., jump tables), each exception table10//! must be used by only one instruction. Sharing is not permitted11//! because it can complicate transforms (how does one change the12//! table used by only one instruction if others also use it?).13//!14//! In order to allow the `try_call` instruction itself to remain15//! small, the exception table also contains the signature ID of the16//! called function.1718use crate::ir::entities::{ExceptionTag, SigRef};19use crate::ir::instructions::ValueListPool;20use crate::ir::{BlockCall, Value};21use alloc::vec::Vec;22use core::fmt::{self, Display, Formatter};23#[cfg(feature = "enable-serde")]24use serde_derive::{Deserialize, Serialize};2526/// Contents of an exception table.27///28/// An exception table consists of a "no exception" ("normal")29/// destination block-call, and a series of exceptional destination30/// block-calls associated with tags.31///32/// The exceptional tags can also be interspersed with "dynamic33/// context" entries, which result in a particular value being stored34/// in the stack frame and accessible at an offset given in the35/// compiled exception-table metadata. This is needed for some kinds36/// of tag-matching where different dynamic instances of tags may37/// exist (e.g., in the WebAssembly exception-handling proposal).38///39/// The sequence of targets is semantically a list of40/// context-or-tagged-blockcall; e.g., `[context v0, tag1: block1(v1,41/// v2), context v2, tag2: block2(), tag3: block3()]`.42///43/// The "no exception" target can be accessed through the44/// `normal_return` and `normal_return_mut` functions. Exceptional45/// catch clauses may be iterated using the `catches` and46/// `catches_mut` functions. All targets may be iterated over using47/// the `all_targets` and `all_targets_mut` functions.48#[derive(Debug, Clone, PartialEq, Hash)]49#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]50pub struct ExceptionTableData {51/// All BlockCalls packed together. This is necessary because the52/// rest of the compiler expects to be able to grab a slice of53/// branch targets for any branch instruction. The last BlockCall54/// is the normal-return destination, and the rest are referred to55/// by index by the `items` below.56targets: Vec<BlockCall>,5758/// Exception-table items.59///60/// This internal representation for items is like61/// `ExceptionTableItem` except that it has indices that refer to62/// `targets` above.63///64/// A tag value of `None` indicates a catch-all handler. The65/// catch-all handler matches only if no other handler matches,66/// regardless of the order in this vector.67///68/// `tags[i]` corresponds to `targets[i]`. Note that there will be69/// one more `targets` element than `tags` because the last70/// element in `targets` is the normal-return path.71items: Vec<InternalExceptionTableItem>,7273/// The signature of the function whose invocation is associated74/// with this handler table.75sig: SigRef,76}7778/// A single item in the match-list of an exception table.79#[derive(Clone, Debug)]80pub enum ExceptionTableItem {81/// A tag match, taking the specified block-call destination if82/// the tag matches the one in the thrown exception. (The match83/// predicate is up to the runtime; Cranelift only emits metadata84/// containing this tag.)85Tag(ExceptionTag, BlockCall),86/// A default match, always taking the specified block-call87/// destination.88Default(BlockCall),89/// A dynamic context update, applying to all tags until the next90/// update. (Cranelift does not interpret this context, but only91/// provides information to the runtime regarding where to find92/// it.)93Context(Value),94}9596/// Our internal representation of exception-table items.97///98/// This is a version of `ExceptionTableItem` with block-calls99/// out-of-lined so that we can provide the slice externally. Each100/// block-call is referenced via an index.101#[derive(Clone, Debug, PartialEq, Hash)]102#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]103enum InternalExceptionTableItem {104Tag(ExceptionTag, u32),105Default(u32),106Context(Value),107}108109impl ExceptionTableData {110/// Create new exception-table data.111///112/// This data represents the destinations upon return from113/// `try_call` or `try_call_indirect` instruction. There are two114/// possibilities: "normal return" (no exception thrown), or an115/// exceptional return corresponding to one of the listed116/// exception tags.117///118/// The given tags are passed through to the metadata provided119/// alongside the provided function body, and Cranelift itself120/// does not implement an unwinder; thus, the meaning of the tags121/// is ultimately up to the embedder of Cranelift. The tags are122/// wrapped in `Option` to allow encoding a "catch-all" handler.123///124/// The BlockCalls must have signatures that match the targeted125/// blocks, as usual. These calls are allowed to use126/// `BlockArg::TryCallRet` in the normal-return case, with types127/// corresponding to the signature's return values, and128/// `BlockArg::TryCallExn` in the exceptional-return cases, with129/// types corresponding to native machine words and an arity130/// corresponding to the number of payload values that the calling131/// convention and platform support. (See [`isa::CallConv`] for132/// more details.)133pub fn new(134sig: SigRef,135normal_return: BlockCall,136matches: impl IntoIterator<Item = ExceptionTableItem>,137) -> Self {138let mut targets = vec![];139let mut items = vec![];140for item in matches {141let target_idx = u32::try_from(targets.len()).unwrap();142match item {143ExceptionTableItem::Tag(tag, target) => {144items.push(InternalExceptionTableItem::Tag(tag, target_idx));145targets.push(target);146}147ExceptionTableItem::Default(target) => {148items.push(InternalExceptionTableItem::Default(target_idx));149targets.push(target);150}151ExceptionTableItem::Context(ctx) => {152items.push(InternalExceptionTableItem::Context(ctx));153}154}155}156targets.push(normal_return);157158ExceptionTableData {159targets,160items,161sig,162}163}164165/// Return a value that can display the contents of this exception166/// table.167pub fn display<'a>(&'a self, pool: &'a ValueListPool) -> DisplayExceptionTable<'a> {168DisplayExceptionTable { table: self, pool }169}170171/// Deep-clone this exception table.172pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {173Self {174targets: self.targets.iter().map(|b| b.deep_clone(pool)).collect(),175items: self.items.clone(),176sig: self.sig,177}178}179180/// Get the default target for the non-exceptional return case.181pub fn normal_return(&self) -> &BlockCall {182self.targets.last().unwrap()183}184185/// Get the default target for the non-exceptional return case.186pub fn normal_return_mut(&mut self) -> &mut BlockCall {187self.targets.last_mut().unwrap()188}189190/// Get the exception-catch items: dynamic context updates for191/// interpreting tags, tag-associated targets, and catch-all192/// targets.193pub fn items(&self) -> impl Iterator<Item = ExceptionTableItem> + '_ {194self.items.iter().map(|item| match item {195InternalExceptionTableItem::Tag(tag, target_idx) => {196ExceptionTableItem::Tag(*tag, self.targets[usize::try_from(*target_idx).unwrap()])197}198InternalExceptionTableItem::Default(target_idx) => {199ExceptionTableItem::Default(self.targets[usize::try_from(*target_idx).unwrap()])200}201InternalExceptionTableItem::Context(ctx) => ExceptionTableItem::Context(*ctx),202})203}204205/// Get all branch targets.206pub fn all_branches(&self) -> &[BlockCall] {207&self.targets[..]208}209210/// Get all branch targets.211pub fn all_branches_mut(&mut self) -> &mut [BlockCall] {212&mut self.targets[..]213}214215/// Get the signature of the function called with this exception216/// table.217pub fn signature(&self) -> SigRef {218self.sig219}220221/// Get a mutable handle to this exception table's signature.222pub(crate) fn signature_mut(&mut self) -> &mut SigRef {223&mut self.sig224}225226/// Get an iterator over context values.227pub(crate) fn contexts(&self) -> impl DoubleEndedIterator<Item = Value> {228self.items.iter().filter_map(|item| match item {229InternalExceptionTableItem::Context(ctx) => Some(*ctx),230_ => None,231})232}233234/// Get a mutable iterator over context values.235pub(crate) fn contexts_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Value> {236self.items.iter_mut().filter_map(|item| match item {237InternalExceptionTableItem::Context(ctx) => Some(ctx),238_ => None,239})240}241242/// Clears all entries in this exception table, but leaves the function signature.243pub fn clear(&mut self) {244self.items.clear();245self.targets.clear();246}247}248249/// A wrapper for the context required to display a250/// [ExceptionTableData].251pub struct DisplayExceptionTable<'a> {252table: &'a ExceptionTableData,253pool: &'a ValueListPool,254}255256impl<'a> Display for DisplayExceptionTable<'a> {257fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {258write!(259fmt,260"{}, {}, [",261self.table.sig,262self.table.normal_return().display(self.pool)263)?;264let mut first = true;265for item in self.table.items() {266if first {267write!(fmt, " ")?;268first = false;269} else {270write!(fmt, ", ")?;271}272match item {273ExceptionTableItem::Tag(tag, block_call) => {274write!(fmt, "{}: {}", tag, block_call.display(self.pool))?;275}276ExceptionTableItem::Default(block_call) => {277write!(fmt, "default: {}", block_call.display(self.pool))?;278}279ExceptionTableItem::Context(ctx) => {280write!(fmt, "context {ctx}")?;281}282}283}284let space = if first { "" } else { " " };285write!(fmt, "{space}]")?;286Ok(())287}288}289290291