Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions crates/cranelift/src/alias_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,15 @@ impl<'a, Offsets> Field<'a, Offsets> {
self
}

/// Get-or-create this field's alias region.
pub fn region(&mut self, func: &mut ir::Function) -> ir::AliasRegion {
self.regions.region(func, self.key)
}

/// Get-or-create this field's alias region and mix it into this field's
/// base flags.
fn flags_with_region(&mut self, func: &mut ir::Function) -> ir::MemFlagsData {
let region = self.regions.region(func, self.key);
let region = self.region(func);
self.flags.with_alias_region(Some(region))
}

Expand Down Expand Up @@ -710,7 +715,10 @@ wasmtime_environ::for_each_vm_type!(define_vm_type_alias_region_helpers);
/// pointer size, so their accessors are available for any `Offsets: GetPtrSize`.
/// Its `dynamic` fields sit at offsets that additionally depend on the module or
/// component being compiled, so their accessors are only available when the
/// `AliasRegions` carries that vmctx's own fully-computed offsets.
/// `AliasRegions` carries that vmctx's own fully-computed offsets. The exception
/// is a `dynamic` field marked `#[ptr_size_offset]`, whose offsets are derived
/// only from the pointer size; these live in the `Offsets: GetPtrSize` block as
/// well.
///
/// A field marked `#[aggregate]` gets no accessor, for the same reason it gets
/// none in [`define_vm_type_alias_region_helpers!`]: it has no single Cranelift
Expand Down Expand Up @@ -748,6 +756,7 @@ macro_rules! define_vmctx_alias_region_helpers {
(@apply_attr $flags:expr, [readonly]) => { $flags.with_readonly() };
(@apply_attr $flags:expr, [can_move]) => { $flags.with_can_move() };
(@apply_attr $flags:expr, [access_as = $($t:tt)*]) => { $flags };
(@apply_attr $flags:expr, [ptr_size_offset]) => { $flags };

// Compute a field's access flags and Cranelift type from its declared type
// and marker attributes, and build the `Field` for it at `$offset`.
Expand Down Expand Up @@ -784,8 +793,53 @@ macro_rules! define_vmctx_alias_region_helpers {
}
};

// ### `dynamic` Section Entries Marked `#[ptr_size_offset]`
//
// These get their offsets from the pointer-size-only `offsets::VMFoo<P>`
// wrapper, and don't require a full `VMOffsets` parameterization.

(@ptr_size_entry $Name:ident $snake:ident field {
#[ptr_size_offset] $(# $fattr:tt)* $fname:ident : $($fty:tt)*
}) => {
#[doc = concat!(
"Get the [`Field`] for the `", stringify!($fname), "` field of `",
stringify!($Name), "`."
)]
pub fn $fname(self) -> Field<'a, Offsets> {
let offset = self.regions.offsets.get_ptr_size().$snake().$fname();
define_vmctx_alias_region_helpers!(
@field $Name (self, offset) [ $($fty)* ] [ $(# $fattr)* ]
)
}
};

(@ptr_size_entry $Name:ident $snake:ident array {
#[ptr_size_offset] $(# $fattr:tt)* $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
}) => {
#[doc = concat!(
"Get the [`Field`] for the `index`th element of `", stringify!($Name),
"`'s `", stringify!($fname), "` array.\n\nThis is not bounds checked: \
the array's length depends on the module or component being compiled, \
which is precisely what this accessor does not require knowing."
)]
pub fn $fname(self, index: $Index) -> Field<'a, Offsets> {
let offset = self.regions.offsets.get_ptr_size().$snake().$fname(index);
define_vmctx_alias_region_helpers!(
@field $Name (self, offset) [ $($fty)* ] [ $(# $fattr)* ]
)
}
};

(@ptr_size_entry $Name:ident $snake:ident $kind:ident $entry:tt) => {};

// ### `dynamic` Section Entries

// Entries marked `#[ptr_size_offset]` were already handled above; emitting
// them here too would be a duplicate definition.
(@dynamic_entry $Name:ident $Offsets:tt $kind:ident {
#[ptr_size_offset] $($rest:tt)*
}) => {};

