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
47 changes: 47 additions & 0 deletions crates/cranelift/src/compiler/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ impl<'a> TrampolineCompiler<'a> {
Trampoline::ResourceNew(ty) => self.translate_resource_new(*ty),
Trampoline::ResourceRep(ty) => self.translate_resource_rep(*ty),
Trampoline::ResourceDrop(ty) => self.translate_resource_drop(*ty),
Trampoline::ResourceTransferOwn => {
self.translate_resource_libcall(host::resource_transfer_own)
}
Trampoline::ResourceTransferBorrow => {
self.translate_resource_libcall(host::resource_transfer_borrow)
}
Trampoline::ResourceEnterCall => {
self.translate_resource_libcall(host::resource_enter_call)
}
Trampoline::ResourceExitCall => {
self.translate_resource_libcall(host::resource_exit_call)
}
}
}

Expand Down Expand Up @@ -496,6 +508,41 @@ impl<'a> TrampolineCompiler<'a> {
self.builder.seal_block(return_block);
}

/// Invokes a host libcall and returns the result.
///
/// Only intended for simple trampolines and effectively acts as a bridge
/// from the wasm abi to host.
fn translate_resource_libcall(
&mut self,
get_libcall: fn(&dyn TargetIsa, &mut ir::Function) -> (ir::SigRef, u32),
) {
match self.abi {
Abi::Wasm => {}

// These trampolines can only actually be called by Wasm, so
// let's assert that here.
Abi::Native | Abi::Array => {
self.builder
.ins()
.trap(ir::TrapCode::User(crate::DEBUG_ASSERT_TRAP_CODE));
return;
}
}

let args = self.builder.func.dfg.block_params(self.block0).to_vec();
let vmctx = args[0];
let mut host_args = vec![vmctx];
host_args.extend(args[2..].iter().copied());
let (host_sig, offset) = get_libcall(self.isa, &mut self.builder.func);
let host_fn = self.load_libcall(vmctx, offset);
let call = self
.builder
.ins()
.call_indirect(host_sig, host_fn, &host_args);
let results = self.builder.func.dfg.inst_results(call).to_vec();
self.builder.ins().return_(&results);
}

