Skip to content

Commit cfc0563

Browse files
authored
Make Wasmtime's FuncKey one-to-one with Cranelift's ir::UserExternalName (#11415)
* Make Wasmtime's `FuncKey` one-to-one with Cranelift's `ir::UserExternalName` `FuncKey`, which used to be called `CompileKey`, is now one-to-one with `cranelift_codegen::ir::UserExternalName`, and is used for not just identifying compilation objects but also relocations and call-graph edges. This allows us to determine the `StaticModuleIndex` and `DefinedFuncIndex` pair for any `cranelift_codegen::ir::FuncRef`, regardless of inlining depth, which fixes some fuzz bugs on OSS-Fuzz. This continues pushing on the idea that Wasmtime's compilation orchestration and linking should be relatively agnostic to the kinds of things it is actually compiling and linking, allowing us to tweak, add, and remove new kinds of `FuncKey`s more easily. Adding a new `FuncKey` should not require modifying relocation resolution, for example, just a little bit of code to run the associated compilation and optionally some code to extract metadata into our final artifacts for querying at runtime. Everything in between should Just Continue Working. We still aren't all the way there yet, but this does bring us a little bit closer. Finally, in Cranelift's inlining pass, this adds a check that a block is inserted in the layout before attempting to remove it from the layout, which would otherwise cause panics. This was triggered by multi-level inlining and now-unreachable blocks in the inner callees. I'll note that this does update basically all of the disas tests, or at least nearly all of them that make function calls. This is because the namespace/index numbering pair changed slightly to align with `FuncKey`, but that should pretty much be the only changes. * remove debug info from panic message, it is only available in some `cfg`s * fill out module doc comment * Fix compilation without `component-model` feature * Fix some more cfg compilations * cargo fmt * fix a wrong `&dyn Any` auto coercion; add helpful debug logging and assertions for this kind of thing
1 parent 2ee6290 commit cfc0563

79 files changed

Lines changed: 996 additions & 755 deletions

File tree

Some content is hidden

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

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cranelift/codegen/src/inline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ fn inline_one(
445445
// terminator, so do a quick pass over the inlined blocks and remove any
446446
// empty blocks from the caller's layout.
447447
for block in entity_map.iter_inlined_blocks(func) {
448-
if func.layout.first_inst(block).is_none() {
448+
if func.layout.is_block_inserted(block) && func.layout.first_inst(block).is_none() {
449449
func.layout.remove_block(block);
450450
}
451451
}

crates/cranelift/src/compiler.rs

Lines changed: 134 additions & 63 deletions
Large diffs are not rendered by default.

crates/cranelift/src/compiler/component.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ use cranelift_codegen::ir::condcodes::IntCC;
99
use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value};
1010
use cranelift_codegen::isa::{CallConv, TargetIsa};
1111
use cranelift_frontend::FunctionBuilder;
12-
use wasmtime_environ::fact::PREPARE_CALL_FIXED_PARAMS;
1312
use wasmtime_environ::{CompiledFunctionBody, component::*};
1413
use wasmtime_environ::{
1514
EntityRef, HostCall, ModuleInternedTypeIndex, PtrSize, TrapSentinel, Tunables, WasmFuncType,
1615
WasmValType,
1716
};
17+
use wasmtime_environ::{FuncKey, fact::PREPARE_CALL_FIXED_PARAMS};
1818

1919
struct TrampolineCompiler<'a> {
2020
compiler: &'a Compiler,
@@ -1281,7 +1281,7 @@ impl ComponentCompiler for Compiler {
12811281
&self,
12821282
component: &ComponentTranslation,
12831283
types: &ComponentTypesBuilder,
1284-
index: TrampolineIndex,
1284+
key: FuncKey,
12851285
tunables: &Tunables,
12861286
_symbol: &str,
12871287
) -> Result<AllCallFunc<CompiledFunctionBody>> {
@@ -1292,7 +1292,7 @@ impl ComponentCompiler for Compiler {
12921292
&mut compiler,
12931293
&component.component,
12941294
types,
1295-
index,
1295+
key.unwrap_component_trampoline(),
12961296
abi,
12971297
tunables,
12981298
);
@@ -1325,12 +1325,12 @@ impl ComponentCompiler for Compiler {
13251325
);
13261326
}
13271327

1328-
c.translate(&component.trampolines[index]);
1328+
c.translate(&component.trampolines[key.unwrap_component_trampoline()]);
13291329
c.builder.finalize();
13301330
compiler.cx.abi = Some(abi);
13311331

13321332
Ok(CompiledFunctionBody {
1333-
code: Box::new(Some(compiler.cx)),
1333+
code: super::box_dyn_any_compiler_context(Some(compiler.cx)),
13341334
needs_gc_heap: false,
13351335
})
13361336
};

crates/cranelift/src/debug/transform/address_transform.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,10 @@ impl AddressTransform {
498498
>,
499499
wasm_file: wasmtime_environ::WasmFileInfo,
500500
) -> Self {
501+
use cranelift_entity::EntityRef;
502+
501503
let mut translations = wasmtime_environ::PrimaryMap::new();
502-
let mut translation = wasmtime_environ::ModuleTranslation::default();
504+
let mut translation = wasmtime_environ::ModuleTranslation::new(StaticModuleIndex::new(0));
503505
translation.debuginfo.wasm_file = wasm_file;
504506
translation
505507
.module

crates/cranelift/src/debug/transform/mod.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,20 @@ fn read_dwarf_package_from_bytes<'data>(
121121
let mut validator = wasmparser::Validator::new();
122122
let parser = wasmparser::Parser::new(0);
123123
let mut types = wasmtime_environ::ModuleTypesBuilder::new(&validator);
124-
let translation =
125-
match wasmtime_environ::ModuleEnvironment::new(tunables, &mut validator, &mut types)
126-
.translate(parser, dwp_bytes)
127-
{
128-
Ok(translation) => translation,
129-
Err(e) => {
130-
log::warn!("failed to parse wasm dwarf package: {e:?}");
131-
return None;
132-
}
133-
};
124+
let translation = match wasmtime_environ::ModuleEnvironment::new(
125+
tunables,
126+
&mut validator,
127+
&mut types,
128+
StaticModuleIndex::from_u32(0),
129+
)
130+
.translate(parser, dwp_bytes)
131+
{
132+
Ok(translation) => translation,
133+
Err(e) => {
134+
log::warn!("failed to parse wasm dwarf package: {e:?}");
135+
return None;
136+
}
137+
};
134138

135139
match load_dwp(translation, buffer) {
136140
Ok(package) => Some(package),

crates/cranelift/src/func_environ.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ use std::mem;
2323
use wasmparser::{Operator, WasmFeatures};
2424
use wasmtime_environ::{
2525
BuiltinFunctionIndex, DataIndex, DefinedFuncIndex, ElemIndex, EngineOrModuleTypeIndex,
26-
FuncIndex, GlobalIndex, IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex,
27-
ModuleTranslation, ModuleTypesBuilder, PtrSize, Table, TableIndex, TripleExt, Tunables,
28-
TypeConvert, TypeIndex, VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType,
29-
WasmHeapType, WasmRefType, WasmResult, WasmValType,
26+
FuncIndex, FuncKey, GlobalIndex, IndexType, Memory, MemoryIndex, Module,
27+
ModuleInternedTypeIndex, ModuleTranslation, ModuleTypesBuilder, PtrSize, Table, TableIndex,
28+
TripleExt, Tunables, TypeConvert, TypeIndex, VMOffsets, WasmCompositeInnerType, WasmFuncType,
29+
WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult, WasmValType,
3030
};
3131
use wasmtime_environ::{FUNCREF_INIT_BIT, FUNCREF_MASK};
3232
use wasmtime_math::f64_cvt_to_int_bounds;
@@ -53,17 +53,17 @@ impl BuiltinFunctions {
5353
}
5454
}
5555

56-
fn load_builtin(&mut self, func: &mut Function, index: BuiltinFunctionIndex) -> ir::FuncRef {
57-
let cache = &mut self.builtins[index.index() as usize];
56+
fn load_builtin(&mut self, func: &mut Function, builtin: BuiltinFunctionIndex) -> ir::FuncRef {
57+
let cache = &mut self.builtins[builtin.index() as usize];
5858
if let Some(f) = cache {
5959
return *f;
6060
}
61-
let signature = func.import_signature(self.types.wasm_signature(index));
62-
let name =
63-
ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName {
64-
namespace: crate::NS_WASMTIME_BUILTIN,
65-
index: index.index(),
66-
}));
61+
let signature = func.import_signature(self.types.wasm_signature(builtin));
62+
let key = FuncKey::WasmToBuiltinTrampoline(builtin);
63+
let (namespace, index) = key.into_raw_parts();
64+
let name = ir::ExternalName::User(
65+
func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
66+
);
6767
let f = func.import_function(ir::ExtFuncData {
6868
name,
6969
signature,
@@ -1265,15 +1265,18 @@ impl FuncEnvironment<'_> {
12651265
def_func_index: DefinedFuncIndex,
12661266
) -> ir::FuncRef {
12671267
let func_index = self.module.func_index(def_func_index);
1268+
12681269
let ty = self.module.functions[func_index]
12691270
.signature
12701271
.unwrap_module_type_index();
12711272
let signature = self.get_or_create_interned_sig_ref(func, ty);
1272-
let name =
1273-
ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName {
1274-
namespace: crate::NS_WASM_FUNC,
1275-
index: func_index.as_u32(),
1276-
}));
1273+
1274+
let key = FuncKey::DefinedWasmFunction(self.translation.module_index, def_func_index);
1275+
let (namespace, index) = key.into_raw_parts();
1276+
let name = ir::ExternalName::User(
1277+
func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
1278+
);
1279+
12771280
func.import_function(ir::ExtFuncData {
12781281
name,
12791282
signature,
@@ -1288,15 +1291,20 @@ impl FuncEnvironment<'_> {
12881291
) -> ir::FuncRef {
12891292
assert!(self.module.is_imported_function(func_index));
12901293
assert!(self.translation.known_imported_functions[func_index].is_some());
1294+
12911295
let ty = self.module.functions[func_index]
12921296
.signature
12931297
.unwrap_module_type_index();
12941298
let signature = self.get_or_create_interned_sig_ref(func, ty);
1295-
let name =
1296-
ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName {
1297-
namespace: crate::NS_WASM_FUNC,
1298-
index: func_index.as_u32(),
1299-
}));
1299+
1300+
let (module, def_func_index) =
1301+
self.translation.known_imported_functions[func_index].unwrap();
1302+
let key = FuncKey::DefinedWasmFunction(module, def_func_index);
1303+
let (namespace, index) = key.into_raw_parts();
1304+
let name = ir::ExternalName::User(
1305+
func.declare_imported_user_function(ir::UserExternalName { namespace, index }),
1306+
);
1307+
13001308
func.import_function(ir::ExtFuncData {
13011309
name,
13021310
signature,

crates/cranelift/src/lib.rs

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use cranelift_entity::PrimaryMap;
2525

2626
use target_lexicon::Architecture;
2727
use wasmtime_environ::{
28-
BuiltinFunctionIndex, FlagValue, FuncIndex, RelocationTarget, Trap, TrapInformation, Tunables,
29-
WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType,
28+
BuiltinFunctionIndex, FlagValue, FuncKey, Trap, TrapInformation, Tunables, WasmFuncType,
29+
WasmHeapTopType, WasmHeapType, WasmValType,
3030
};
3131

3232
pub use builder::builder;
@@ -223,29 +223,13 @@ fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
223223

224224
// List of namespaces which are processed in `mach_reloc_to_reloc` below.
225225

226-
/// Namespace corresponding to wasm functions, the index is the index of the
227-
/// defined function that's being referenced.
228-
pub const NS_WASM_FUNC: u32 = 0;
229-
230-
/// Namespace for builtin function trampolines. The index is the index of the
231-
/// builtin that's being referenced. These trampolines invoke the real host
232-
/// function through an indirect function call loaded by the `VMContext`.
233-
pub const NS_WASMTIME_BUILTIN: u32 = 1;
234-
235-
/// Namespace used to when a call from Pulley to the host is being made. This is
236-
/// used with a `colocated: false` name to trigger codegen for a special opcode
237-
/// for pulley-to-host communication. The index of the functions used in this
238-
/// namespace correspond to the function signature of `for_each_host_signature!`
239-
/// in the pulley_interpreter crate.
240-
pub const NS_PULLEY_HOSTCALL: u32 = 2;
241-
242226
/// A record of a relocation to perform.
243227
#[derive(Debug, Clone, PartialEq, Eq)]
244228
pub struct Relocation {
245229
/// The relocation code.
246230
pub reloc: binemit::Reloc,
247231
/// Relocation target.
248-
pub reloc_target: RelocationTarget,
232+
pub reloc_target: FuncKey,
249233
/// The offset where to apply the relocation.
250234
pub offset: binemit::CodeOffset,
251235
/// The addend to add to the relocation value.
@@ -312,14 +296,7 @@ fn mach_reloc_to_reloc(
312296
let reloc_target = match *target {
313297
FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
314298
let name = &name_map[user_func_ref];
315-
match name.namespace {
316-
NS_WASM_FUNC => RelocationTarget::Wasm(FuncIndex::from_u32(name.index)),
317-
NS_WASMTIME_BUILTIN => {
318-
RelocationTarget::Builtin(BuiltinFunctionIndex::from_u32(name.index))
319-
}
320-
NS_PULLEY_HOSTCALL => RelocationTarget::PulleyHostcall(name.index),
321-
_ => panic!("unknown namespace {}", name.namespace),
322-
}
299+
FuncKey::from_raw_parts(name.namespace, name.index)
323300
}
324301
FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
325302
// We should have avoided any code that needs this style of libcalls

crates/cranelift/src/obj.rs

Lines changed: 43 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//! function body, the imported wasm function do not. The trampolines symbol
1414
//! names have format "_trampoline_N", where N is `SignatureIndex`.
1515
16-
use crate::{CompiledFunction, RelocationTarget};
16+
use crate::CompiledFunction;
1717
use anyhow::Result;
1818
use cranelift_codegen::TextSectionBuilder;
1919
use cranelift_codegen::isa::unwind::{UnwindInfo, systemv};
@@ -23,8 +23,8 @@ use gimli::write::{Address, EhFrame, EndianVec, FrameTable, Writer};
2323
use object::write::{Object, SectionId, StandardSegment, Symbol, SymbolId, SymbolSection};
2424
use object::{Architecture, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope};
2525
use std::ops::Range;
26-
use wasmtime_environ::obj;
2726
use wasmtime_environ::{Compiler, TripleExt};
27+
use wasmtime_environ::{FuncKey, obj};
2828

2929
const TEXT_SECTION_NAME: &[u8] = b".text";
3030

@@ -120,7 +120,7 @@ impl<'a> ModuleTextBuilder<'a> {
120120
&mut self,
121121
name: &str,
122122
compiled_func: &'a CompiledFunction,
123-
resolve_reloc_target: impl Fn(wasmtime_environ::RelocationTarget) -> usize,
123+
resolve_reloc_target: impl Fn(wasmtime_environ::FuncKey) -> usize,
124124
) -> (SymbolId, Range<u64>) {
125125
let body = compiled_func.buffer.data();
126126
let alignment = compiled_func.alignment;
@@ -146,65 +146,49 @@ impl<'a> ModuleTextBuilder<'a> {
146146

147147
for r in compiled_func.relocations() {
148148
let reloc_offset = off + u64::from(r.offset);
149-
match r.reloc_target {
150-
// Relocations against user-defined functions means that this is
151-
// a relocation against a module-local function, typically a
152-
// call between functions. The `text` field is given priority to
153-
// resolve this relocation before we actually emit an object
154-
// file, but if it can't handle it then we pass through the
155-
// relocation.
156-
RelocationTarget::Wasm(_) | RelocationTarget::Builtin(_) => {
157-
let target = resolve_reloc_target(r.reloc_target);
158-
if self
159-
.text
160-
.resolve_reloc(reloc_offset, r.reloc, r.addend, target)
161-
{
162-
continue;
163-
}
164-
165-
// At this time it's expected that all relocations are
166-
// handled by `text.resolve_reloc`, and anything that isn't
167-
// handled is a bug in `text.resolve_reloc` or something
168-
// transitively there. If truly necessary, though, then this
169-
// loop could also be updated to forward the relocation to
170-
// the final object file as well.
171-
panic!(
172-
"unresolved relocation could not be processed against \
173-
{:?}: {r:?}",
174-
r.reloc_target,
175-
);
176-
}
177149

178-
// This relocation is used to fill in which hostcall id is
179-
// desired within the `call_indirect_host` opcode of Pulley
180-
// itself. The relocation target is the start of the instruction
181-
// and the goal is to insert the static signature number, `n`,
182-
// into the instruction.
183-
//
184-
// At this time the instruction looks like:
185-
//
186-
// +------+------+------+------+
187-
// | OP | OP_EXTENDED | N |
188-
// +------+------+------+------+
189-
//
190-
// This 4-byte encoding has `OP` indicating this is an "extended
191-
// opcode" where `OP_EXTENDED` is a 16-bit extended opcode.
192-
// The `N` byte is the index of the signature being called and
193-
// is what's b eing filled in.
194-
//
195-
// See the `test_call_indirect_host_width` in
196-
// `pulley/tests/all.rs` for this guarantee as well.
197-
RelocationTarget::PulleyHostcall(n) => {
198-
#[cfg(feature = "pulley")]
199-
{
200-
use pulley_interpreter::encode::Encode;
201-
assert_eq!(pulley_interpreter::CallIndirectHost::WIDTH, 4);
202-
}
203-
let byte = u8::try_from(n).unwrap();
204-
self.text.write(reloc_offset + 3, &[byte]);
150+
// This relocation is used to fill in which hostcall id is
151+
// desired within the `call_indirect_host` opcode of Pulley
152+
// itself. The relocation target is the start of the instruction
153+
// and the goal is to insert the static signature number, `n`,
154+
// into the instruction.
155+
//
156+
// At this time the instruction looks like:
157+
//
158+
// +------+------+------+------+
159+
// | OP | OP_EXTENDED | N |
160+
// +------+------+------+------+
161+
//
162+
// This 4-byte encoding has `OP` indicating this is an "extended
163+
// opcode" where `OP_EXTENDED` is a 16-bit extended opcode.
164+
// The `N` byte is the index of the signature being called and
165+
// is what's b eing filled in.
166+
//
167+
// See the `test_call_indirect_host_width` in
168+
// `pulley/tests/all.rs` for this guarantee as well.
169+
if let FuncKey::PulleyHostCall(host_call) = r.reloc_target {
170+
#[cfg(feature = "pulley")]
171+
{
172+
use pulley_interpreter::encode::Encode;
173+
assert_eq!(pulley_interpreter::CallIndirectHost::WIDTH, 4);
205174
}
206-
};
175+
let n = host_call.index();
176+
let byte = u8::try_from(n).unwrap();
177+
self.text.write(reloc_offset + 3, &[byte]);
178+
continue;
179+
}
180+
181+
let target = resolve_reloc_target(r.reloc_target);
182+
if self
183+
.text
184+
.resolve_reloc(reloc_offset, r.reloc, r.addend, target)
185+
{
186+
continue;
187+
}
188+
189+
panic!("failed to resolve relocation: {r:?} -> {target}");
207190
}
191+
208192
(symbol_id, off..off + body_len)
209193
}
210194

0 commit comments

Comments
 (0)