Skip to content

Commit

Permalink
Support executing Pulley in Wasmtime (#9744)
Browse files Browse the repository at this point in the history
* Support executing Pulley in Wasmtime

This commit is the initial implementation of executing the Pulley
interpreter from the `wasmtime` crate. This gives access to all of the
`wasmtime` crate's runtime APIs backed by execution of bytecode in
Pulley. This builds on the previous PRs I've been making for support in
Pulley to culminate in testing on CI in this PR. This PR handles some
final tidbits related to producing a runnable image that can be
interpreted by the `wasmtime` crate such as:

* Pulley compilation artifacts are no longer marked as natively
  executable, just read-only.
* Pulley compilation artifacts include wasm-to-array trampolines like
  normal platforms (removes a pulley special-case).
* Dispatch of host calls from Pulley to the Wasmtime runtime are
  implemented.
* Pulley's list of panicking wasm features is slimmed down as most are
  covered by "this lowering isn't supported" errors.
* Execution of wasm code now has an `if` to see whether Pulley is
  enabled within a `Store` or not.
* Traps and basic "unwinding" of the pulley stack are implemented (e.g.
  a "pulley version" of `setjmp` and `longjmp`, sort of)
* Halting the interpreter has been refactored to help shrink the size of
  `ControlFlow<Done>` and handle metadata with each done state.

Some minor refactorings are also included here and there along with a
few fixes here and there necessary to get tests passing.

The next major part of this commit is updates to our `wast` test suite
and executing all `*.wast` files. Pulley is now executed by default for
all files as a new execution engine. This means that all platforms in CI
will start executing Pulley tests. At this time almost all tests are
flagged as "expected to fail" but there are a small handful of
allow-listed tests which are expected to pass. This exact list will
change over time as CLIF lowerings are implemented and the interpreter
is extended.

Follow-up PRs will extend the testing strategy further such as:

* Extending `#[wasmtime_test]` to include Pulley in addition to Winch.
* Getting testing set up on CI for 32-bit platforms.

prtest:full

* Fix pulley fuzz build

* Fix clippy lints

* Shuffle around some `#[cfg]`'d code

* Remove unused imports

* Update feature sets testing MIRI

Enable pulley for wasmtime/wasmtime-cli and also enable all features for
wasmtime-environ

* Round up pulley's page size to 64k

* Skip pulley tests on s390x for now

* Add a safety rail for matching a pulley target to the host

* Fix more pulley tests on s390x

* Review comments

* Fix fuzz build
  • Loading branch information
alexcrichton authored Dec 6, 2024
1 parent 792cccc commit 70a3793
Show file tree
Hide file tree
Showing 42 changed files with 1,120 additions and 455 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1046,9 +1046,9 @@ jobs:
strategy:
matrix:
crate:
- "wasmtime"
- "wasmtime --features pulley"
- "wasmtime-cli"
- "wasmtime-environ"
- "wasmtime-environ --all-features"
- "pulley-interpreter --all-features"
needs: determine
if: needs.determine.outputs.test-miri && github.repository == 'bytecodealliance/wasmtime'
Expand Down
4 changes: 3 additions & 1 deletion cranelift/codegen/src/isa/pulley_shared/inst/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ impl Amode {
+ frame_layout.outgoing_args_size;
i64::from(sp_offset) - offset
}
StackAMode::Slot(offset) => *offset,
StackAMode::Slot(offset) => {
offset + i64::from(state.frame_layout().outgoing_args_size)
}
StackAMode::OutgoingArg(offset) => *offset,
},
}
Expand Down
10 changes: 8 additions & 2 deletions cranelift/codegen/src/isa/pulley_shared/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,15 @@ fn pulley_emit<P>(
*start_offset = sink.cur_offset();
}

Inst::PushFrame => enc::push_frame(sink),
Inst::PushFrame => {
sink.add_trap(ir::TrapCode::STACK_OVERFLOW);
enc::push_frame(sink);
}
Inst::PopFrame => enc::pop_frame(sink),
Inst::StackAlloc32 { amt } => enc::stack_alloc32(sink, *amt),
Inst::StackAlloc32 { amt } => {
sink.add_trap(ir::TrapCode::STACK_OVERFLOW);
enc::stack_alloc32(sink, *amt);
}
Inst::StackFree32 { amt } => enc::stack_free32(sink, *amt),

Inst::Zext8 { dst, src } => enc::zext8(sink, dst, src),
Expand Down
8 changes: 4 additions & 4 deletions cranelift/codegen/src/isa/pulley_shared/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@

