Skip to content

Commit 36d9b96

Browse files
LPTKAnsonYeung
andauthored
Improve codegen slightly (#362)
Co-authored-by: Anson Yeung <[email protected]>
1 parent 4e8e246 commit 36d9b96

File tree

14 files changed

+493
-300
lines changed

14 files changed

+493
-300
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ sealed abstract class Block extends Product:
2929
case _: End => true
3030
case _ => false
3131

32+
lazy val isAbortive: Bool = this match
33+
case _: End => false
34+
case _: Throw | _: Break | _: Continue => true
35+
case ret: Return => !ret.implct
36+
case Begin(sub, rst) => sub.isAbortive || rst.isAbortive
37+
case Assign(_, _, rst) => rst.isAbortive
38+
case AssignField(_, _, _, rst) => rst.isAbortive
39+
case AssignDynField(_, _, _, _, rst) => rst.isAbortive
40+
case Match(_, arms, dflt, rst) => rst.isAbortive
41+
case Define(_, rst) => rst.isAbortive
42+
case TryBlock(sub, fin, rst) => rst.isAbortive || sub.isAbortive || fin.isAbortive
43+
case Label(_, _, bod, rst) => rst.isAbortive
44+
case HandleBlock(_, _, _, _, _, handlers, body, rst) => rst.isAbortive
3245

3346
lazy val definedVars: Set[Local] = this match
3447
case _: Return | _: Throw => Set.empty
@@ -287,6 +300,23 @@ case class AssignDynField(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, res
287300
case class Define(defn: Defn, rest: Block) extends Block with ProductWithTail
288301

289302

303+
object Match:
304+
def apply(scrut: Path, arms: Ls[Case -> Block], dflt: Opt[Block], rest: Block): Block = dflt match
305+
case S(Match(`scrut`, arms2, dflt2, _: End)) => // TODO: also handle non-End rest (may require a join point)
306+
// * Currently, this branch does not seem used, because the UCS already does a good job at merging matches
307+
Match(scrut, arms ::: arms2, dflt2, rest)
308+
case _ =>
309+
if !rest.isEmpty && arms.forall(_._2.isAbortive) && dflt.exists(_.isAbortive)
310+
then new Match(scrut, arms, dflt, End("unreachable"))
311+
else new Match(scrut, arms, dflt, rest)
312+
313+
object Begin:
314+
def apply(sub: Block, rest: Block): Block =
315+
if sub.isEmpty then rest
316+
else if sub.isAbortive then sub
317+
else new Begin(sub, rest)
318+
319+
290320
case class HandleBlock(
291321
lhs: Local,
292322
res: Local,

hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,17 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
447447
case S(el) => returningTerm(el, endSemi = true)
448448
case N => doc""
449449
e :: returningTerm(rest, endSemi)
450+
case Match(scrut, arms, els, rest)
451+
if arms.sizeCompare(1) > 0 && arms.forall(_._1.isInstanceOf[Case.Lit]) =>
452+
val l = arms.foldLeft(doc""): (acc, arm) =>
453+
acc :: doc" # case ${arm._1.asInstanceOf[Case.Lit].lit.idStr}: #{ ${
454+
returningTerm(arm._2, endSemi = true)
455+
} # break; #} "
456+
val e = els match
457+
case S(el) =>
458+
doc" # default: #{ ${ returningTerm(el, endSemi = true) } # break; #} "
459+
case N => doc""
460+
doc" # switch (${result(scrut)}) { #{ ${l :: e} #} # }" :: returningTerm(rest, endSemi)
450461
case Match(scrut, hd :: tl, els, rest) =>
451462
val sd = result(scrut)
452463
def cond(cse: Case) = cse match

hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,16 @@ fun nott = case
176176
//│ nott = function nott() {
177177
//│ let lambda1;
178178
//│ lambda1 = (undefined, function (caseScrut) {
179-
//│ if (caseScrut === true) {
180-
//│ return false
181-
//│ } else if (caseScrut === false) {
182-
//│ return true
183-
//│ } else {
184-
//│ throw globalThis.Object.freeze(new globalThis.Error("match error"))
179+
//│ switch (caseScrut) {
180+
//│ case true:
181+
//│ return false;
182+
//│ break;
183+
//│ case false:
184+
//│ return true;
185+
//│ break;
186+
//│ default:
187+
//│ throw globalThis.Object.freeze(new globalThis.Error("match error"));
188+
//│ break;
185189
//│ }
186190
//│ });
187191
//│ return lambda1

hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ if true then 1 else 0
99
:sjs
1010
let f = x => if x then print("ok") else print("ko")
1111
//│ JS (unsanitized):
12-
//│ let f, lambda;
13-
//│ lambda = (undefined, function (x) {
12+
//│ let f, f1;
13+
//│ f1 = function f(x) {
1414
//│ if (x === true) { return Predef.print("ok") } else { return Predef.print("ko") }
15-
//│ });
16-
//│ f = lambda;
17-
//│ f = fun
15+
//│ };
16+
//│ f = f1;
17+
//│ f = fun f
1818

1919
f(true)
2020
//│ > ok
@@ -26,31 +26,31 @@ f(false)
2626
:sjs
2727
let f = x => print((if x then "ok" else "ko") + "!")
2828
//│ JS (unsanitized):
29-
//│ let f1, lambda1;
30-
//│ lambda1 = (undefined, function (x) {
29+
//│ let f2, lambda;
30+
//│ lambda = (undefined, function (x) {
3131
//│ let tmp, tmp1;
3232
//│ if (x === true) {
3333
//│ tmp = "ok";
3434
//│ } else { tmp = "ko"; }
3535
//│ tmp1 = tmp + "!";
3636
//│ return Predef.print(tmp1)
3737
//│ });
38-
//│ f1 = lambda1;
38+
//│ f2 = lambda;
3939
//│ f = fun
4040

4141
:sjs
4242
let f = x => print((if x and x then "ok" else "ko") + "!")
4343
//│ JS (unsanitized):
44-
//│ let f2, lambda2;
45-
//│ lambda2 = (undefined, function (x) {
44+
//│ let f3, lambda1;
45+
//│ lambda1 = (undefined, function (x) {
4646
//│ let tmp, tmp1;
4747
//│ if (x === true) {
4848
//│ tmp = "ok";
4949
//│ } else { tmp = "ko"; }
5050
//│ tmp1 = tmp + "!";
5151
//│ return Predef.print(tmp1)
5252
//│ });
53-
//│ f2 = lambda2;
53+
//│ f3 = lambda1;
5454
//│ f = fun
5555
// --- TODO: What we want ---
5656
// this.f = (x) => {

hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,17 +225,17 @@ _ - _ of 1, 2
225225
:sjs
226226
1 \ (_ - 2)
227227
//│ JS (unsanitized):
228-
//│ let lambda38; lambda38 = (undefined, function (_0) { return _0 - 2 }); Predef.passTo(1, lambda38)
228+
//│ let lambda37; lambda37 = (undefined, function (_0) { return _0 - 2 }); Predef.passTo(1, lambda37)
229229
//│ = fun
230230

231231
:sjs
232232
1 \ (_ - 2)()
233233
//│ JS (unsanitized):
234-
//│ let lambda39, tmp19;
235-
//│ lambda39 = (undefined, function (_0) {
234+
//│ let lambda38, tmp19;
235+
//│ lambda38 = (undefined, function (_0) {
236236
//│ return _0 - 2
237237
//│ });
238-
//│ tmp19 = Predef.passTo(1, lambda39);
238+
//│ tmp19 = Predef.passTo(1, lambda38);
239239
//│ runtime.safeCall(tmp19())
240240
//│ = -1
241241

hkmc2/shared/src/test/mlscript/codegen/Throw.mls

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,19 @@ f(false)
4747
//│ };
4848
//│ f3(false)
4949
//│ ═══[RUNTIME ERROR] Error: y
50+
51+
52+
:re
53+
:sjs
54+
if false then throw 0
55+
throw 1
56+
//│ JS (unsanitized):
57+
//│ let scrut;
58+
//│ scrut = false;
59+
//│ if (scrut === true) {
60+
//│ throw 0
61+
//│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) }
62+
//│ /* unreachable */
63+
//│ ═══[RUNTIME ERROR] Error: match error
64+
65+

hkmc2/shared/src/test/mlscript/codegen/While.mls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
//│ } else {
1818
//│ throw globalThis.Object.freeze(new globalThis.Error("match error"))
1919
//│ }
20-
//│ return runtime.LoopEnd
20+
//│ /* unreachable */
2121
//│ });
2222
//│ tmp1 = while1();
2323
//│ tmp2 = tmp1 !== runtime.LoopEnd;

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

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -53,44 +53,53 @@ fun f() =
5353
//│ this.pc = pc;
5454
//│ }
5555
//│ resume(value$) {
56-
//│ if (this.pc === 1) {
57-
//│ scrut = value$;
58-
//│ } else if (this.pc === 2) {
59-
//│ tmp = value$;
60-
//│ } else if (this.pc === 3) {
61-
//│ tmp1 = value$;
56+
//│ switch (this.pc) {
57+
//│ case 1:
58+
//│ scrut = value$;
59+
//│ break;
60+
//│ case 2:
61+
//│ tmp = value$;
62+
//│ break;
63+
//│ case 3:
64+
//│ tmp1 = value$;
65+
//│ break;
6266
//│ }
6367
//│ contLoop: while (true) {
64-
//│ if (this.pc === 4) {
65-
//│ return j / i
66-
//│ } else if (this.pc === 1) {
67-
//│ if (scrut === true) {
68-
//│ tmp = Predef.raiseUnhandledEffect();
69-
//│ if (tmp instanceof runtime.EffectSig.class) {
70-
//│ return this.doUnwind(tmp, 2)
68+
//│ switch (this.pc) {
69+
//│ case 4:
70+
//│ return j / i;
71+
//│ break;
72+
//│ case 1:
73+
//│ if (scrut === true) {
74+
//│ tmp = Predef.raiseUnhandledEffect();
75+
//│ if (tmp instanceof runtime.EffectSig.class) {
76+
//│ return this.doUnwind(tmp, 2)
77+
//│ }
78+
//│ this.pc = 2;
79+
//│ continue contLoop
80+
//│ } else {
81+
//│ tmp1 = runtime.Unit;
82+
//│ this.pc = 4;
83+
//│ continue contLoop
7184
//│ }
72-
//│ this.pc = 2;
73-
//│ continue contLoop
74-
//│ } else {
75-
//│ tmp1 = runtime.Unit;
85+
//│ /* unreachable */
86+
//│ break;
87+
//│ case 5:
88+
//│ tmp1 = Predef.print(tmp);
89+
//│ if (tmp1 instanceof runtime.EffectSig.class) {
90+
//│ return this.doUnwind(tmp1, 3)
91+
//│ }
92+
//│ this.pc = 3;
93+
//│ continue contLoop;
94+
//│ break;
95+
//│ case 2:
96+
//│ this.pc = 5;
97+
//│ continue contLoop;
98+
//│ break;
99+
//│ case 3:
76100
//│ this.pc = 4;
77-
//│ continue contLoop
78-
//│ }
79-
//│ this.pc = 4;
80-
//│ continue contLoop
81-
//│ } else if (this.pc === 5) {
82-
//│ tmp1 = Predef.print(tmp);
83-
//│ if (tmp1 instanceof runtime.EffectSig.class) {
84-
//│ return this.doUnwind(tmp1, 3)
85-
//│ }
86-
//│ this.pc = 3;
87-
//│ continue contLoop
88-
//│ } else if (this.pc === 2) {
89-
//│ this.pc = 5;
90-
//│ continue contLoop
91-
//│ } else if (this.pc === 3) {
92-
//│ this.pc = 4;
93-
//│ continue contLoop
101+
//│ continue contLoop;
102+
//│ break;
94103
//│ }
95104
//│ break;
96105
//│ }
@@ -99,12 +108,16 @@ fun f() =
99108
//│ return getLocals3();
100109
//│ }
101110
//│ get getLoc() {
102-
//│ if (this.pc === 1) {
103-
//│ return "Debugging.mls:16:6"
104-
//│ } else if (this.pc === 2) {
105-
//│ return "Debugging.mls:17:14"
106-
//│ } else if (this.pc === 3) {
107-
//│ return "Debugging.mls:17:5"
111+
//│ switch (this.pc) {
112+
//│ case 1:
113+
//│ return "Debugging.mls:16:6";
114+
//│ break;
115+
//│ case 2:
116+
//│ return "Debugging.mls:17:14";
117+
//│ break;
118+
//│ case 3:
119+
//│ return "Debugging.mls:17:5";
120+
//│ break;
108121
//│ }
109122
//│ }
110123
//│ toString() { return runtime.render(this); }
@@ -148,8 +161,8 @@ lambda_test(() =>
148161
raiseUnhandledEffect()
149162
100)
150163
//│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect
151-
//│ at lambda (Debugging.mls:148:3)
152-
//│ at lambda_test (Debugging.mls:145:3)
164+
//│ at lambda (Debugging.mls:161:3)
165+
//│ at lambda_test (Debugging.mls:158:3)
153166

154167

155168
import "../../mlscript-compile/Runtime.mls"
@@ -228,9 +241,9 @@ fun f() =
228241
f()
229242
//│ > [FnLocalsInfo("‹top level›", [LocalVarInfo("i", 100)]), FnLocalsInfo("f", [LocalVarInfo("j", 200)])]
230243
//│ > Stack Trace:
231-
//│ > at f (Debugging.mls:221:3) with locals: j=200
244+
//│ > at f (Debugging.mls:234:3) with locals: j=200
232245
//│ > Stack Trace:
233-
//│ > at f (Debugging.mls:223:3)
246+
//│ > at f (Debugging.mls:236:3)
234247
//│ > Stack Trace:
235-
//│ > at f (Debugging.mls:224:3) with locals: j=300
248+
//│ > at f (Debugging.mls:237:3) with locals: j=300
236249
//│ > [FnLocalsInfo("‹top level›", [LocalVarInfo("i", 100)]), FnLocalsInfo("f", [LocalVarInfo("j", 300)])]

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

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,35 @@ data class Lol(h) with
3131
//│ this.pc = pc;
3232
//│ }
3333
//│ resume(value$) {
34-
//│ if (this.pc === 1) {
35-
//│ tmp = value$;
36-
//│ } else if (this.pc === 2) {
37-
//│ res = value$;
34+
//│ switch (this.pc) {
35+
//│ case 1:
36+
//│ tmp = value$;
37+
//│ break;
38+
//│ case 2:
39+
//│ res = value$;
40+
//│ break;
3841
//│ }
3942
//│ contLoop: while (true) {
40-
//│ if (this.pc === 3) {
41-
//│ return this$Lol
42-
//│ } else if (this.pc === 4) {
43-
//│ res = Predef.print(tmp);
44-
//│ if (res instanceof runtime.EffectSig.class) {
45-
//│ return this.doUnwind(res, 2)
46-
//│ }
47-
//│ this.pc = 2;
48-
//│ continue contLoop
49-
//│ } else if (this.pc === 1) {
50-
//│ this.pc = 4;
51-
//│ continue contLoop
52-
//│ } else if (this.pc === 2) {
53-
//│ this.pc = 3;
54-
//│ continue contLoop
43+
//│ switch (this.pc) {
44+
//│ case 3:
45+
//│ return this$Lol;
46+
//│ break;
47+
//│ case 4:
48+
//│ res = Predef.print(tmp);
49+
//│ if (res instanceof runtime.EffectSig.class) {
50+
//│ return this.doUnwind(res, 2)
51+
//│ }
52+
//│ this.pc = 2;
53+
//│ continue contLoop;
54+
//│ break;
55+
//│ case 1:
56+
//│ this.pc = 4;
57+
//│ continue contLoop;
58+
//│ break;
59+
//│ case 2:
60+
//│ this.pc = 3;
61+
//│ continue contLoop;
62+
//│ break;
5563
//│ }
5664
//│ break;
5765
//│ }

0 commit comments

Comments
 (0)