ReactiveFunction and codegen for optional calls
Implements HIR->ReactiveFunction conversion and Codegen for optional calls. We add a new OptionalCall variant of ReactiveValue, which is a SequenceExpression that describes the evaluation of the args and the call itself. This is then straightforward to codgen.
Joe Savona committed
Mar 24, 2023 at 14:22 UTC
77bb9ff765af43a59c96f5f558c3a5df46445c48
11 files changed
+104
-21
compiler/forget/src/HIR/BuildHIR.ts
+1
-1
@@ -941,7 +941,7 @@ function lowerExpression(
941
}
942
case "OptionalCallExpression": {
943
const expr = exprPath as NodePath<t.OptionalCallExpression>;
944
- const optional = expr.get("optional");
944
+ const optional = expr.node.optional;
945
const calleePath = expr.get("callee");
946
if (
947
calleePath.isMemberExpression() ||
compiler/forget/src/HIR/HIR.ts
+10
-1
@@ -90,7 +90,8 @@ export type ReactiveValue =
90
| InstructionValue
91
| ReactiveLogicalValue
92
| ReactiveSequenceValue
93
- | ReactiveTernaryValue;
93
+ | ReactiveTernaryValue
94
+ | ReactiveOptionalCallValue;
95
96
export type ReactiveLogicalValue = {
97
kind: "LogicalExpression";
@@ -116,6 +117,14 @@ export type ReactiveSequenceValue = {
117
loc: SourceLocation;
118
};
119
120
+export type ReactiveOptionalCallValue = {
121
+ kind: "OptionalCall";
122
+ id: InstructionId;
123
+ call: ReactiveValue;
124
+ optional: boolean;
125
+ loc: SourceLocation;
126
+};
127
+
128
export type ReactiveTerminal =
129
| ReactiveBreakTerminal
130
| ReactiveContinueTerminal
compiler/forget/src/HIR/PrintHIR.ts
+4
@@ -440,6 +440,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
440
value = `Global ${instrValue.name}`;
441
break;
442
}
443
+ case "OptionalCall": {
444
+ value = `OptionalCall ${printInstructionValue(instrValue.call)}`;
445
+ break;
446
+ }
447
default: {
448
assertExhaustive(
449
instrValue,
compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts
+26
-4
@@ -29,7 +29,6 @@ import {
29
ReactiveValue,
30
Terminal,
31
} from "../HIR/HIR";
32
-import todo from "../Utils/todo";
32
import { assertExhaustive } from "../Utils/utils";
33
34
/**
@@ -457,6 +456,7 @@ class Driver {
456
457
break;
458
}
459
+ case "optional-call":
460
case "ternary":
461
case "logical": {
462
const fallthroughId = terminal.fallthrough;
@@ -482,9 +482,6 @@ class Driver {
482
this.visitBlock(this.cx.ir.blocks.get(fallthroughId)!, blockValue);
483
break;
484
}
485
- case "optional-call": {
486
- todo("BuildReactiveFunction: support optional-call terminal");
487
- }
485
case "goto": {
486
switch (terminal.variant) {
487
case GotoVariant.Break: {
@@ -655,6 +652,31 @@ class Driver {
652
id: InstructionId;
653
} {
654
switch (terminal.kind) {
655
+ case "optional-call": {
656
+ const test = this.visitValueBlock(terminal.test, terminal.loc);
657
+ const testBlock = this.cx.ir.blocks.get(test.block)!;
658
+ invariant(
659
+ testBlock.terminal.kind === "branch",
660
+ "Unexpected terminal kind '%s' for optional call test block",
661
+ testBlock.terminal.kind
662
+ );
663
+ const consequent = this.visitValueBlock(
664
+ testBlock.terminal.consequent,
665
+ terminal.loc
666
+ );
667
+ return {
668
+ place: { ...consequent.place },
669
+ value: {
670
+ kind: "OptionalCall",
671
+ optional: terminal.optional,
672
+ call: consequent.value,
673
+ id: terminal.id,
674
+ loc: terminal.loc,
675
+ },
676
+ fallthrough: terminal.fallthrough,
677
+ id: terminal.id,
678
+ };
679
+ }
680
case "logical": {
681
const test = this.visitValueBlock(terminal.test, terminal.loc);
682
const testBlock = this.cx.ir.blocks.get(test.block)!;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+14
@@ -589,6 +589,20 @@ function codegenInstructionValue(
589
value = createCallExpression(instrValue.loc, callee, args);
590
break;
591
}
592
+ case "OptionalCall": {
593
+ const call = codegenInstructionValue(cx, instrValue.call);
594
+ invariant(call.type === "CallExpression", "Expected a call expression");
595
+ invariant(
596
+ t.isExpression(call.callee),
597
+ "v8 intrinsics are validated during lowering"
598
+ );
599
+ value = t.optionalCallExpression(
600
+ call.callee,
601
+ call.arguments,
602
+ instrValue.optional
603
+ );
604
+ break;
605
+ }
606
case "MethodCall": {
607
const memberExpr = codegenPlace(cx, instrValue.property);
608
invariant(
compiler/forget/src/ReactiveScopes/MergeOverlappingReactiveScopes.ts
+2
-1
@@ -124,7 +124,8 @@ class Visitor extends ReactiveFunctionVisitor<Context> {
124
): void {
125
if (
126
instruction.value.kind === "ConditionalExpression" ||
127
- instruction.value.kind === "LogicalExpression"
127
+ instruction.value.kind === "LogicalExpression" ||
128
+ instruction.value.kind === "OptionalCall"
129
) {
130
state.enter(() => {
131
super.visitInstruction(instruction, state);
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+6
@@ -444,6 +444,12 @@ function visitReactiveValue(
444
value: ReactiveValue
445
): void {
446
switch (value.kind) {
447
+ case "OptionalCall": {
448
+ context.enterConditional(() => {
449
+ visitReactiveValue(context, id, value.call);
450
+ });
451
+ break;
452
+ }
453
case "LogicalExpression": {
454
visitReactiveValue(context, id, value.left);
455
context.enterConditional(() => {
compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+1
@@ -508,6 +508,7 @@ function computeMemoizationInputs(
508
rvalues: [value.value],
509
};
510
}
511
+ case "OptionalCall":
512
case "FunctionExpression":
513
case "TaggedTemplateExpression":
514
case "CallExpression":
compiler/forget/src/ReactiveScopes/visitors.ts
+8
@@ -41,6 +41,10 @@ export class ReactiveFunctionVisitor<TState = void> {
41
}
42
traverseValue(id: InstructionId, value: ReactiveValue, state: TState): void {
43
switch (value.kind) {
44
+ case "OptionalCall": {
45
+ this.visitValue(id, value.call, state);
46
+ break;
47
+ }
48
case "LogicalExpression": {
49
this.visitValue(id, value.left, state);
50
this.visitValue(id, value.right, state);
@@ -277,6 +281,10 @@ export function* eachReactiveValueOperand(
281
instrValue: ReactiveValue
282
): Iterable<Place> {
283
switch (instrValue.kind) {
284
+ case "OptionalCall": {
285
+ yield* eachReactiveValueOperand(instrValue.call);
286
+ break;
287
+ }
288
case "LogicalExpression": {
289
yield* eachReactiveValueOperand(instrValue.left);
290
yield* eachReactiveValueOperand(instrValue.right);
compiler/forget/src/SSA/LeaveSSA.ts
+5
-1
@@ -336,7 +336,11 @@ export function leaveSSA(fn: HIRFunction): void {
336
const update = fn.body.blocks.get(terminal.update)!;
337
pushPhis(update);
338
}
339
- if (terminal.kind === "logical" || terminal.kind === "ternary") {
339
+ if (
340
+ terminal.kind === "logical" ||
341
+ terminal.kind === "ternary" ||
342
+ terminal.kind === "optional-call"
343
+ ) {
344
const fallthrough = fn.body.blocks.get(terminal.fallthrough)!;
345
pushPhis(fallthrough);
346
}
compiler/forget/yarn.lock
+27
-13
@@ -83,12 +83,12 @@
83
"@jridgewell/gen-mapping" "^0.3.2"
84
jsesc "^2.5.1"
85
86
-"@babel/generator@^7.21.1":
87
- version "7.21.1"
88
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd"
89
- integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==
86
+"@babel/generator@^7.21.3":
87
+ version "7.21.3"
88
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce"
89
+ integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==
90
dependencies:
91
- "@babel/types" "^7.21.0"
91
+ "@babel/types" "^7.21.3"
92
"@jridgewell/gen-mapping" "^0.3.2"
93
"@jridgewell/trace-mapping" "^0.3.17"
94
jsesc "^2.5.1"
@@ -269,11 +269,16 @@
269
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
270
integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
271
272
-"@babel/parser@^7.20.7", "@babel/parser@^7.21.2":
272
+"@babel/parser@^7.20.7":
273
version "7.21.2"
274
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3"
275
integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==
276
277
+"@babel/parser@^7.21.3":
278
+ version "7.21.3"
279
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
280
+ integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==
281
+
282
"@babel/plugin-syntax-async-generators@^7.8.4":
283
version "7.8.4"
284
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -502,18 +507,18 @@
507
lodash "^4.17.10"
508
509
"@babel/traverse@^7.19.1":
505
- version "7.21.2"
506
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.2.tgz#ac7e1f27658750892e815e60ae90f382a46d8e75"
507
- integrity sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==
510
+ version "7.21.3"
511
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67"
512
+ integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==
513
dependencies:
514
"@babel/code-frame" "^7.18.6"
510
- "@babel/generator" "^7.21.1"
515
+ "@babel/generator" "^7.21.3"
516
"@babel/helper-environment-visitor" "^7.18.9"
517
"@babel/helper-function-name" "^7.21.0"
518
"@babel/helper-hoist-variables" "^7.18.6"
519
"@babel/helper-split-export-declaration" "^7.18.6"
515
- "@babel/parser" "^7.21.2"
516
- "@babel/types" "^7.21.2"
520
+ "@babel/parser" "^7.21.3"
521
+ "@babel/types" "^7.21.3"
522
debug "^4.1.0"
523
globals "^11.1.0"
524
@@ -561,7 +566,7 @@
566
"@babel/helper-validator-identifier" "^7.19.1"
567
to-fast-properties "^2.0.0"
568
564
-"@babel/types@^7.21.0", "@babel/types@^7.21.2":
569
+"@babel/types@^7.21.0":
570
version "7.21.2"
571
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1"
572
integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==
@@ -570,6 +575,15 @@
575
"@babel/helper-validator-identifier" "^7.19.1"
576
to-fast-properties "^2.0.0"
577
578
+"@babel/types@^7.21.3":
579
+ version "7.21.3"
580
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05"
581
+ integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==
582
+ dependencies:
583
+ "@babel/helper-string-parser" "^7.19.4"
584
+ "@babel/helper-validator-identifier" "^7.19.1"
585
+ to-fast-properties "^2.0.0"
586
+
587
"@bcoe/v8-coverage@^0.2.3":
588
version "0.2.3"
589
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"