Skip to content

Commit 7a0489a

Browse files
committed
Cranelift: add get_exception_handler_address.
This is designed to enable applications such as bytecodealliance#11592 that use alternative unwinding mechanisms that may not necessarily want to walk a stack and look up exception tables. The idea is that whenever it would be valid to resume to an exception handler that is active on the stack, we can provide the same PC as a first-class runtime value that would be found in the exception table for the given handler edge. A "custom" resume step can then use this PC as a resume-point as long as it follows the relevant exception ABI (i.e.: restore SP, FP, any other saved registers that the exception ABI specifies, and provide appropriate payload value(s)). Handlers are associated with edges out of `try_call`s (or `try_call_indirect`s); and edges specifically, not blocks, because there could be multiple out-edges to one block. The instruction thus takes the block that contains the try-call and an immediate that indexes its exceptional edges. This CLIF instruction required a bit of infrastructure to (i) allow naming raw blocks, not just block calls, as instruction arguments, and (ii) allow getting the MachLabel for any other lowered block during lowering. But given that, the lowerings themselves are straightforward uses of MachBuffer labels to fix-up PC-relative address-loading instructions (e.g., `LEA` or `ADR` or `AUIPC`+`ADDI`).
1 parent db7b44d commit 7a0489a

File tree

46 files changed

+1205
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1205
-12
lines changed

cranelift/codegen/meta/src/cdsl/formats.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub(crate) struct InstructionFormat {
4040

4141
pub num_block_operands: usize,
4242

43+
pub num_raw_block_operands: usize,
44+
4345
/// Index of the value input operand that is used to infer the controlling type variable. By
4446
/// default, this is `0`, the first `value` operand. The index is relative to the values only,
4547
/// ignoring immediate operands.
@@ -52,6 +54,7 @@ pub(crate) struct FormatStructure {
5254
pub num_value_operands: usize,
5355
pub has_value_list: bool,
5456
pub num_block_operands: usize,
57+
pub num_raw_block_operands: usize,
5558
/// Tuples of (Rust field name / Rust type) for each immediate field.
5659
pub imm_field_names: Vec<(&'static str, &'static str)>,
5760
}
@@ -65,8 +68,12 @@ impl fmt::Display for InstructionFormat {
6568
.collect::<Vec<_>>()
6669
.join(", ");
6770
fmt.write_fmt(format_args!(
68-
"{}(imms=({}), vals={}, blocks={})",
69-
self.name, imm_args, self.num_value_operands, self.num_block_operands,
71+
"{}(imms=({}), vals={}, blocks={}, raw_blocks={})",
72+
self.name,
73+
imm_args,
74+
self.num_value_operands,
75+
self.num_block_operands,
76+
self.num_raw_block_operands,
7077
))?;
7178
Ok(())
7279
}
@@ -79,6 +86,7 @@ impl InstructionFormat {
7986
num_value_operands: self.num_value_operands,
8087
has_value_list: self.has_value_list,
8188
num_block_operands: self.num_block_operands,
89+
num_raw_block_operands: self.num_raw_block_operands,
8290
imm_field_names: self
8391
.imm_fields
8492
.iter()
@@ -97,6 +105,7 @@ impl InstructionFormatBuilder {
97105
num_value_operands: 0,
98106
has_value_list: false,
99107
num_block_operands: 0,
108+
num_raw_block_operands: 0,
100109
imm_fields: Vec::new(),
101110
typevar_operand: None,
102111
})
@@ -117,6 +126,11 @@ impl InstructionFormatBuilder {
117126
self
118127
}
119128

129+
pub fn raw_block(mut self) -> Self {
130+
self.0.num_raw_block_operands += 1;
131+
self
132+
}
133+
120134
pub fn imm(mut self, operand_kind: &OperandKind) -> Self {
121135
let field = FormatField {
122136
kind: operand_kind.clone(),

cranelift/codegen/meta/src/cdsl/instructions.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionF
288288
// - whether it has a value list or not.
289289
let mut num_values = 0;
290290
let mut num_blocks = 0;
291+
let mut num_raw_blocks = 0;
291292
let mut num_immediates = 0;
292293

293294
for operand in operands_in.iter() {
@@ -304,6 +305,8 @@ fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionF
304305
}
305306
if operand.kind.is_block() {
306307
num_blocks += 1;
308+
} else if operand.kind.is_raw_block() {
309+
num_raw_blocks += 1;
307310
} else if operand.is_immediate_or_entityref() {
308311
if let Some(format_field) = format.imm_fields.get(num_immediates) {
309312
assert_eq!(
@@ -335,6 +338,13 @@ fn verify_format(inst_name: &str, operands_in: &[Operand], format: &InstructionF
335338
inst_name, format.name,
336339
);
337340

341+
assert_eq!(
342+
num_raw_blocks, format.num_raw_block_operands,
343+
"inst {} doesn't have as many raw-block input operands as its format {} declares; you may need \
344+
to use a different format.",
345+
inst_name, format.name,
346+
);
347+
338348
assert_eq!(
339349
num_immediates,
340350
format.imm_fields.len(),

cranelift/codegen/meta/src/cdsl/operands.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ impl OperandKind {
150150
pub(crate) fn is_block(&self) -> bool {
151151
self.rust_type == "ir::BlockCall"
152152
}
153+
154+
pub(crate) fn is_raw_block(&self) -> bool {
155+
self.rust_type == "ir::Block"
156+
}
153157
}
154158

155159
impl From<&TypeVar> for OperandKind {

cranelift/codegen/meta/src/gen_inst.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ fn gen_instruction_data(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter)
8787
n => panic!("Too many block operands in instruction: {n}"),
8888
}
8989

90+
match format.num_raw_block_operands {
91+
0 => (),
92+
1 => fmt.line("block: ir::Block,"),
93+
n => panic!("Too many block operands in instruction: {n}"),
94+
}
95+
9096
for field in &format.imm_fields {
9197
fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);
9298
}
@@ -249,6 +255,15 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
249255
}
250256
};
251257

258+
let raw_blocks_eq = match format.num_raw_block_operands {
259+
0 => None,
260+
1 => {
261+
members.push("block");
262+
Some("block1 == block2")
263+
}
264+
_ => unreachable!("Not a valid format"),
265+
};
266+
252267
for field in &format.imm_fields {
253268
members.push(field.member);
254269
}
@@ -266,6 +281,9 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
266281
if let Some(blocks_eq) = blocks_eq {
267282
fmtln!(fmt, "&& {}", blocks_eq);
268283
}
284+
if let Some(raw_blocks_eq) = raw_blocks_eq {
285+
fmtln!(fmt, "&& {}", raw_blocks_eq);
286+
}
269287
});
270288
}
271289
fmt.line("_ => unreachable!()");
@@ -314,6 +332,15 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
314332
}
315333
};
316334