(@dynamic_entry $Name:ident $Offsets:tt align { $al:tt }) => {};

// Aggregates get no accessor.
Expand Down Expand Up @@ -848,7 +902,7 @@ macro_rules! define_vmctx_alias_region_helpers {
// Emit the accessor `struct` and both `impl` blocks for one vmctx type.
(@emit $Name:ident $snake:ident $Offsets:tt
static { $($skind:ident $sentry:tt)* }
dynamic { $($dyn:tt)* }
dynamic { $($dkind:ident $dentry:tt)* }
) => {
#[doc = concat!(
"An [`AliasRegions`] accessor for the fields of a `", stringify!($Name),
Expand Down Expand Up @@ -879,13 +933,14 @@ macro_rules! define_vmctx_alias_region_helpers {
Offsets: GetPtrSize,
{
$( define_vmctx_alias_region_helpers!(@static_entry $Name $snake $skind $sentry); )*
$( define_vmctx_alias_region_helpers!(@ptr_size_entry $Name $snake $dkind $dentry); )*
}

// A dynamically-positioned field's offset depends on the module or
// component being compiled, so these accessors require this vmctx's own
// fully-computed offsets.
define_vmctx_alias_region_helpers!(
@dynamic_impl $Name $Offsets $Offsets { $($dyn)* }
@dynamic_impl $Name $Offsets $Offsets { $($dkind $dentry)* }
);
};

Expand Down
16 changes: 13 additions & 3 deletions crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ use wasmtime_environ::{
BuiltinFunctionIndex, ComponentPC, ConstExpr, ConstOp, DataIndex, DefinedFuncIndex,
DefinedGlobalIndex, DefinedTableIndex, ElemIndex, EngineOrModuleTypeIndex, FactInlineIntrinsic,
FrameStateSlotBuilder, FrameValType, FuncIndex, FuncKey, GlobalConstValue, GlobalIndex,
IndexType, KnownFunc, Memory, MemoryIndex, MemoryInit, MemorySegmentOffset, MemoryTunables,
Module, ModuleInternedTypeIndex, ModuleTranslation, ModuleTypesBuilder,
IndexType, KnownFunc, KnownGlobal, Memory, MemoryIndex, MemoryInit, MemorySegmentOffset,
MemoryTunables, Module, ModuleInternedTypeIndex, ModuleTranslation, ModuleTypesBuilder,
NUM_COMPONENT_CONTEXT_SLOTS, PassiveElemIndex, PtrSize, RuntimeDataIndex, Table, TableIndex,
TableInitialValue, TableSegment, TableSegmentElements, TagIndex, Tunables, TypeConvert,
TypeIndex, VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, WasmHeapType,
Expand Down Expand Up @@ -446,10 +446,20 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
}
}
None => match self.translation.known_imported_globals[global] {
Some(known) => {
Some(KnownGlobal::Defined(known)) => {
self.alias_regions
.defined_global_region(func, known.module, known.index)
}
Some(KnownGlobal::ComponentInstanceFlags(instance)) => self
.alias_regions
.vmcomponent()
.may_leave(instance)
.region(func),
Some(KnownGlobal::TaskMayBlock) => self
.alias_regions
.vmcomponent()
.task_may_block()
.region(func),
None => self.alias_regions.public_global_region(func),
},
}
Expand Down
21 changes: 19 additions & 2 deletions crates/environ/src/compile/module_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,31 @@ impl From<FactInlineIntrinsic> for KnownFunc {
}

/// A statically-known import of a core Wasm global, memory, or table.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct KnownEntity<T> {
/// The module that defines this entity.
pub module: StaticModuleIndex,
/// The entity's index in the defining module's defined-entity index space.
pub index: T,
}

/// A statically-known import of a core wasm global.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum KnownGlobal {
/// A global defined by a module in the same component.
Defined(KnownEntity<DefinedGlobalIndex>),

