Change VMMemoryDefinition::current_length to usize (#3134)

* Change VMMemoryDefinition::current_length to `usize`

This commit changes the definition of
`VMMemoryDefinition::current_length` to `usize` from its previous
definition of `u32`. This is a pretty impactful change because it also
changes the cranelift semantics of "dynamic" heaps where the bound
global value specifier must now match the pointer type for the platform
rather than the index type for the heap.

The motivation for this change is that the `current_length` field (or
bound for the heap) is intended to reflect the current size of the heap.
This is bound by `usize` on the host platform rather than `u32` or`
u64`. The previous choice of `u32` couldn't represent a 4GB memory
because we couldn't put a number representing 4GB into the
`current_length` field. By using `usize`, which reflects the host's
memory allocation, this should better reflect the size of the heap and
allows Wasmtime to support a full 4GB heap for a wasm program (instead
of 4GB minus one page).

This commit also updates the legalization of the `heap_addr` clif
instruction to appropriately cast the address to the platform's pointer
type, handling bounds checks along the way. The practical impact for
today's targets is that a `uextend` is happening sooner than it happened
before, but otherwise there is no intended impact of this change. In the
future when 64-bit memories are supported there will likely need to be
fancier logic which handles offsets a bit differently (especially in the
case of a 64-bit memory on a 32-bit host).

The clif `filetest` changes should show the differences in codegen, and
the Wasmtime changes are largely removing casts here and there.

Closes #3022

* Add tests for memory.size at maximum memory size

* Add a dfg helper method
This commit is contained in:
Alex Crichton
2021-08-02 13:09:40 -05:00
committed by GitHub
parent 87fefd8a21
commit 63a3bbbf5a
19 changed files with 146 additions and 140 deletions

View File

@@ -1154,7 +1154,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
base: ptr,
offset: Offset32::new(current_length_offset),
global_type: self.offsets.type_of_vmmemory_definition_current_length(),
global_type: pointer_type,
readonly: false,
});
(
@@ -1417,7 +1417,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
.vmctx_vmmemory_definition_current_length(def_index),
)
.unwrap();
pos.ins().load(I32, ir::MemFlags::trusted(), base, offset)
pos.ins()
.load(pointer_type, ir::MemFlags::trusted(), base, offset)
}
None => {
let offset = i32::try_from(self.offsets.vmctx_vmmemory_import_from(index)).unwrap();
@@ -1425,7 +1426,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
pos.ins()
.load(pointer_type, ir::MemFlags::trusted(), base, offset);
pos.ins().load(
I32,
pointer_type,
ir::MemFlags::trusted(),
vmmemory_ptr,
i32::from(self.offsets.vmmemory_definition_current_length()),
@@ -1435,7 +1436,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
let current_length_in_pages = pos
.ins()
.udiv_imm(current_length_in_bytes, i64::from(WASM_PAGE_SIZE));
Ok(current_length_in_pages)
if pointer_type == I32 {
Ok(current_length_in_pages)
} else {
assert_eq!(pointer_type, I64);
Ok(pos.ins().ireduce(I32, current_length_in_pages))
}
}
fn translate_memory_copy(

View File

@@ -422,23 +422,11 @@ impl<P: PtrSize> VMOffsets<P> {
1 * self.pointer_size()
}
/// The size of the `current_length` field.
#[inline]
pub fn size_of_vmmemory_definition_current_length(&self) -> u8 {
4
}
/// Return the size of `VMMemoryDefinition`.
#[inline]
pub fn size_of_vmmemory_definition(&self) -> u8 {
2 * self.pointer_size()
}
/// The type of the `current_length` field.
#[inline]
pub fn type_of_vmmemory_definition_current_length(&self) -> ir::Type {
ir::Type::int(u16::from(self.size_of_vmmemory_definition_current_length()) * 8).unwrap()
}
}
/// Offsets for `VMGlobalImport`.

View File

@@ -626,13 +626,11 @@ impl Instance {
let src_mem = self.get_memory(src_index);
let dst_mem = self.get_memory(dst_index);
if src
.checked_add(len)
.map_or(true, |n| n > src_mem.current_length)
|| dst
.checked_add(len)
.map_or(true, |m| m > dst_mem.current_length)
{
if src.checked_add(len).map_or(true, |n| {
usize::try_from(n).unwrap() > src_mem.current_length
}) || dst.checked_add(len).map_or(true, |m| {
usize::try_from(m).unwrap() > dst_mem.current_length
}) {
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
}
@@ -664,10 +662,9 @@ impl Instance {
) -> Result<(), Trap> {
let memory = self.get_memory(memory_index);
if dst
.checked_add(len)
.map_or(true, |m| m > memory.current_length)
{
if dst.checked_add(len).map_or(true, |m| {
usize::try_from(m).unwrap() > memory.current_length
}) {
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
}
@@ -726,10 +723,10 @@ impl Instance {
if src
.checked_add(len)
.map_or(true, |n| n as usize > data.len())
|| dst
.checked_add(len)
.map_or(true, |m| m > memory.current_length)
.map_or(true, |n| usize::try_from(n).unwrap() > data.len())
|| dst.checked_add(len).map_or(true, |m| {
usize::try_from(m).unwrap() > memory.current_length
})
{
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
}

View File

@@ -309,7 +309,7 @@ fn check_memory_init_bounds(
let end = start.checked_add(init.data.len());
match end {
Some(end) if end <= memory.current_length as usize => {
Some(end) if end <= memory.current_length => {
// Initializer is in bounds
}
_ => {
@@ -382,9 +382,8 @@ fn initialize_instance(
MemoryInitialization::Paged { map, out_of_bounds } => {
for (index, pages) in map {
let memory = instance.memory(index);
let slice = unsafe {
slice::from_raw_parts_mut(memory.base, memory.current_length as usize)
};
let slice =
unsafe { slice::from_raw_parts_mut(memory.base, memory.current_length) };
for (page_index, page) in pages.iter().enumerate() {
if let Some(data) = page {

View File

@@ -155,10 +155,6 @@ impl RuntimeLinearMemory for MmapMemory {
// Linear memory size would exceed the index range.
return None;
}
// FIXME: https://github.com/bytecodealliance/wasmtime/issues/3022
if new_pages == WASM_MAX_PAGES {
return None;
}
let delta_bytes = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize;
let prev_bytes = usize::try_from(prev_pages).unwrap() * WASM_PAGE_SIZE as usize;
@@ -198,8 +194,7 @@ impl RuntimeLinearMemory for MmapMemory {
fn vmmemory(&self) -> VMMemoryDefinition {
VMMemoryDefinition {
base: unsafe { self.mmap.alloc.as_mut_ptr().add(self.pre_guard_size) },
current_length: u32::try_from(self.mmap.size as usize * WASM_PAGE_SIZE as usize)
.unwrap(),
current_length: self.mmap.size as usize * WASM_PAGE_SIZE as usize,
}
}
}
@@ -276,13 +271,6 @@ impl Memory {
}
fn limit_new(plan: &MemoryPlan, limiter: Option<&mut dyn ResourceLimiter>) -> Result<()> {
// FIXME: https://github.com/bytecodealliance/wasmtime/issues/3022
if plan.memory.minimum == WASM_MAX_PAGES {
bail!(
"memory minimum size of {} pages exceeds memory limits",
plan.memory.minimum
);
}
if let Some(limiter) = limiter {
if !limiter.memory_growing(0, plan.memory.minimum, plan.memory.maximum) {
bail!(
@@ -374,10 +362,6 @@ impl Memory {
if new_size > maximum.unwrap_or(WASM_MAX_PAGES) {
return None;
}
// FIXME: https://github.com/bytecodealliance/wasmtime/issues/3022
if new_size == WASM_MAX_PAGES {
return None;
}
let start = usize::try_from(old_size).unwrap() * WASM_PAGE_SIZE as usize;
let len = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize;
@@ -397,7 +381,7 @@ impl Memory {
match self {
Memory::Static { base, size, .. } => VMMemoryDefinition {
base: base.as_ptr() as *mut _,
current_length: u32::try_from(*size as usize * WASM_PAGE_SIZE as usize).unwrap(),
current_length: *size as usize * WASM_PAGE_SIZE as usize,
},
Memory::Dynamic(mem) => mem.vmmemory(),
}

View File

@@ -207,7 +207,7 @@ pub struct VMMemoryDefinition {
pub base: *mut u8,
/// The current logical size of this linear memory in bytes.
pub current_length: u32,
pub current_length: usize,
}
#[cfg(test)]

View File

@@ -318,7 +318,7 @@ impl Memory {
unsafe {
let store = store.into();
let definition = *store[self.0].definition;
slice::from_raw_parts(definition.base, definition.current_length as usize)
slice::from_raw_parts(definition.base, definition.current_length)
}
}
@@ -334,7 +334,7 @@ impl Memory {
unsafe {
let store = store.into();
let definition = *store[self.0].definition;
slice::from_raw_parts_mut(definition.base, definition.current_length as usize)
slice::from_raw_parts_mut(definition.base, definition.current_length)
}
}
@@ -395,7 +395,7 @@ impl Memory {
///
/// Panics if this memory doesn't belong to `store`.
pub fn data_size(&self, store: impl AsContext) -> usize {
unsafe { (*store.as_context()[self.0].definition).current_length as usize }
unsafe { (*store.as_context()[self.0].definition).current_length }
}
/// Returns the size, in WebAssembly pages, of this wasm memory.

View File

@@ -3,13 +3,11 @@ use crate::store::{InstanceId, StoreOpaque};
use crate::trampoline::create_handle;
use crate::{Limits, MemoryType};
use anyhow::{anyhow, Result};
use std::sync::Arc;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, MemoryPlan, MemoryStyle, Module, WASM_PAGE_SIZE};
use wasmtime_runtime::{RuntimeLinearMemory, RuntimeMemoryCreator, VMMemoryDefinition};
use std::convert::TryFrom;
use std::sync::Arc;
pub fn create_memory(store: &mut StoreOpaque<'_>, memory: &MemoryType) -> Result<InstanceId> {
let mut module = Module::new();
@@ -49,8 +47,7 @@ impl RuntimeLinearMemory for LinearMemoryProxy {
fn vmmemory(&self) -> VMMemoryDefinition {
VMMemoryDefinition {
base: self.mem.as_ptr(),
current_length: u32::try_from(self.mem.size() as usize * WASM_PAGE_SIZE as usize)
.unwrap(),
current_length: self.mem.size() as usize * WASM_PAGE_SIZE as usize,
}
}
}