Skip to content

Commit 89fdfa1

Browse files
Fix reverse PC to function lookups for sparse func kinds (#11766)
* Add failing test for index building * Fix reverse PC to function lookups for sparse func kinds We were previously computing an offset within the kind's sparse index space, and needed to add its sparse start index to get the actual `SparseIndex` for the function. Fixes #11749 --------- Co-authored-by: Alex Crichton <[email protected]>
1 parent 47fb746 commit 89fdfa1

File tree

8 files changed

+117
-5
lines changed

8 files changed

+117
-5
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ base64 = "0.22.1"
413413
termcolor = "1.4.1"
414414
flate2 = "1.0.30"
415415
tokio-util = "0.7.4"
416+
arbtest = "0.3.1"
416417

417418
# =============================================================================
418419
#

cranelift/assembler-x64/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ capstone = { workspace = true, optional = true }
1212

1313
[dev-dependencies]
1414
arbitrary = { workspace = true, features = ["derive"] }
15-
arbtest = "0.3.1"
15+
arbtest = { workspace = true }
1616
capstone = { workspace = true }
1717

1818
[build-dependencies]

crates/environ/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ wat = { workspace = true }
4747
# feature we don't require anything to be aligned so it doesn't matter the
4848
# alignment of the bytes that we're reading.
4949
object = { workspace = true, features = ['unaligned'] }
50+
arbtest = { workspace = true }
51+
arbitrary = { workspace = true, features = ["derive"] }
5052

5153
[[example]]
5254
name = "factc"

crates/environ/src/builtin.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,13 @@ macro_rules! declare_builtin_index {
277277

278278
$for_each_builtin!(declare_builtin_index_constructors);
279279
}
280+
281+
#[cfg(test)]
282+
impl arbitrary::Arbitrary<'_> for $index_name {
283+
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
284+
Ok(Self(u.int_in_range(0..=Self::len() - 1)?))
285+
}
286+
}
280287
};
281288
}
282289