335+
let raw_block = match format.num_raw_block_operands {
336+
0 => None,
337+
1 => {
338+
members.push("block");
339+
Some("block")
340+
}
341+
_ => panic!("Too many raw block operands"),
342+
};
343+
317344
for field in &format.imm_fields {
318345
members.push(field.member);
319346
}
@@ -341,6 +368,10 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
341368
});
342369
});
343370
}
371+
372+
if let Some(raw_block) = raw_block {
373+
fmtln!(fmt, "::core::hash::Hash::hash(&{raw_block}, state);");
374+
}
344375
});
345376
}
346377
});
@@ -378,6 +409,14 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
378409
}
379410
};
380411

412+
match format.num_raw_block_operands {
413+
0 => {}
414+
1 => {
415+
members.push("block");
416+
}
417+
_ => panic!("Too many raw-block operands to format"),
418+
}
419+
381420
for field in &format.imm_fields {
382421
members.push(field.member);
383422
}
@@ -406,6 +445,14 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
406445
_ => panic!("Too many block targets in instruction"),
407446
}
408447

448+
match format.num_raw_block_operands {
449+
0 => {}
450+
1 => {
451+
fmtln!(fmt, "block,");
452+
}
453+
_ => panic!("Too many raw-block operands in instruction"),
454+
}
455+
409456
for field in &format.imm_fields {
410457
fmtln!(fmt, "{},", field.member);
411458
}
@@ -442,6 +489,14 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
442489
}
443490
};
444491

492+
match format.num_raw_block_operands {
493+
0 => {}
494+
1 => {
495+
members.push("block");
496+
}
497+
_ => panic!("Too many raw-block operands"),
498+
}
499+
445500
for field in &format.imm_fields {
446501
members.push(field.member);
447502
}
@@ -474,6 +529,14 @@ fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Format
474529
_ => panic!("Too many block targets in instruction"),
475530
}
476531

532+
match format.num_raw_block_operands {
533+
0 => {}
534+
1 => {
535+
fmtln!(fmt, "block: mapper.map_block(block),");
536+
}
537+
_ => panic!("Too many raw block arguments in instruction"),
538+
}
539+
477540
for field in &format.imm_fields {
478541
let member = field.member;
479542
match &field.kind.fields {
@@ -958,6 +1021,13 @@ fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {
9581021
fmtln!(fmt, "blocks: [{}],", blocks.join(", "));
9591022
}
9601023
}
1024+
1025+
// Raw block operands.
1026+
match format.num_raw_block_operands {
1027+
0 => (),
1028+
1 => fmt.line("block: block0,"),
1029+
_ => panic!("Too many raw block arguments"),
1030+
}
9611031
}
9621032

9631033
/// Emit a method for creating and inserting an instruction format.
@@ -972,6 +1042,9 @@ fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {
9721042
"ctrl_typevar: Type".into(),
9731043
];
9741044

