Skip to content

Commit 0bc05a7

Browse files
CAG2MarkLPTK
authored andcommitted
Fix stack safety
1 parent e262ba9 commit 0bc05a7

File tree

3 files changed

+43
-28
lines changed

3 files changed

+43
-28
lines changed

hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,19 @@ class StackSafeTransform(depthLimit: Int)(using State):
3535
.ret(res)
3636
else
3737
val tmp = TempSymbol(None, "tmp")
38+
val offsetGtDepth = TempSymbol(None, "offsetGtDepth")
3839
val prevDepth = TempSymbol(None, "prevDepth")
3940
blockBuilder
4041
.assign(prevDepth, stackDepthPath)
4142
.assignFieldN(predefPath, STACK_DEPTH_IDENT, op("+", stackDepthPath, intLit(1)))
4243
.assign(tmp, res)
4344
.assignFieldN(predefPath, STACK_DEPTH_IDENT, prevDepth.asPath)
45+
.assign(offsetGtDepth, op("<", prevDepth.asPath, stackOffsetPath))
46+
.ifthen(
47+
offsetGtDepth.asPath,
48+
Case.Lit(Tree.BoolLit(true)),
49+
blockBuilder.assignFieldN(predefPath, STACK_OFFSET_IDENT, prevDepth.asPath).end
50+
)
4451
.rest(f(tmp.asPath))
4552

4653
def extractResTopLevel(res: Result, isTailCall: Bool, f: Result => Block) =
@@ -66,14 +73,14 @@ class StackSafeTransform(depthLimit: Int)(using State):
6673
let curOffset = stackOffset
6774
stackOffset = stackDepth
6875
let ret = resume()
69-
stackOffset = curOffset
76+
// stackOffset = curOffset // this line is wrong
7077
ret
7178
*/
7279
blockBuilder
7380
.assign(curOffsetSym, stackOffsetPath)
7481
.assignFieldN(predefPath, STACK_OFFSET_IDENT, stackDepthPath)
7582
.assign(handlerRes, Call(Value.Ref(resumeSym), Nil)(true))
76-
.assignFieldN(predefPath, STACK_OFFSET_IDENT, curOffsetSym.asPath)
83+
// .assignFieldN(predefPath, STACK_OFFSET_IDENT, curOffsetSym.asPath) // this line is wrong
7784
.ret(handlerRes.asPath)
7885
) :: Nil,
7986
blockBuilder

hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ hi(0)
104104
//│ contLoop: while (true) {
105105
//│ if (this.pc === 4) {
106106
//│ res2 = res3;
107-
//│ globalThis.Predef.__stackOffset = curOffset;
108107
//│ return res2;
109108
//│ }
110109
//│ break;
@@ -121,7 +120,6 @@ hi(0)
121120
//│ return res3;
122121
//│ }
123122
//│ res2 = res3;
124-
//│ globalThis.Predef.__stackOffset = curOffset;
125123
//│ return res2;
126124
//│ });
127125
//│ }
@@ -179,7 +177,7 @@ sum(10000)
179177
//│ JS (unsanitized):
180178
//│ let sum3, res1, handleBlock$3;
181179
//│ sum3 = function sum(n) {
182-
//│ let scrut, tmp, tmp1, tmp2, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$;
180+
//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$;
183181
//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); };
184182
//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class {
185183
//│ constructor(pc) {
@@ -194,7 +192,7 @@ sum(10000)
194192
//│ res2 = value$;
195193
//│ }
196194
//│ contLoop: while (true) {
197-
//│ if (this.pc === 3) {
195+
//│ if (this.pc === 4) {
198196
//│ scrut = n == 0;
199197
//│ if (scrut === true) {
200198
//│ return 0;
@@ -217,11 +215,20 @@ sum(10000)
217215
//│ } else if (this.pc === 1) {
218216
//│ tmp2 = res3;
219217
//│ globalThis.Predef.__stackDepth = prevDepth;
218+
//│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset;
219+
//│ if (offsetGtDepth === true) {
220+
//│ globalThis.Predef.__stackOffset = prevDepth;
221+
//│ this.pc = 3;
222+
//│ continue contLoop;
223+
//│ }
224+
//│ this.pc = 3;
225+
//│ continue contLoop;
226+
//│ } else if (this.pc === 3) {
220227
//│ tmp1 = tmp2;
221228
//│ return n + tmp1;
222229
//│ } else if (this.pc === 0) {
223230
//│ dummy = res2;
224-
//│ this.pc = 3;
231+
//│ this.pc = 4;
225232
//│ continue contLoop;
226233
//│ }
227234
//│ break;
@@ -257,6 +264,10 @@ sum(10000)
257264
//│ }
258265
//│ tmp2 = res3;
259266
//│ globalThis.Predef.__stackDepth = prevDepth;
267+
//│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset;
268+
//│ if (offsetGtDepth === true) {
269+
//│ globalThis.Predef.__stackOffset = prevDepth;
270+
//│ }
260271
//│ tmp1 = tmp2;
261272
//│ return n + tmp1;
262273
//│ }
@@ -279,13 +290,12 @@ sum(10000)
279290
//│ this.pc = pc;
280291
//│ }
281292
//│ resume(value$) {
282-
//│ if (this.pc === 5) {
293+
//│ if (this.pc === 6) {
283294
//│ res4 = value$;
284295
//│ }
285296
//│ contLoop: while (true) {
286-
//│ if (this.pc === 5) {
297+
//│ if (this.pc === 6) {
287298
//│ res3 = res4;
288-
//│ globalThis.Predef.__stackOffset = curOffset;
289299
//│ return res3;
290300
//│ }
291301
//│ break;
@@ -297,12 +307,11 @@ sum(10000)
297307
//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth;
298308
//│ res4 = resume();
299309
//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) {
300-
//│ res4.tail.next = new Cont$1.class(5);
310+
//│ res4.tail.next = new Cont$1.class(6);
301311
//│ res4.tail = res4.tail.next;
302312
//│ return res4;
303313
//│ }
304314
//│ res3 = res4;
305-
//│ globalThis.Predef.__stackOffset = curOffset;
306315
//│ return res3;
307316
//│ });
308317
//│ }
@@ -317,11 +326,11 @@ sum(10000)
317326
//│ this.pc = pc;
318327
//│ }
319328
//│ resume(value$) {
320-
//│ if (this.pc === 4) {
329+
//│ if (this.pc === 5) {
321330
//│ res2 = value$;
322331
//│ }
323332
//│ contLoop: while (true) {
324-
//│ if (this.pc === 4) {
333+
//│ if (this.pc === 5) {
325334
//│ return res2;
326335
//│ }
327336
//│ break;
@@ -334,7 +343,7 @@ sum(10000)
334343
//│ globalThis.Predef.__stackHandler = stackHandler;
335344
//│ res2 = sum3(10000);
336345
//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) {
337-
//│ res2.tail.next = new Cont$(4);
346+
//│ res2.tail.next = new Cont$(5);
338347
//│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler);
339348
//│ }
340349
//│ return res2;
@@ -475,7 +484,6 @@ hi(0)
475484
//│ ═══[RUNTIME ERROR] Error: This code requires effect handler instrumentation but was compiled without it.
476485

477486

478-
:fixme
479487
:stackSafe 1000
480488
:handler
481489
:expect 100010000
@@ -485,4 +493,4 @@ fun sum(n) =
485493
n + sum(n - 1)
486494
fun bad() = sum(10000) + sum(10000)
487495
bad()
488-
//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded
496+
//│ = 100010000

hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fact(10)
2323
fact(10000)
2424
//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded
2525

26-
26+
:stackSafe 1000
2727
fun mkrec(g) =
2828
selfApp of self =>
2929
g of y => selfApp(self)(y)
@@ -91,16 +91,16 @@ let fact =
9191
:expect 3628800
9292
:stackSafe 5
9393
fact(10)
94-
//│ > 2 0
95-
//│ > 4 0
96-
//│ > 6 5
97-
//│ > 8 5
98-
//│ > 10 10
99-
//│ > 12 10
100-
//│ > 14 10
101-
//│ > 16 15
102-
//│ > 18 15
103-
//│ > 20 20
94+
//│ > 2 993
95+
//│ > 4 1
96+
//│ > 6 4
97+
//│ > 8 4
98+
//│ > 10 8
99+
//│ > 12 8
100+
//│ > 14 12
101+
//│ > 16 12
102+
//│ > 18 16
103+
//│ > 20 16
104104
//│ > resumed: 1
105105
//│ > resumed: 2
106106
//│ > resumed: 3

0 commit comments

Comments
 (0)