/// A component instance's runtime-managed flags, such as its `may-leave`
/// flag.
#[cfg(feature = "component-model")]
ComponentInstanceFlags(crate::component::RuntimeComponentInstanceIndex),

/// The runtime-managed flag recording whether the currently-executing task
/// may perform blocking operations.
#[cfg(feature = "component-model")]
TaskMayBlock,
}

/// The result of translating via `ModuleEnvironment`.
///
/// Function bodies are not yet translated, and data initializers have not yet
Expand Down Expand Up @@ -154,7 +171,7 @@ pub struct ModuleTranslation<'data> {
/// TODO(#14164): Actually record (1) and (2) in separate maps, enabling
/// optimizations that rely on just (1) but not (2), instead of folding them
/// into this same map.
pub known_imported_globals: SecondaryMap<GlobalIndex, Option<KnownEntity<DefinedGlobalIndex>>>,
pub known_imported_globals: SecondaryMap<GlobalIndex, Option<KnownGlobal>>,

/// Same as `known_imported_globals`, but for memories.
pub known_imported_memories: SecondaryMap<MemoryIndex, Option<KnownEntity<DefinedMemoryIndex>>>,
Expand Down
105 changes: 79 additions & 26 deletions crates/environ/src/component/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::component::*;
use crate::prelude::*;
use crate::{
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, EngineOrModuleTypeIndex,
EntityIndex, FactInlineIntrinsic, FuncKey, KnownEntity, ModuleEnvironment,
EntityIndex, FactInlineIntrinsic, FuncKey, KnownEntity, KnownGlobal, ModuleEnvironment,
ModuleInternedTypeIndex, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, ScopeVec, TagIndex,
Tunables, TypeConvert, WasmHeapType, WasmResult, WasmValType,
};
Expand Down Expand Up @@ -606,21 +606,30 @@ impl<'a, 'data> Translator<'a, 'data> {
for i in 0..translation.module.num_defined_globals() {
let index = DefinedGlobalIndex::new(i);
let global = translation.module.global_index(index);
if !ambiguous.contains(&(module, EntityIndex::Global(global))) {
if !ambiguous
.entities
.contains(&(module, EntityIndex::Global(global)))
{
translation.globals_known_to_importers.insert(index);
}
}
for i in 0..translation.module.num_defined_memories() {
let index = DefinedMemoryIndex::new(i);
let memory = translation.module.memory_index(index);
if !ambiguous.contains(&(module, EntityIndex::Memory(memory))) {
if !ambiguous
.entities
.contains(&(module, EntityIndex::Memory(memory)))
{
translation.memories_known_to_importers.insert(index);
}
}
for i in 0..translation.module.num_defined_tables() {
let index = DefinedTableIndex::new(i);
let table = translation.module.table_index(index);
if !ambiguous.contains(&(module, EntityIndex::Table(table))) {
if !ambiguous
.entities
.contains(&(module, EntityIndex::Table(table)))
{
translation.tables_known_to_importers.insert(index);
}
}
Expand All @@ -643,9 +652,14 @@ impl<'a, 'data> Translator<'a, 'data> {
// same defined entity, when we know that and when everything
// else that imports that entity knows it too.
macro_rules! record_known_entity {
($variant:ident, $imported:expr, $defined_index:ident, $known:ident) => {{
($variant:ident, $imported:expr, $defined_index:ident, $known:ident, $wrap:expr) => {{
let Some((arg_module, EntityIndex::$variant(arg_entity))) =
unambiguous_entity(&self.static_modules, &instances, &ambiguous, arg)
unambiguous_entity(
&self.static_modules,
&instances,
&ambiguous.entities,
arg,
)
else {
continue;
};
Expand All @@ -657,10 +671,10 @@ impl<'a, 'data> Translator<'a, 'data> {
defines",
);
assert!(self.static_modules[module].$known[$imported].is_none());
self.static_modules[module].$known[$imported] = Some(KnownEntity {
self.static_modules[module].$known[$imported] = Some($wrap(KnownEntity {
module: arg_module,
index,
});
}));
}};
}

Expand Down Expand Up @@ -761,27 +775,43 @@ impl<'a, 'data> Translator<'a, 'data> {

// Note that a global import is not necessarily satisfied by a
// wasm global: it can also be one of the component-model
// flags that live in the `VMComponentContext`, which have
// nothing to do with defined-global alias regions.
EntityIndex::Global(imported_global) => record_known_entity!(
Global,
imported_global,
defined_global_index,
known_imported_globals
),
// flags that live in the `VMComponentContext`, and those get
// their own alias regions rather than a defined-global one.
EntityIndex::Global(imported_global) => match component_flags(arg) {
Some(flags) => {
if ambiguous.flags.contains(&flags) {
continue;
}
assert!(
self.static_modules[module].known_imported_globals[imported_global]
.is_none()
);
self.static_modules[module].known_imported_globals[imported_global] =
Some(flags);
}
None => record_known_entity!(
Global,
imported_global,
defined_global_index,
known_imported_globals,
KnownGlobal::Defined
),
},

EntityIndex::Memory(imported_memory) => record_known_entity!(
Memory,
imported_memory,
defined_memory_index,
known_imported_memories
known_imported_memories,
core::convert::identity
),

EntityIndex::Table(imported_table) => record_known_entity!(
Table,
imported_table,
defined_table_index,
known_imported_tables
known_imported_tables,
core::convert::identity
),

// Tags don't have alias regions of their own.
Expand Down Expand Up @@ -1927,6 +1957,28 @@ use pre_inlining::PreInliningComponentTypes;
type StaticInstances<'a> =
PrimaryMap<RuntimeInstanceIndex, Option<(StaticModuleIndex, &'a [CoreDef])>>;

/// Every entity whose identity is not statically known to everything that can
/// access it.
#[derive(Default)]
struct Ambiguous {
/// Globals, memories, and tables defined by a static module in this
/// component.
entities: HashSet<(StaticModuleIndex, EntityIndex)>,

/// Component-model flags living in the `VMComponentContext`. Only ever
/// contains the non-`KnownGlobal::Defined` variants.
flags: HashSet<KnownGlobal>,
}

/// Get the component-model flag that a `CoreDef` names, if it names one.
fn component_flags(def: &CoreDef) -> Option<KnownGlobal> {
match def {
CoreDef::InstanceFlags(instance) => Some(KnownGlobal::ComponentInstanceFlags(*instance)),
CoreDef::TaskMayBlock => Some(KnownGlobal::TaskMayBlock),
CoreDef::Export(_) | CoreDef::Trampoline(_) | CoreDef::UnsafeIntrinsic(_) => None,
}
}

/// Resolve a `CoreExport` to the static module that *defines* it and the entity
/// index it refers to within that module, when we can see through it statically.
///
Expand Down Expand Up @@ -2047,22 +2099,23 @@ fn ambiguous_entities(
translation: &ComponentTranslation,
instantiations: &SecondaryMap<StaticModuleIndex, dfg::AbstractInstantiations<'_>>,
instances: &StaticInstances<'_>,
) -> HashSet<(StaticModuleIndex, EntityIndex)> {
let mut ambiguous = HashSet::default();
) -> Ambiguous {
let mut ambiguous = Ambiguous::default();

let mut mark = |def: &CoreDef| match def {
CoreDef::Export(export) => {
if let Some(entity) = resolve_core_export(static_modules, instances, export) {
ambiguous.insert(entity);
ambiguous.entities.insert(entity);
}
}

// None of these are entities that get an alias region keyed by a
CoreDef::InstanceFlags(_) | CoreDef::TaskMayBlock => {
ambiguous.flags.insert(component_flags(def).unwrap());
}

// Neither of these is an entity that gets an alias region keyed by a
// defining module and index.
CoreDef::InstanceFlags(_)
| CoreDef::Trampoline(_)
| CoreDef::UnsafeIntrinsic(_)
| CoreDef::TaskMayBlock => {}
CoreDef::Trampoline(_) | CoreDef::UnsafeIntrinsic(_) => {}
};

for init in &translation.component.initializers {
Expand Down
Loading
Loading