;;;; Rules for `trapz` and `trapnz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rule (lower (trapz a @ (value_type (fits_in_64 ty)) code))
(rule (lower (trapz a @ (value_type (ty_32_or_64 ty)) code))
(let ((zero Reg (pulley_xconst8 0)))
(side_effect (pulley_trap_if (IntCC.Equal)
(ty_to_operand_size ty)
a
zero
code))))

(rule (lower (trapnz a @ (value_type (fits_in_64 ty)) code))
(rule (lower (trapnz a @ (value_type (ty_32_or_64 ty)) code))
(let ((zero Reg (pulley_xconst8 0)))
(side_effect (pulley_trap_if (IntCC.NotEqual)
(ty_to_operand_size ty)
Expand All @@ -77,14 +77,14 @@

;; Fold `(trap[n]z (icmp ...))` together.

(rule 1 (lower (trapz (icmp cc a b @ (value_type (fits_in_64 ty))) code))
(rule 1 (lower (trapz (icmp cc a b @ (value_type (ty_32_or_64 ty))) code))
(side_effect (pulley_trap_if (intcc_complement cc)
(ty_to_operand_size ty)
a
b
code)))

(rule 1 (lower (trapnz (icmp cc a b @ (value_type (fits_in_64 ty))) code))
(rule 1 (lower (trapnz (icmp cc a b @ (value_type (ty_32_or_64 ty))) code))
(side_effect (pulley_trap_if cc
(ty_to_operand_size ty)
a
Expand Down
11 changes: 3 additions & 8 deletions crates/cranelift/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use wasmtime_environ::{
AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, DefinedFuncIndex, FlagValue,
FunctionBodyData, FunctionLoc, HostCall, ModuleTranslation, ModuleTypesBuilder, PtrSize,
RelocationTarget, StackMapInformation, StaticModuleIndex, TrapEncodingBuilder, TrapSentinel,
Tunables, VMOffsets, WasmFuncType, WasmFunctionInfo, WasmValType,
TripleExt, Tunables, VMOffsets, WasmFuncType, WasmFunctionInfo, WasmValType,
};

#[cfg(feature = "component-model")]
Expand Down Expand Up @@ -152,12 +152,7 @@ impl Compiler {
// `call` instruction where the name is `colocated: false`. This will
// force a pulley-specific relocation to get emitted in addition to
// using the `call_indirect_host` instruction.
let is_pulley = match self.isa.triple().architecture {
target_lexicon::Architecture::Pulley32 => true,
target_lexicon::Architecture::Pulley64 => true,
_ => false,
};
if is_pulley {
if self.isa.triple().is_pulley() {
let mut new_signature = signature.clone();
new_signature
.params
Expand Down Expand Up @@ -246,7 +241,7 @@ impl wasmtime_environ::Compiler for Compiler {
// abort for the whole program since the runtime limits configured by
// the embedder should cause wasm to trap before it reaches that
// (ensuring the host has enough space as well for its functionality).
if !func_env.is_pulley() {
if !isa.triple().is_pulley() {
let vmctx = context
.func
.create_global_value(ir::GlobalValueData::VMContext);
Expand Down
20 changes: 5 additions & 15 deletions crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ use wasmparser::{Operator, WasmFeatures};
use wasmtime_environ::{
BuiltinFunctionIndex, DataIndex, ElemIndex, EngineOrModuleTypeIndex, FuncIndex, GlobalIndex,
IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex, ModuleTranslation,
ModuleTypesBuilder, PtrSize, Table, TableIndex, Tunables, TypeConvert, TypeIndex, VMOffsets,
WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult,
WasmValType,
ModuleTypesBuilder, PtrSize, Table, TableIndex, TripleExt, Tunables, TypeConvert, TypeIndex,
VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmRefType,
WasmResult, WasmValType,
};
use wasmtime_environ::{FUNCREF_INIT_BIT, FUNCREF_MASK};

Expand Down Expand Up @@ -1187,16 +1187,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
i32::from(self.offsets.ptr.vm_func_ref_type_index()),
)
}

/// Returns whether the current compilation target is for the Pulley
/// interpreter.
pub fn is_pulley(&self) -> bool {
match self.isa.triple().architecture {
target_lexicon::Architecture::Pulley32 => true,
target_lexicon::Architecture::Pulley64 => true,
_ => false,
}
}
}

struct Call<'a, 'func, 'module_env> {
Expand Down Expand Up @@ -3419,7 +3409,7 @@ impl FuncEnvironment<'_> {
/// being targetted since the Pulley runtime doesn't catch segfaults for
/// itself.
pub fn clif_memory_traps_enabled(&self) -> bool {
self.tunables.signals_based_traps && !self.is_pulley()
self.tunables.signals_based_traps && !self.isa.triple().is_pulley()
}

/// Returns whether it's acceptable to have CLIF instructions natively trap,
Expand All @@ -3429,7 +3419,7 @@ impl FuncEnvironment<'_> {
/// unconditionally since Pulley doesn't use hardware-based traps in its
/// runtime.
pub fn clif_instruction_traps_enabled(&self) -> bool {
self.tunables.signals_based_traps || self.is_pulley()
self.tunables.signals_based_traps || self.isa.triple().is_pulley()
}
}

Expand Down
18 changes: 15 additions & 3 deletions crates/cranelift/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ use cranelift_control::ControlPlane;
use gimli::write::{Address, EhFrame, EndianVec, FrameTable, Writer};
use gimli::RunTimeEndian;
use object::write::{Object, SectionId, StandardSegment, Symbol, SymbolId, SymbolSection};
use object::{Architecture, SectionKind, SymbolFlags, SymbolKind, SymbolScope};
use object::{Architecture, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope};
use std::collections::HashMap;
use std::ops::Range;
use wasmtime_environ::obj::LibCall;
use wasmtime_environ::{Compiler, Unsigned};
use wasmtime_environ::obj::{self, LibCall};
use wasmtime_environ::{Compiler, TripleExt, Unsigned};

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

Expand Down Expand Up @@ -83,6 +83,18 @@ impl<'a> ModuleTextBuilder<'a> {
SectionKind::Text,
);

// If this target is Pulley then flag the text section as not needing the
// executable bit in virtual memory which means that the runtime won't
// try to call `Mmap::make_exectuable`, which makes Pulley more
// portable.
if compiler.triple().is_pulley() {
let section = obj.section_mut(text_section);
assert!(matches!(section.flags, SectionFlags::None));
section.flags = SectionFlags::Elf {
sh_flags: obj::SH_WASMTIME_NOT_EXECUTED,
};
}

Self {
compiler,
obj,
Expand Down
3 changes: 3 additions & 0 deletions crates/environ/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ pub trait Compiler: Send + Sync {
// 64 KB is the maximal page size (i.e. memory translation granule size)
// supported by the architecture and is used on some platforms.
(_, Architecture::Aarch64(..)) => 0x10000,
// Conservatively assume the max-of-all-supported-hosts for pulley
// and round up to 64k.
(_, Architecture::Pulley32 | Architecture::Pulley64) => 0x10000,
_ => 0x1000,
}
}
Expand Down
17 changes: 1 addition & 16 deletions crates/environ/src/compile/module_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ pub struct ObjectBuilder<'a> {
/// will go.
data: SectionId,

/// The target triple for this compilation.
triple: target_lexicon::Triple,

/// The section identifier for function name information, or otherwise where
/// the `name` custom section of wasm is copied into.
///
Expand All @@ -46,11 +43,7 @@ pub struct ObjectBuilder<'a> {

impl<'a> ObjectBuilder<'a> {
/// Creates a new builder for the `obj` specified.
pub fn new(
mut obj: Object<'a>,
tunables: &'a Tunables,
triple: target_lexicon::Triple,
) -> ObjectBuilder<'a> {
pub fn new(mut obj: Object<'a>, tunables: &'a Tunables) -> ObjectBuilder<'a> {
let data = obj.add_section(
obj.segment_name(StandardSegment::Data).to_vec(),
obj::ELF_WASM_DATA.as_bytes().to_vec(),
Expand All @@ -60,7 +53,6 @@ impl<'a> ObjectBuilder<'a> {
obj,
tunables,
data,
triple,
names: None,
dwarf: None,
}
Expand Down Expand Up @@ -225,12 +217,6 @@ impl<'a> ObjectBuilder<'a> {
self.push_debuginfo(&mut dwarf, &debuginfo);
}

let is_pulley = matches!(
self.triple.architecture,
target_lexicon::Architecture::Pulley32 | target_lexicon::Architecture::Pulley64
);
assert!(!is_pulley || wasm_to_array_trampolines.is_empty());

Ok(CompiledModuleInfo {
module,
funcs,
Expand All @@ -240,7 +226,6 @@ impl<'a> ObjectBuilder<'a> {
has_unparsed_debuginfo,
code_section_offset: debuginfo.wasm_file.code_section_offset,
has_wasm_debuginfo: self.tunables.parse_wasm_debuginfo,
is_pulley,
dwarf,
},
})
Expand Down
21 changes: 16 additions & 5 deletions crates/environ/src/component/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,19 +452,30 @@ pub struct CanonicalOptions {
}

/// Possible encodings of strings within the component model.
//
// Note that the `repr(u8)` is load-bearing here since this is used in an
// `extern "C" fn()` function argument which is called from cranelift-compiled
// code so we must know the representation of this.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[allow(missing_docs, reason = "self-describing variants")]
#[repr(u8)]
pub enum StringEncoding {
Utf8,
Utf16,
CompactUtf16,
}

impl StringEncoding {
/// Decodes the `u8` provided back into a `StringEncoding`, if it's valid.
pub fn from_u8(val: u8) -> Option<StringEncoding> {
if val == StringEncoding::Utf8 as u8 {
return Some(StringEncoding::Utf8);
}
if val == StringEncoding::Utf16 as u8 {
return Some(StringEncoding::Utf16);
}
if val == StringEncoding::CompactUtf16 as u8 {
return Some(StringEncoding::CompactUtf16);
}
None
}
}

/// Possible transcoding operations that must be provided by the host.
///
/// Note that each transcoding operation may have a unique signature depending
Expand Down
18 changes: 18 additions & 0 deletions crates/environ/src/ext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use target_lexicon::{Architecture, Triple};

/// Extension methods for `target_lexicon::Triple`.
pub trait TripleExt {
/// Helper for returning whether this target is for pulley, wasmtime's
/// interpreter.
fn is_pulley(&self) -> bool;
}

impl TripleExt for Triple {
fn is_pulley(&self) -> bool {
match self.architecture {
Architecture::Pulley32 => true,
Architecture::Pulley64 => true,
_ => false,
}
}
}
2 changes: 2 additions & 0 deletions crates/environ/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod address_map;
mod builtin;
mod demangling;
mod error;
mod ext;
mod gc;
mod hostcall;
mod module;
Expand All @@ -33,6 +34,7 @@ mod tunables;
mod types;
mod vmoffsets;

pub use self::ext::*;
pub use crate::address_map::*;
pub use crate::builtin::*;
pub use crate::demangling::*;
Expand Down
4 changes: 0 additions & 4 deletions crates/environ/src/module_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ pub struct Metadata {
/// weren't found in the original wasm module itself.
pub has_wasm_debuginfo: bool,

/// Whether this artifact contains Pulley bytecode (instead of machine code)
/// or not.
pub is_pulley: bool,

/// Dwarf sections and the offsets at which they're stored in the
/// ELF_WASMTIME_DWARF
pub dwarf: Vec<(u8, Range<u64>)>,
Expand Down
2 changes: 1 addition & 1 deletion crates/fuzzing/src/generators/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ impl Config {
InstanceAllocationStrategy::Pooling(_)
),
compiler: match self.wasmtime.compiler_strategy {
CompilerStrategy::Cranelift => wasmtime_wast_util::Compiler::Cranelift,
CompilerStrategy::Cranelift => wasmtime_wast_util::Compiler::CraneliftNative,
CompilerStrategy::Winch => wasmtime_wast_util::Compiler::Winch,
},
}
Expand Down
21 changes: 14 additions & 7 deletions crates/misc/component-test-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,23 @@ forward_impls! {

/// Helper method to apply `wast_config` to `config`.
pub fn apply_wast_config(config: &mut Config, wast_config: &wasmtime_wast_util::WastConfig) {
use wasmtime_wast_util::{Collector, Compiler};

config.strategy(match wast_config.compiler {
wasmtime_wast_util::Compiler::Cranelift => wasmtime::Strategy::Cranelift,
wasmtime_wast_util::Compiler::Winch => wasmtime::Strategy::Winch,
Compiler::CraneliftNative | Compiler::CraneliftPulley => wasmtime::Strategy::Cranelift,
Compiler::Winch => wasmtime::Strategy::Winch,
});
config.collector(match wast_config.collector {
wasmtime_wast_util::Collector::Auto => wasmtime::Collector::Auto,
wasmtime_wast_util::Collector::Null => wasmtime::Collector::Null,
wasmtime_wast_util::Collector::DeferredReferenceCounting => {
wasmtime::Collector::DeferredReferenceCounting
if let Compiler::CraneliftPulley = wast_config.compiler {
if cfg!(target_pointer_width = "32") {
config.target("pulley32").unwrap();
} else {
config.target("pulley64").unwrap();
}
}
config.collector(match wast_config.collector {
Collector::Auto => wasmtime::Collector::Auto,
Collector::Null => wasmtime::Collector::Null,
Collector::DeferredReferenceCounting => wasmtime::Collector::DeferredReferenceCounting,
});
}

Expand Down
4 changes: 2 additions & 2 deletions crates/test-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl TestConfig {
self.strategies.retain(|s| *s != Compiler::Winch);
Ok(())
} else if meta.path.is_ident("Cranelift") {
self.strategies.retain(|s| *s != Compiler::Cranelift);
self.strategies.retain(|s| *s != Compiler::CraneliftNative);
Ok(())
} else {
Err(meta.error("Unknown strategy"))
Expand Down Expand Up @@ -97,7 +97,7 @@ impl TestConfig {
impl Default for TestConfig {
fn default() -> Self {
Self {
strategies: vec![Compiler::Cranelift, Compiler::Winch],
strategies: vec![Compiler::CraneliftNative, Compiler::Winch],
flags: Default::default(),
test_attribute: None,
}
Expand Down
Loading

0 comments on commit 70a3793

Please sign in to comment.