Skip to content

Commit 08b1328

Browse files
committed
[stack-switching] Compression test styles
1 parent 41b5a8f commit 08b1328

1 file changed

Lines changed: 72 additions & 16 deletions

File tree

test/unittest/CompressionTest.v3

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// See LICENSE for details of Apache 2.0 license.
33

44
// Zero-initialized reentry label used as a placeholder in test frames.
5+
// TODO: fuzz this for testing
56
var DUMMY_REENTRY: TargetReentryLabel;
67

7-
// A strategy adapter that erases the compressed stack type, providing a uniform
8-
// roundtrip interface for testing all compression strategies.
8+
// An adapter that erases the compressed stack type to perform strategy-agnostic roundtrip tests.
99
class CompressionStrategyAdapter {
1010
def roundtrip: Array<RelocatableFrame> -> Array<RelocatableFrame>;
1111
new(roundtrip) { }
@@ -26,7 +26,7 @@ def packedRoundtrip(frames: Array<RelocatableFrame>) -> Array<RelocatableFrame>
2626
return packed.decompress(packed.compress(frames));
2727
}
2828

29-
// Register all tests across all strategies.
29+
// Register all tests across all strategies: for each test, register it once for every strategy.
3030
def X_ = registerAll([
3131
("i32", test_i32),
3232
("i64", test_i64),
@@ -118,7 +118,7 @@ class CompressionTester(t: Tester, adapter: CompressionStrategyAdapter) {
118118
}
119119
}
120120

121-
// === Part 1: Individual value type round-trips ===
121+
// ===== Compression roundtrip tests =====
122122

123123
def test_i32(t: CompressionTester) {
124124
t.assert_roundtrip_vals([Value.I32(0)]);
@@ -170,8 +170,6 @@ def test_ref(t: CompressionTester) {
170170
}
171171
}
172172

173-
// === Part 2: Edge cases and multi-frame ===
174-
175173
def test_empty_frames(t: CompressionTester) {
176174
var got = t.roundtrip([]);
177175
t.assert_length(0, got.length, "frames");
@@ -224,8 +222,6 @@ def test_single_val_per_frame(t: CompressionTester) {
224222
t.assert_frames_eq(frames, got);
225223
}
226224

227-
// === Part 3: Continuation values ===
228-
229225
def test_cont(t: CompressionTester) {
230226
var stack = Target.newWasmStack();
231227
var cont = Continuations.makeContinuation(stack);
@@ -242,8 +238,6 @@ def test_cont(t: CompressionTester) {
242238
}
243239
}
244240

245-
// === Part 4: Frame metadata preservation ===
246-
247241
def test_frame_metadata(t: CompressionTester) {
248242
var func = t.getDummyFunc();
249243
var frame = RelocatableFrame(func, 42, DUMMY_REENTRY, DUMMY_REENTRY, [Value.I32(99)]);
@@ -254,8 +248,6 @@ def test_frame_metadata(t: CompressionTester) {
254248
t.assert_vals_eq([Value.I32(99)], got[0].vals);
255249
}
256250

257-
// === Part 5: Repeated values ===
258-
259251
// Many identical values within a single frame.
260252
def test_repeated_vals(t: CompressionTester) {
261253
var vals = Array<Value>.new(100);
@@ -281,8 +273,6 @@ def test_repeated_across_frames(t: CompressionTester) {
281273
t.assert_frames_eq(frames, got);
282274
}
283275

284-
// === Part 6: Many diverse values ===
285-
286276
// Single frame with many values of varying types and magnitudes.
287277
def test_many_diverse_vals(t: CompressionTester) {
288278
var vals = Vector<Value>.new();
@@ -317,8 +307,6 @@ def test_many_frames_diverse(t: CompressionTester) {
317307
t.assert_frames_eq(input, got);
318308
}
319309

320-
// === Part 7: Complex reference objects ===
321-
322310
// Multiple distinct reference types in a single frame.
323311
def test_refs_mixed(t: CompressionTester) {
324312
var heap_obj = HeapObject.new(null, []);
@@ -393,3 +381,71 @@ def test_large_uniform_frames(t: CompressionTester) {
393381
var got = t.roundtrip(frames);
394382
t.assert_frames_eq(frames, got);
395383
}
384+
385+
// ===== Stack capture tests =====
386+
387+
// Host function callback. Set {stack} before resuming; {invoke} serves as the imported host function body.
388+
class FrameCapturer {
389+
var stack: WasmStack;
390+
391+
def invoke(args: Range<Value>) -> HostResult {
392+
return HostResult.Value0;
393+
}
394+
}
395+
396+
// Tester that builds Wasm functions and captures stack frames during execution.
397+
class StackCaptureTester extends ExeTester {
398+
var capturer = FrameCapturer.new();
399+
var import_index: int;
400+
401+
new(t: Tester, tiering: ExecutionStrategy) super(t, tiering) { }
402+
403+
// Adds an imported v_v host function to the module and sets up the import binding.
404+
// Must be called before codev/makeFunc.
405+
def addHostImport() {
406+
var import_sig = addSig(SigCache.v_v);
407+
var import_decl = FuncDecl.new(import_sig.heaptype_index);
408+
module.addImport("test", "capture", import_decl);
409+
import_index = import_decl.func_index;
410+
imports = [HostFunction.new("capture", import_sig, capturer.invoke)];
411+
}
412+
413+
// Builds a validated function that pushes all params onto the value stack and calls the imported v_v host function,
414+
// This ensures the value stack holds exactly the bound param values during the host call.
415+
def buildCaptureFunc(param_types: Array<ValueType>) -> Function {
416+
addHostImport();
417+
var main_sig = SigDecl.new(true, ValueTypes.NO_HEAPTYPES, param_types, SigCache.arr_v);
418+
sig(main_sig);
419+
var bc = BinBuilder.new();
420+
for (i < param_types.length) {
421+
bc.put(u8.!(Opcode.LOCAL_GET.code));
422+
bc.put_u32leb(u32.!(i));
423+
}
424+
bc.put(u8.!(Opcode.CALL.code));
425+
bc.put_u32leb(u32.!(import_index));
426+
for (i < param_types.length) {
427+
bc.put(u8.!(Opcode.DROP.code));
428+
}
429+
codev(bc.extract());
430+
return makeFunc();
431+
}
432+
}
433+
434+
// TODO: add more tests, after stack capture utils are done
435+
def S = StackCaptureTester.new;
436+
def unused_stack_ = TestTiers.addTests2([
437+
("stack_capture_smoke", S, test_stack_capture_smoke)
438+
]);
439+
440+
def test_stack_capture_smoke(t: StackCaptureTester) {
441+
var f = t.buildCaptureFunc([ValueType.I32, ValueType.I64]);
442+
if (f == null) return;
443+
444+
var s = Target.newWasmStack();
445+
s.reset(f);
446+
s.bind([Value.I32(42), Value.I64(99)]);
447+
var r = s.resume();
448+
449+
// The function should complete normally with no return values.
450+
t.assert_req(r, Result.Value(Values.NONE));
451+
}

0 commit comments

Comments
 (0)