crates/environ/src/hostcall.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::component::ComponentBuiltinFunctionIndex;
99
/// is used by Pulley for example to identify how transitions from the guest to
1010
/// the host are performed.
1111
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12+
#[cfg_attr(test, derive(arbitrary::Arbitrary))]
1213
pub enum HostCall {
1314
/// An "array call" is being done which means that the wasm is calling the
1415
/// host using the array calling convention (e.g. `VMArrayCallNative`).

crates/environ/src/key.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::component;
66
use crate::{
77
BuiltinFunctionIndex, DefinedFuncIndex, HostCall, ModuleInternedTypeIndex, StaticModuleIndex,
88
};
9-
use core::cmp;
9+
use core::{cmp, fmt};
1010
use serde_derive::{Deserialize, Serialize};
1111

1212
/// The kind of a function that is being compiled, linked, or otherwise
@@ -15,6 +15,7 @@ use serde_derive::{Deserialize, Serialize};
1515
/// This is like a `FuncKey` but without any payload values.
1616
#[repr(u32)]
1717
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18+
#[cfg_attr(test, derive(arbitrary::Arbitrary))]
1819
pub enum FuncKeyKind {
1920
/// A Wasm-defined function.
2021
DefinedWasmFunction = FuncKey::new_kind(0b000),
@@ -76,9 +77,25 @@ impl FuncKeyKind {
7677
/// The namespace half of a `FuncKey`.
7778
///
7879
/// This is an opaque combination of the key's kind and module index, if any.
79-
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
80+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8081
pub struct FuncKeyNamespace(u32);
8182

83+
impl fmt::Debug for FuncKeyNamespace {
84+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85+
struct Hex<T: fmt::LowerHex>(T);
86+
impl<T: fmt::LowerHex> fmt::Debug for Hex<T> {
87+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88+
write!(f, "{:#x}", self.0)
89+
}
90+
}
91+
f.debug_struct("FuncKeyNamespace")
92+
.field("raw", &Hex(self.0))
93+
.field("kind", &self.kind())
94+
.field("module", &self.module())
95+
.finish()
96+
}
97+
}
98+
8299
impl From<FuncKeyNamespace> for u32 {
83100
fn from(ns: FuncKeyNamespace) -> Self {
84101
ns.0
@@ -123,6 +140,10 @@ impl FuncKeyNamespace {
123140
let raw = self.0 & FuncKey::KIND_MASK;
124141
FuncKeyKind::from_raw(raw)
125142
}
143+
144+
fn module(&self) -> u32 {
145+
self.0 & FuncKey::MODULE_MASK
146+
}
126147
}
127148

128149
/// The index half of a `FuncKey`.
@@ -153,6 +174,7 @@ impl FuncKeyIndex {
153174

154175
/// ABI signature of functions that are generated here.
155176
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
177+
#[cfg_attr(test, derive(arbitrary::Arbitrary))]
156178
pub enum Abi {
157179
/// The "wasm" ABI, or suitable to be a `wasm_call` field of a `VMFuncRef`.
158180
Wasm = 0,

crates/environ/src/module_artifacts.rs

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,17 @@ impl CompiledFunctionsTable {
478478
let key_index = index.as_u32() - start.as_u32();
479479
FuncKeyIndex::from_raw(key_index)
480480
} else {
481-
let sparse_index = index.as_u32() - start.as_u32();
482-
let sparse_index = SparseIndex::from_u32(sparse_index);
481+
let sparse_offset = index.as_u32() - start.as_u32();
482+
let sparse_start = self.sparse_starts[ns_idx];
483+
let sparse_index = SparseIndex::from_u32(sparse_start.as_u32() + sparse_offset);
484+
debug_assert!(
485+
{
486+
let range = self.sparse_range(ns_idx);
487+
range.start <= sparse_index && sparse_index < range.end
488+
},
489+
"{sparse_index:?} is not within {:?}",
490+
self.sparse_range(ns_idx)
491+
);
483492
self.sparse_indices[sparse_index]
484493
};
485494
let key = FuncKey::from_parts(key_ns, key_index);
@@ -732,4 +741,72 @@ mod tests {
732741
}
733742
}
734743
}
744+
745+
#[test]
746+
fn reverse_lookups() {
747+
use arbitrary::{Result, Unstructured};
748+
749+
arbtest::arbtest(|u| run(u)).budget_ms(1_000);
750+
751+
fn run(u: &mut Unstructured<'_>) -> Result<()> {
752+
let mut funcs = Vec::new();
753+
754+
// Build up a random set of functions with random indices.
755+
for _ in 0..u.int_in_range(1..=200)? {
756+
let key = match u.int_in_range(0..=6)? {
757+
0 => FuncKey::DefinedWasmFunction(idx(u, 10)?, idx(u, 200)?),
758+
1 => FuncKey::ArrayToWasmTrampoline(idx(u, 10)?, idx(u, 200)?),
759+
2 => FuncKey::WasmToArrayTrampoline(idx(u, 100)?),
760+
3 => FuncKey::WasmToBuiltinTrampoline(u.arbitrary()?),
761+
4 => FuncKey::PulleyHostCall(u.arbitrary()?),
762+
5 => FuncKey::ComponentTrampoline(u.arbitrary()?, idx(u, 50)?),
763+
6 => FuncKey::ResourceDropTrampoline,
764+
_ => unreachable!(),
765+
};
766+
funcs.push(key);
767+
}
768+
769+
// Sort/dedup our list of `funcs` to satisfy the requirement of
770+
// `CompiledFunctionsTableBuilder::push_func`.
771+
funcs.sort();
772+
funcs.dedup();
773+
774+
let mut builder = CompiledFunctionsTableBuilder::new();
775+
let mut size = 0;
776+
let mut expected = Vec::new();
777+
for key in funcs {
778+
let length = u.int_in_range(1..=10)?;
779+
for _ in 0..length {
780+
expected.push(key);
781+
}
782+
// println!("push {key:?} - {length}");
783+
builder.push_func(
784+
key,
785+
FunctionLoc {
786+
start: size,
787+
length,
788+
},
789+
FilePos::none(),
790+
);
791+
size += length;
792+
}
793+
let index = builder.finish();
794+
795+
let mut expected = expected.iter();
796+
for i in 0..size {
797+
// println!("lookup {i}");
798+
let actual = index.func_by_text_offset(i).unwrap();
799+
assert_eq!(Some(&actual), expected.next());
800+
}
801+
802+
Ok(())
803+
}
804+
805+
fn idx<T>(u: &mut Unstructured<'_>, max: usize) -> Result<T>
806+
where
807+
T: EntityRef,
808+
{
809+
Ok(T::new(u.int_in_range(0..=max - 1)?))
810+
}
811+
}
735812
}

0 commit comments

Comments
 (0)