Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/exception_table.rs
1693 views
1
//! Exception tables: catch handlers on `try_call` instructions.
2
//!
3
//! An exception table describes where execution flows after returning
4
//! from `try_call`. It contains both the "normal" destination -- the
5
//! block to branch to when the function returns without throwing an
6
//! exception -- and any "catch" destinations associated with
7
//! particular exception tags. Each target indicates the arguments to
8
//! pass to the block that receives control.
9
//!
10
//! Like other side-tables (e.g., jump tables), each exception table
11
//! must be used by only one instruction. Sharing is not permitted
12
//! because it can complicate transforms (how does one change the
13
//! table used by only one instruction if others also use it?).
14
//!
15
//! In order to allow the `try_call` instruction itself to remain
16
//! small, the exception table also contains the signature ID of the
17
//! called function.
18
19
use crate::ir::entities::{ExceptionTag, SigRef};
20
use crate::ir::instructions::ValueListPool;
21
use crate::ir::{BlockCall, Value};
22
use alloc::vec::Vec;
23
use core::fmt::{self, Display, Formatter};
24
#[cfg(feature = "enable-serde")]
25
use serde_derive::{Deserialize, Serialize};
26
27
/// Contents of an exception table.
28
///
29
/// An exception table consists of a "no exception" ("normal")
30
/// destination block-call, and a series of exceptional destination
31
/// block-calls associated with tags.
32
///
33
/// The exceptional tags can also be interspersed with "dynamic
34
/// context" entries, which result in a particular value being stored
35
/// in the stack frame and accessible at an offset given in the
36
/// compiled exception-table metadata. This is needed for some kinds
37
/// of tag-matching where different dynamic instances of tags may
38
/// exist (e.g., in the WebAssembly exception-handling proposal).
39
///
40
/// The sequence of targets is semantically a list of
41
/// context-or-tagged-blockcall; e.g., `[context v0, tag1: block1(v1,
42
/// v2), context v2, tag2: block2(), tag3: block3()]`.
43
///
44
/// The "no exception" target can be accessed through the
45
/// `normal_return` and `normal_return_mut` functions. Exceptional
46
/// catch clauses may be iterated using the `catches` and
47
/// `catches_mut` functions. All targets may be iterated over using
48
/// the `all_targets` and `all_targets_mut` functions.
49
#[derive(Debug, Clone, PartialEq, Hash)]
50
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
51
pub struct ExceptionTableData {
52
/// All BlockCalls packed together. This is necessary because the
53
/// rest of the compiler expects to be able to grab a slice of
54
/// branch targets for any branch instruction. The last BlockCall
55
/// is the normal-return destination, and the rest are referred to
56
/// by index by the `items` below.
57
targets: Vec<BlockCall>,
58
59
/// Exception-table items.
60
///
61
/// This internal representation for items is like
62
/// `ExceptionTableItem` except that it has indices that refer to
63
/// `targets` above.
64
///
65
/// A tag value of `None` indicates a catch-all handler. The
66
/// catch-all handler matches only if no other handler matches,
67
/// regardless of the order in this vector.
68
///
69
/// `tags[i]` corresponds to `targets[i]`. Note that there will be
70
/// one more `targets` element than `tags` because the last
71
/// element in `targets` is the normal-return path.
72
items: Vec<InternalExceptionTableItem>,
73
74
/// The signature of the function whose invocation is associated
75
/// with this handler table.
76
sig: SigRef,
77
}
78
79
/// A single item in the match-list of an exception table.
80
#[derive(Clone, Debug)]
81
pub enum ExceptionTableItem {
82
/// A tag match, taking the specified block-call destination if
83
/// the tag matches the one in the thrown exception. (The match
84
/// predicate is up to the runtime; Cranelift only emits metadata
85
/// containing this tag.)
86
Tag(ExceptionTag, BlockCall),
87
/// A default match, always taking the specified block-call
88
/// destination.
89
Default(BlockCall),
90
/// A dynamic context update, applying to all tags until the next
91
/// update. (Cranelift does not interpret this context, but only
92
/// provides information to the runtime regarding where to find
93
/// it.)
94
Context(Value),
95
}
96
97
/// Our internal representation of exception-table items.
98
///
99
/// This is a version of `ExceptionTableItem` with block-calls
100
/// out-of-lined so that we can provide the slice externally. Each
101
/// block-call is referenced via an index.
102
#[derive(Clone, Debug, PartialEq, Hash)]
103
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
104
enum InternalExceptionTableItem {
105
Tag(ExceptionTag, u32),
106
Default(u32),
107
Context(Value),
108
}
109
110
impl ExceptionTableData {
111
/// Create new exception-table data.
112
///
113
/// This data represents the destinations upon return from
114
/// `try_call` or `try_call_indirect` instruction. There are two
115
/// possibilities: "normal return" (no exception thrown), or an
116
/// exceptional return corresponding to one of the listed
117
/// exception tags.
118
///
119
/// The given tags are passed through to the metadata provided
120
/// alongside the provided function body, and Cranelift itself
121
/// does not implement an unwinder; thus, the meaning of the tags
122
/// is ultimately up to the embedder of Cranelift. The tags are
123
/// wrapped in `Option` to allow encoding a "catch-all" handler.
124
///
125
/// The BlockCalls must have signatures that match the targeted
126
/// blocks, as usual. These calls are allowed to use
127
/// `BlockArg::TryCallRet` in the normal-return case, with types
128
/// corresponding to the signature's return values, and
129
/// `BlockArg::TryCallExn` in the exceptional-return cases, with
130
/// types corresponding to native machine words and an arity
131
/// corresponding to the number of payload values that the calling
132
/// convention and platform support. (See [`isa::CallConv`] for
133
/// more details.)
134
pub fn new(
135
sig: SigRef,
136
normal_return: BlockCall,
137
matches: impl IntoIterator<Item = ExceptionTableItem>,
138
) -> Self {
139
let mut targets = vec![];
140
let mut items = vec![];
141
for item in matches {
142
let target_idx = u32::try_from(targets.len()).unwrap();
143
match item {
144
ExceptionTableItem::Tag(tag, target) => {
145
items.push(InternalExceptionTableItem::Tag(tag, target_idx));
146
targets.push(target);
147
}
148
ExceptionTableItem::Default(target) => {
149
items.push(InternalExceptionTableItem::Default(target_idx));
150
targets.push(target);
151
}
152
ExceptionTableItem::Context(ctx) => {
153
items.push(InternalExceptionTableItem::Context(ctx));
154
}
155
}
156
}
157
targets.push(normal_return);
158
159
ExceptionTableData {
160
targets,
161
items,
162
sig,
163
}
164
}
165
166
/// Return a value that can display the contents of this exception
167
/// table.
168
pub fn display<'a>(&'a self, pool: &'a ValueListPool) -> DisplayExceptionTable<'a> {
169
DisplayExceptionTable { table: self, pool }
170
}
171
172
/// Deep-clone this exception table.
173
pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
174
Self {
175
targets: self.targets.iter().map(|b| b.deep_clone(pool)).collect(),
176
items: self.items.clone(),
177
sig: self.sig,
178
}
179
}
180
181
/// Get the default target for the non-exceptional return case.
182
pub fn normal_return(&self) -> &BlockCall {
183
self.targets.last().unwrap()
184
}
185
186
/// Get the default target for the non-exceptional return case.
187
pub fn normal_return_mut(&mut self) -> &mut BlockCall {
188
self.targets.last_mut().unwrap()
189
}
190
191
/// Get the exception-catch items: dynamic context updates for
192
/// interpreting tags, tag-associated targets, and catch-all
193
/// targets.
194
pub fn items(&self) -> impl Iterator<Item = ExceptionTableItem> + '_ {
195
self.items.iter().map(|item| match item {
196
InternalExceptionTableItem::Tag(tag, target_idx) => {
197
ExceptionTableItem::Tag(*tag, self.targets[usize::try_from(*target_idx).unwrap()])
198
}
199
InternalExceptionTableItem::Default(target_idx) => {
200
ExceptionTableItem::Default(self.targets[usize::try_from(*target_idx).unwrap()])
201
}
202
InternalExceptionTableItem::Context(ctx) => ExceptionTableItem::Context(*ctx),
203
})
204
}
205
206
/// Get all branch targets.
207
pub fn all_branches(&self) -> &[BlockCall] {
208
&self.targets[..]
209
}
210
211
/// Get all branch targets.
212
pub fn all_branches_mut(&mut self) -> &mut [BlockCall] {
213
&mut self.targets[..]
214
}
215
216
/// Get the signature of the function called with this exception
217
/// table.
218
pub fn signature(&self) -> SigRef {
219
self.sig
220
}
221
222
/// Get a mutable handle to this exception table's signature.
223
pub(crate) fn signature_mut(&mut self) -> &mut SigRef {
224
&mut self.sig
225
}
226
227
/// Get an iterator over context values.
228
pub(crate) fn contexts(&self) -> impl DoubleEndedIterator<Item = Value> {
229
self.items.iter().filter_map(|item| match item {
230
InternalExceptionTableItem::Context(ctx) => Some(*ctx),
231
_ => None,
232
})
233
}
234
235
/// Get a mutable iterator over context values.
236
pub(crate) fn contexts_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Value> {
237
self.items.iter_mut().filter_map(|item| match item {
238
InternalExceptionTableItem::Context(ctx) => Some(ctx),
239
_ => None,
240
})
241
}
242
243
/// Clears all entries in this exception table, but leaves the function signature.
244
pub fn clear(&mut self) {
245
self.items.clear();
246
self.targets.clear();
247
}
248
}
249
250
/// A wrapper for the context required to display a
251
/// [ExceptionTableData].
252
pub struct DisplayExceptionTable<'a> {
253
table: &'a ExceptionTableData,
254
pool: &'a ValueListPool,
255
}
256
257
impl<'a> Display for DisplayExceptionTable<'a> {
258
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
259
write!(
260
fmt,
261
"{}, {}, [",
262
self.table.sig,
263
self.table.normal_return().display(self.pool)
264
)?;
265
let mut first = true;
266
for item in self.table.items() {
267
if first {
268
write!(fmt, " ")?;
269
first = false;
270
} else {
271
write!(fmt, ", ")?;
272
}
273
match item {
274
ExceptionTableItem::Tag(tag, block_call) => {
275
write!(fmt, "{}: {}", tag, block_call.display(self.pool))?;
276
}
277
ExceptionTableItem::Default(block_call) => {
278
write!(fmt, "default: {}", block_call.display(self.pool))?;
279
}
280
ExceptionTableItem::Context(ctx) => {
281
write!(fmt, "context {ctx}")?;
282
}
283
}
284
}
285
let space = if first { "" } else { " " };
286
write!(fmt, "{space}]")?;
287
Ok(())
288
}
289
}
290
291