Skip to content

Commit b0ef1fb

Browse files
committed
[stack-switching] X86_64Stack compression
[stack-switching] Correct suspension logic after compression [stack-switching] Compression bug fixes [stack-switching] Temp [stack-switching] Comment regarding tag coercion [stack-switching] Fixed REF <=> REF_U64 tag coercion error [stack-switching] Removed debug messages [stack-switching] Lots of debugging [stack-switching] memory mapping bug fixed [stack-switching] Removed debug [stack-switching] temp: debug [stack-switching] Parameter overlap test case [stack-switching] Code jump error in fast-int
1 parent 29c4d16 commit b0ef1fb

16 files changed

Lines changed: 401 additions & 12 deletions

src/engine/Debug.v3

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ component Debug {
1313
def stack = false;
1414
def memory = false;
1515
def diagnostic = false;
16+
def compression = false;
1617

1718
// Prevents arguments from being dead-code-eliminated.
1819
def keepAlive<T>(x: T) { }

src/engine/Sidetable.v3

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ component Sidetables {
8282
BR => size = Sidetable_BrEntry.size;
8383
BR_IF => size = Sidetable_BrEntry.size;
8484
BR_TABLE => { var count = immptr.skip_labels(); size = (1 + count) * Sidetable_BrEntry.size; }
85-
TRY_TABLE => { var count = immptr.skip_catches(); size = count * Sidetable_CatchEntry.size; }
85+
TRY_TABLE => {
86+
immptr.read_BlockTypeCode();
87+
var count = immptr.skip_catches();
88+
size = count * Sidetable_CatchEntry.size;
89+
}
8690
BR_ON_NULL => size = Sidetable_BrEntry.size;
8791
BR_ON_NON_NULL => size = Sidetable_BrEntry.size;
8892
BR_ON_CAST => size = Sidetable_BrEntry.size;

src/engine/Value.v3

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ component Values {
7171
var id = if(x.decl == null, -1, x.decl.heaptype_index);
7272
buf.put1("<ref array #%d>", id);
7373
}
74-
x: Object => x.render(buf);
74+
x: Object => { x.render(buf); }
7575
null => buf.puts("<ref null>");
7676
}
7777
I31(val) => buf.put1("i31:%d", u32.view(val));

src/engine/WasmStack.v3

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

44
// An execution stack.
5-
class ExecStack {
5+
class ExecStack extends Object {
66
def popV(t: ValueType) -> Value;
77
def popi() -> i32;
88
def popu() -> u32;
@@ -51,10 +51,14 @@ class ExecStack {
5151
}
5252
}
5353

54+
// Represents a suspendable stack that can be used to instantiate a continuation.
55+
class VersionedStack extends ExecStack {
56+
var version: u64;
57+
}
58+
5459
// Represents a stack on which Wasm code can be executed.
55-
class WasmStack extends ExecStack {
60+
class WasmStack extends VersionedStack {
5661
var parent: WasmStack;
57-
var version: u64;
5862

5963
// ext:stack-switching
6064
// Denotes the bottom stack of a suspended continuation (with {this} as the top stack).

src/engine/continuation/BoxedContinuation.v3

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,7 @@ component Continuations {
2929

3030
def getStoredStack(cont: Continuation) -> WasmStack { return cont.stack; }
3131
def getStoredVersion(cont: Continuation) -> u64 { return 0; } // boxed cont does not store version
32+
33+
def objectIsContinuation(obj: Object) -> bool { return Continuation.?(obj); }
34+
def objectToContinuation(obj: Object) -> Continuation { return Continuation.!(obj); }
3235
}

src/engine/continuation/UnboxedContinuation.v3

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ component Continuations {
2525

2626
def getStoredStack(cont: Continuation) -> WasmStack { return cont.stack; }
2727
def getStoredVersion(cont: Continuation) -> u64 { return cont.version; }
28+
29+
def objectIsContinuation(obj: Object) -> bool { return false; }
30+
def objectToContinuation(obj: Object) -> Continuation;
2831
}

src/engine/x86-64/Mmap.v3

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,8 @@ class MemoryRange {
4949
def range(offset: int, length: int) -> Range<byte> {
5050
return CiRuntime.forgeRange(this.start + offset, length);
5151
}
52+
53+
def render(buf: StringBuilder) -> StringBuilder {
54+
return buf.put2("MemRange[0x%x, 0x%x)", start - Pointer.NULL, end - Pointer.NULL);
55+
}
5256
}

src/engine/x86-64/X86_64Interpreter.v3

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4230,6 +4230,7 @@ class X86_64InterpreterGen(ic: X86_64InterpreterCode, w: DataWriter) {
42304230
}
42314231
// generates cleanup code for after a child stack returns
42324232
private def genOnResumeFinish(skip_tag: bool) {
4233+
restoreCurPcFromFrame();
42334234
restoreCallerIVars();
42344235
restoreDispatchTableReg();
42354236
var r_stack = r_tmp1;

src/engine/x86-64/X86_64Runtime.v3

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,18 @@ component X86_64Runtime {
135135
// is found). Then, the tag parameters and the continuation is pushed onto
136136
// the handler stack.
137137
def runtime_handle_suspend(stack: X86_64Stack, instance: Instance, tag_id: u31) -> Throwable {
138+
// TODO[sc]: remove this check
139+
Trace.OUT.put2(
140+
"suspending on stack=0x%x, rsp=0x%x",
141+
Pointer.atObject(stack) - Pointer.NULL,
142+
stack.rsp - Pointer.NULL
143+
).ln();
144+
var old_parent = stack.parent;
145+
var compressed = X86_64Compression.compress(stack);
146+
X86_64Compression.decompress(stack, compressed);
147+
stack.parent = old_parent;
148+
if (old_parent != null) stack.parent_rsp_ptr.store<Pointer>(X86_64Stack.!(old_parent).rsp);
149+
138150
var tag = instance.tags[tag_id];
139151
var vals = stack.popN(tag.sig.params);
140152
var cont = Runtime.unwindStackChain(stack, instance, tag_id, WasmStack.tryHandleSuspension);

src/engine/x86-64/X86_64Stack.v3

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class X86_64Stack extends WasmStack {
3333
}
3434
clear();
3535
if (valuerep.tagged) RiGc.registerScanner(this, X86_64Stack.scan);
36-
if (Trace.stack && Debug.stack) Trace.OUT.put2("newStack start=0x%x, end=0x%x", mapping.range.start - Pointer.NULL, mapping.range.end - Pointer.NULL).ln();
36+
if (Debug.stack) Trace.OUT.put2("newStack start=0x%x, end=0x%x", mapping.range.start - Pointer.NULL, mapping.range.end - Pointer.NULL).ln();
3737
}
3838
// Gets the state of this stack.
3939
def state() -> StackState {
@@ -120,7 +120,7 @@ class X86_64Stack extends WasmStack {
120120
// stop the stackwalk. Otherwise stop at the return-parent stub. In any case, return the last
121121
// valid stack pointer, and a boolean indicating if the walk stopped early (due to {f} returning {false}).
122122
// (XXX: takes a function {f} with an additional parameter, and the parameter, to avoid a closure).
123-
private def walk<P>(f: (Pointer, RiUserCode, StackFramePos, P) -> bool, param: P, start_sp: Pointer, continue_to_parent: bool) -> (bool, StackFramePos) {
123+
def walk<P>(f: (Pointer, RiUserCode, StackFramePos, P) -> bool, param: P, start_sp: Pointer, continue_to_parent: bool) -> (bool, StackFramePos) {
124124
var stack = this;
125125
var sp = start_sp;
126126
if (Trace.stack && Debug.stack) {
@@ -185,7 +185,7 @@ class X86_64Stack extends WasmStack {
185185
}
186186
// Increment by {Pointer.SIZE} to skip the return address pushed by {resume}.
187187
var frame = TargetFrame(rsp + Pointer.SIZE);
188-
var accessor = frame.getFrameAccessor();
188+
var accessor = frame.getFrameAccessorOfStack(this);
189189
var func = accessor.func();
190190
var handler = func.decl.findSuspensionHandler(func.instance, tag, accessor.pc());
191191
if (handler.handler_pc < 0) { // not found
@@ -469,9 +469,15 @@ class X86_64Stack extends WasmStack {
469469
code.scanFrame(ip, pos.frame.sp);
470470
return true;
471471
}
472+
// XXX: If you ever get a nonsensical stacktrace (i.e., non-contiguous, untraceble
473+
// call stack) where a virtual call on a value returned by {X86_64Stack.readValue}
474+
// shows up somewhere in it, it's almost certainly the REF <=> REF_U64 tag coercion.
472475
def readValue(base: Pointer, offset: int) -> Value {
476+
// TODO: correctly tag the REF_U64 storage kind (requires typed REF_NULL loads).
473477
if (!valuerep.tagged) fatal("untyped frame access requires value tagging to be enabled");
474478
var tp = base + offset * valuerep.slot_size;
479+
480+
if (!mapping.range.contains(base)) System.error("FrameAccessError", "base out of bounds");
475481
if (!mapping.range.contains(tp)) System.error("FrameAccessError", "out of bounds");
476482
var vp = tp + valuerep.tag_size;
477483
var tag = tp.load<u8>() & '\x7F';
@@ -484,6 +490,7 @@ class X86_64Stack extends WasmStack {
484490
BpTypeCode.NULLEXTERNREF.code,
485491
BpTypeCode.I31REF.code => return readI31OrObject(vp);
486492

493+
// TODO: check if nullref needs REF_U64 coercion
487494
BpTypeCode.STRUCTREF.code,
488495
BpTypeCode.NULLREF.code,
489496
BpTypeCode.ARRAYREF.code,
@@ -572,6 +579,22 @@ class X86_64Stack extends WasmStack {
572579
if (bits == 0) return Values.REF_NULL;
573580
if ((bits & 1) == 1) return Value.I31(u31.view(bits >> 1));
574581
var obj = vp.load<Object>();
582+
// TODO[sc]: revisit this check
583+
if (FeatureDisable.unboxedConts) {
584+
if (Continuations.objectIsContinuation(obj)) {
585+
return Value.Cont(Continuations.objectToContinuation(obj));
586+
}
587+
} else {
588+
if (WasmStack.?(obj)) {
589+
var version = (vp + 8).load<u64>();
590+
return Value.Cont(Continuations.continuationWithVersion(WasmStack.!(obj), version));
591+
}
592+
}
593+
594+
if (ExecStack.?(obj)) {
595+
// {vp} points to an unboxed continuation.
596+
var x = 1 / 0;
597+
}
575598
return Value.Ref(obj);
576599
}
577600
def popResult(rt: Array<ValueType>) -> Result {
@@ -589,7 +612,7 @@ def G = X86_64MasmRegs.toGpr, X = X86_64MasmRegs.toXmmr;
589612
component X86_64Stacks {
590613
var RESUME_STUB_POINTER: Pointer;
591614

592-
def getFrameAccessor(sp: Pointer) -> X86_64FrameAccessor {
615+
def getFrameAccessorOfStack(sp: Pointer, stack: X86_64Stack) -> X86_64FrameAccessor {
593616
var retip = (sp + -RETADDR_SIZE).load<Pointer>();
594617
var code = RiRuntime.findUserCode(retip);
595618
match (code) {
@@ -598,7 +621,7 @@ component X86_64Stacks {
598621
if (prev != null) return prev;
599622
// Interpreter frames store the {WasmFunction} _and_ {FuncDecl}.
600623
var decl = (sp + X86_64InterpreterFrame.func_decl.offset).load<FuncDecl>();
601-
var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, sp, decl);
624+
var n = X86_64FrameAccessor.new(stack, sp, decl);
602625
(sp + X86_64InterpreterFrame.accessor.offset).store<X86_64FrameAccessor>(n);
603626
return n;
604627
}
@@ -608,13 +631,19 @@ component X86_64Stacks {
608631
// SPC frames only store the {WasmFunction}.
609632
var wf = (sp + X86_64InterpreterFrame.wasm_func.offset).load<WasmFunction>();
610633
// TODO: assert wf.decl == x.decl()
611-
var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, sp, wf.decl);
634+
var n = X86_64FrameAccessor.new(stack, sp, wf.decl);
612635
(sp + X86_64InterpreterFrame.accessor.offset).store<X86_64FrameAccessor>(n);
613636
return n;
614637
}
615638
}
616639
return null;
617640
}
641+
642+
// TODO[cleanup]: defaulting to {curStack} should be deprecated with stack-switching
643+
def getFrameAccessor(sp: Pointer) -> X86_64FrameAccessor {
644+
return getFrameAccessorOfStack(sp, X86_64Runtime.curStack);
645+
}
646+
618647
def traceIpAndSp(ip: Pointer, sp: Pointer, out: Range<byte> -> void) {
619648
var buf = X86_64Runtime.globalFrameDescriptionBuf;
620649
buf.put2("\t@[ip=0x%x, sp=0x%x] ", ip - Pointer.NULL, sp - Pointer.NULL).send(out);
@@ -1025,6 +1054,13 @@ class X86_64FrameAccessor(stack: X86_64Stack, sp: Pointer, decl: FuncDecl) exten
10251054
var vfp = (sp + X86_64InterpreterFrame.vfp.offset).load<Pointer>();
10261055
return stack.readValue(vfp, i);
10271056
}
1057+
// Get the value at {vfp + i}.
1058+
// XXX: refactor
1059+
def getValue(i: int) -> Value {
1060+
checkNotUnwound();
1061+
var vfp = (sp + X86_64InterpreterFrame.vfp.offset).load<Pointer>();
1062+
return stack.readValue(vfp, i);
1063+
}
10281064
// Get the value of frame variable {i}.
10291065
def getFrameVar(i: int) -> Value {
10301066
checkNotUnwound();
@@ -1116,7 +1152,7 @@ class X86_64FrameAccessor(stack: X86_64Stack, sp: Pointer, decl: FuncDecl) exten
11161152
var st_ptr = if(stp == st_entries.length, Pointer.atContents(st_entries), Pointer.atElement(func.sidetable.entries, stp));
11171153
(sp + X86_64InterpreterFrame.stp.offset) .store<Pointer>(st_ptr);
11181154
}
1119-
private def vfp() -> Pointer {
1155+
def vfp() -> Pointer {
11201156
return (sp + X86_64InterpreterFrame.vfp.offset).load<Pointer>();
11211157
}
11221158
private def set_vsp(p: Pointer) {

0 commit comments

Comments
 (0)