/// Loads a host function pointer for a libcall stored at the `offset`
/// provided in the libcalls array.
///
Expand Down
5 changes: 5 additions & 0 deletions crates/environ/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ macro_rules! foreach_builtin_component_function {
// is encoded as a 64-bit integer where the low bit is Some/None
// and bits 1-33 are the payload.
resource_drop(vmctx: vmctx, resource: u32, idx: u32) -> u64;

resource_transfer_own(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u32;
resource_transfer_borrow(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u32;
resource_enter_call(vmctx: vmctx);
resource_exit_call(vmctx: vmctx);
}
};
}
11 changes: 10 additions & 1 deletion crates/environ/src/component/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub struct ComponentDfg {

/// All trampolines and their type signature which will need to get
/// compiled by Cranelift.
pub trampolines: PrimaryMap<TrampolineIndex, (SignatureIndex, Trampoline)>,
pub trampolines: Intern<TrampolineIndex, (SignatureIndex, Trampoline)>,

/// Know reallocation functions which are used by `lowerings` (e.g. will be
/// used by the host)
Expand Down Expand Up @@ -238,6 +238,7 @@ impl<T> CoreExport<T> {
}

/// Same as `info::Trampoline`
#[derive(Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum Trampoline {
LowerImport {
Expand All @@ -256,6 +257,10 @@ pub enum Trampoline {
ResourceNew(TypeResourceTableIndex),
ResourceRep(TypeResourceTableIndex),
ResourceDrop(TypeResourceTableIndex),
ResourceTransferOwn,
ResourceTransferBorrow,
ResourceEnterCall,
ResourceExitCall,
}

/// Same as `info::CanonicalOptions`
Expand Down Expand Up @@ -581,6 +586,10 @@ impl LinearizeDfg<'_> {
Trampoline::ResourceNew(ty) => info::Trampoline::ResourceNew(*ty),
Trampoline::ResourceDrop(ty) => info::Trampoline::ResourceDrop(*ty),
Trampoline::ResourceRep(ty) => info::Trampoline::ResourceRep(*ty),
Trampoline::ResourceTransferOwn => info::Trampoline::ResourceTransferOwn,
Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow,
Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall,
Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall,
};
let i1 = self.trampolines.push(*signature);
let i2 = self.trampoline_defs.push(trampoline);
Expand Down
22 changes: 22 additions & 0 deletions crates/environ/src/component/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,24 @@ pub enum Trampoline {

/// Same as `ResourceNew`, but for the `resource.drop` intrinsic.
ResourceDrop(TypeResourceTableIndex),

/// An intrinsic used by FACT-generated modules which will transfer an owned
/// resource from one table to another. Used in component-to-component
/// adapter trampolines.
ResourceTransferOwn,

/// Same as `ResourceTransferOwn` but for borrows.
ResourceTransferBorrow,

/// An intrinsic used by FACT-generated modules which indicates that a call
/// is being entered and resource-related metadata needs to be configured.
///
/// Note that this is currently only invoked when borrowed resources are
/// detected, otherwise this is "optimized out".
ResourceEnterCall,

/// Same as `ResourceEnterCall` except for when exiting a call.
ResourceExitCall,
}

impl Trampoline {
Expand All @@ -556,6 +574,10 @@ impl Trampoline {
ResourceNew(i) => format!("component-resource-new[{}]", i.as_u32()),
ResourceRep(i) => format!("component-resource-rep[{}]", i.as_u32()),
ResourceDrop(i) => format!("component-resource-drop[{}]", i.as_u32()),
ResourceTransferOwn => format!("component-resource-transfer-own"),
ResourceTransferBorrow => format!("component-resource-transfer-borrow"),
ResourceEnterCall => format!("component-resource-enter-call"),
ResourceExitCall => format!("component-resource-exit-call"),
}
}
}
16 changes: 12 additions & 4 deletions crates/environ/src/component/translate/adapt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ fn fact_import_to_core_def(
import: &fact::Import,
ty: EntityType,
) -> dfg::CoreDef {
let mut simple_intrinsic = |trampoline: dfg::Trampoline| {
let signature = ty.unwrap_func();
let index = dfg.trampolines.push((signature, trampoline));
dfg::CoreDef::Trampoline(index)
};
match import {
fact::Import::CoreDef(def) => def.clone(),
fact::Import::Transcode {
Expand All @@ -278,10 +283,7 @@ fn fact_import_to_core_def(

let from = dfg.memories.push(unwrap_memory(from));
let to = dfg.memories.push(unwrap_memory(to));
let signature = match ty {
EntityType::Function(signature) => signature,
_ => unreachable!(),
};
let signature = ty.unwrap_func();
let index = dfg.trampolines.push((
signature,
dfg::Trampoline::Transcoder {
Expand All @@ -294,6 +296,12 @@ fn fact_import_to_core_def(
));
dfg::CoreDef::Trampoline(index)
}
fact::Import::ResourceTransferOwn => simple_intrinsic(dfg::Trampoline::ResourceTransferOwn),
fact::Import::ResourceTransferBorrow => {
simple_intrinsic(dfg::Trampoline::ResourceTransferBorrow)
}
fact::Import::ResourceEnterCall => simple_intrinsic(dfg::Trampoline::ResourceEnterCall),
fact::Import::ResourceExitCall => simple_intrinsic(dfg::Trampoline::ResourceExitCall),
}
}

Expand Down
22 changes: 20 additions & 2 deletions crates/environ/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,12 @@ impl ComponentTypesBuilder {
self.type_information(ty).flat.as_flat_types()
}

/// Returns whether the type specified contains any borrowed resources
/// within it.
pub fn ty_contains_borrow_resource(&self, ty: &InterfaceType) -> bool {
self.type_information(ty).has_borrow
}

fn type_information(&self, ty: &InterfaceType) -> &TypeInformation {
match ty {
InterfaceType::U8
Expand All @@ -877,11 +883,18 @@ impl ComponentTypesBuilder {
| InterfaceType::U32
| InterfaceType::S32
| InterfaceType::Char
| InterfaceType::Own(_)
| InterfaceType::Borrow(_) => {
| InterfaceType::Own(_) => {
static INFO: TypeInformation = TypeInformation::primitive(FlatType::I32);
&INFO
}
InterfaceType::Borrow(_) => {
static INFO: TypeInformation = {
let mut info = TypeInformation::primitive(FlatType::I32);
info.has_borrow = true;
info
};
&INFO
}
InterfaceType::U64 | InterfaceType::S64 => {
static INFO: TypeInformation = TypeInformation::primitive(FlatType::I64);
&INFO
Expand Down Expand Up @@ -1711,13 +1724,15 @@ struct TypeInformationCache {
struct TypeInformation {
depth: u32,
flat: FlatTypesStorage,
has_borrow: bool,
}

impl TypeInformation {
const fn new() -> TypeInformation {
TypeInformation {
depth: 0,
flat: FlatTypesStorage::new(),
has_borrow: false,
}
}

Expand Down Expand Up @@ -1747,6 +1762,7 @@ impl TypeInformation {
self.depth = 1;
for info in types {
self.depth = self.depth.max(1 + info.depth);
self.has_borrow = self.has_borrow || info.has_borrow;
match info.flat.as_flat_types() {
Some(types) => {
for (t32, t64) in types.memory32.iter().zip(types.memory64) {
Expand Down Expand Up @@ -1789,6 +1805,7 @@ impl TypeInformation {
None => continue,
};
self.depth = self.depth.max(1 + info.depth);
self.has_borrow = self.has_borrow || info.has_borrow;

// If this variant is already unrepresentable in a flat
// representation then this can be skipped.
Expand Down Expand Up @@ -1898,5 +1915,6 @@ impl TypeInformation {
*self = TypeInformation::string();
let info = types.type_information(&ty.element);
self.depth += info.depth;
self.has_borrow = info.has_borrow;
}
}
85 changes: 85 additions & 0 deletions crates/environ/src/fact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ pub struct Module<'a> {
/// Intern'd transcoders and what index they were assigned.
imported_transcoders: HashMap<Transcoder, FuncIndex>,

/// Cached versions of imported trampolines for working with resources.
imported_resource_transfer_own: Option<FuncIndex>,
imported_resource_transfer_borrow: Option<FuncIndex>,
imported_resource_enter_call: Option<FuncIndex>,
imported_resource_exit_call: Option<FuncIndex>,

// Current status of index spaces from the imports generated so far.
imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,
imported_memories: PrimaryMap<MemoryIndex, CoreDef>,
Expand Down Expand Up @@ -178,6 +184,10 @@ impl<'a> Module<'a> {
funcs: PrimaryMap::new(),
helper_funcs: HashMap::new(),
helper_worklist: Vec::new(),
imported_resource_transfer_own: None,
imported_resource_transfer_borrow: None,
imported_resource_enter_call: None,
imported_resource_exit_call: None,
}
}

Expand Down Expand Up @@ -359,6 +369,72 @@ impl<'a> Module<'a> {
})
}

fn import_simple(
&mut self,
module: &str,
name: &str,
params: &[ValType],
results: &[ValType],
import: Import,
get: impl Fn(&mut Self) -> &mut Option<FuncIndex>,
) -> FuncIndex {
if let Some(idx) = get(self) {
return *idx;
}
let ty = self.core_types.function(params, results);
let ty = EntityType::Function(ty);
self.core_imports.import(module, name, ty);

self.imports.push(import);
let idx = self.imported_funcs.push(None);
*get(self) = Some(idx);
idx
}

fn import_resource_transfer_own(&mut self) -> FuncIndex {
self.import_simple(
"resource",
"transfer-own",
&[ValType::I32, ValType::I32, ValType::I32],
&[ValType::I32],
Import::ResourceTransferOwn,
|me| &mut me.imported_resource_transfer_own,
)
}

fn import_resource_transfer_borrow(&mut self) -> FuncIndex {
self.import_simple(
"resource",
"transfer-borrow",
&[ValType::I32, ValType::I32, ValType::I32],
&[ValType::I32],
Import::ResourceTransferBorrow,
|me| &mut me.imported_resource_transfer_borrow,
)
}

fn import_resource_enter_call(&mut self) -> FuncIndex {
self.import_simple(
"resource",
"enter-call",
&[],
&[],
Import::ResourceEnterCall,
|me| &mut me.imported_resource_enter_call,
)
}

fn import_resource_exit_call(&mut self) -> FuncIndex {
self.import_simple(
"resource",
"exit-call",
&[],
&[],
Import::ResourceExitCall,
|me| &mut me.imported_resource_exit_call,
)
}

fn translate_helper(&mut self, helper: Helper) -> FunctionId {
*self.helper_funcs.entry(helper).or_insert_with(|| {
// Generate a fresh `Function` with a unique id for what we're about to
Expand Down Expand Up @@ -470,6 +546,15 @@ pub enum Import {
/// Whether or not `to` is a 64-bit memory
to64: bool,
},
/// Transfers an owned resource from one table to another.
ResourceTransferOwn,
/// Transfers a borrowed resource from one table to another.
ResourceTransferBorrow,
/// Sets up entry metadata for a borrow resources when a call starts.
ResourceEnterCall,
/// Tears down a previous entry and handles checking borrow-related
/// metadata.
ResourceExitCall,
}

impl Options {
Expand Down
17 changes: 17 additions & 0 deletions crates/environ/src/fact/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,21 @@ impl ComponentTypesBuilder {
(abi.size32, abi.align32)
}
}

/// Tests whether the type signature for `options` contains a borrowed
/// resource anywhere.
pub(super) fn contains_borrow_resource(&self, options: &AdapterOptions) -> bool {
let ty = &self[options.ty];

// Only parameters need to be checked since results should never have
// borrowed resources.
debug_assert!(!self[ty.results]
.types
.iter()
.any(|t| self.ty_contains_borrow_resource(t)));
self[ty.params]
.types
.iter()
.any(|t| self.ty_contains_borrow_resource(t))
}
}
Loading