1045+
// Raw block operands.
1046+
args.extend((0..format.num_raw_block_operands).map(|i| format!("block{i}: ir::Block")));
1047+
9751048
// Normal operand arguments. Start with the immediate operands.
9761049
for f in &format.imm_fields {
9771050
args.push(format!("{}: {}", f.member, f.kind.rust_type));
@@ -1074,6 +1147,9 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
10741147
args_doc.push(format!("- {}_args: {}", op.name, "Block arguments"));
10751148

10761149
block_args.push(op);
1150+
} else if op.kind.is_raw_block() {
1151+
args.push("block: ir::Block".into());
1152+
args_doc.push("- block: raw basic block".into());
10771153
} else {
10781154
let t = if op.is_immediate() {
10791155
let t = format!("T{}", tmpl_types.len() + 1);

cranelift/codegen/meta/src/gen_isle.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ fn gen_common_isle(
142142
fmt.empty_line();
143143
}
144144

145+
// Raw block entities.
146+
fmtln!(fmt, "(type Block extern (enum))");
147+
fmt.empty_line();
148+
145149
// Generate the extern type declaration for `Opcode`.
146150
fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
147151
fmt.empty_line();
@@ -184,6 +188,12 @@ fn gen_common_isle(
184188
n => write!(&mut s, " (blocks BlockArray{n})").unwrap(),
185189
}
186190

191+
match format.num_raw_block_operands {
192+
0 => (),
193+
1 => write!(&mut s, "(block Block)").unwrap(),
194+
_ => panic!("Too many raw block arguments"),
195+
}
196+
187197
for field in &format.imm_fields {
188198
write!(
189199
&mut s,
@@ -322,7 +332,9 @@ fn gen_common_isle(
322332
let imm_operands: Vec<_> = inst
323333
.operands_in
324334
.iter()
325-
.filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block())
335+
.filter(|o| {
336+
!o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
337+
})
326338
.collect();
327339
assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),);
328340
for op in imm_operands {
@@ -353,6 +365,15 @@ fn gen_common_isle(
353365
}
354366
}
355367

368+
// Raw blocks.
369+
match inst.format.num_raw_block_operands {
370+
0 => {}
371+
1 => {
372+
write!(&mut s, " block").unwrap();
373+
}
374+
_ => panic!("Too many raw block arguments"),
375+
}
376+
356377
s.push_str("))");
357378
fmt.line(&s);
358379
});
@@ -441,12 +462,18 @@ fn gen_common_isle(
441462
}
442463
}
443464

465+
match inst.format.num_raw_block_operands {
466+
0 => {}
467+
1 => {
468+
write!(&mut s, " block").unwrap();
469+
}
470+
_ => panic!("Too many raw block arguments"),
471+
}
472+
444473
// Immediates (non-value args).
445-
for o in inst
446-
.operands_in
447-
.iter()
448-
.filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block())
449-
{
474+
for o in inst.operands_in.iter().filter(|o| {
475+
!o.is_value() && !o.is_varargs() && !o.kind.is_block() && !o.kind.is_raw_block()
476+
}) {
450477
write!(&mut s, " {}", o.name).unwrap();
451478
}
452479
s.push_str("))");

cranelift/codegen/meta/src/shared/entities.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ pub(crate) struct EntityRefs {
2323
/// This is primarily used in control flow instructions.
2424
pub(crate) block_else: OperandKind,
2525

26+
/// A reference to a basic block in the same function, without any arguments.
27+
/// This is primarily used to refer to block `try_call` terminators to get
28+
/// exception metadata (e.g., resume PCs) as first-class values.
29+
pub(crate) raw_block: OperandKind,
30+
2631
/// A reference to a stack slot declared in the function preamble.
2732
pub(crate) stack_slot: OperandKind,
2833

@@ -84,6 +89,12 @@ impl EntityRefs {
8489
"a basic block in the same function, with its arguments provided.",
8590
),
8691

92+
raw_block: new(
93+
"raw_block",
94+
"ir::Block",
95+
"a basic block in the same function, with no arguments provided.",
96+
),
97+
8798
stack_slot: new("stack_slot", "ir::StackSlot", "A stack slot"),
8899

89100
dynamic_stack_slot: new(

cranelift/codegen/meta/src/shared/formats.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub(crate) struct Formats {
4242
pub(crate) unary_ieee32: Rc<InstructionFormat>,
4343
pub(crate) unary_ieee64: Rc<InstructionFormat>,
4444
pub(crate) unary_imm: Rc<InstructionFormat>,
45+
pub(crate) exception_handler_address: Rc<InstructionFormat>,
4546
}
4647

4748
impl Formats {
@@ -219,6 +220,11 @@ impl Formats {
219220
.value()
220221
.imm(&imm.trapcode)
221222
.build(),
223+
224+
exception_handler_address: Builder::new("ExceptionHandlerAddress")
225+
.raw_block()
226+
.imm(&imm.imm64)
227+
.build(),
222228
}
223229
}
224230
}

0 commit comments

Comments
 (0)