Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
InstructionValue,
isArrayType,
isMapType,
isPrimitiveType,
isSetType,
makeIdentifierId,
ObjectMethod,
Expand Down Expand Up @@ -464,12 +465,15 @@ function applyEffect(
break;
}
default: {
effects.push({
// OK: recording information flow
kind: 'CreateFrom', // prev Alias
from: effect.from,
into: effect.into,
});
// Even if the source is non-primitive, the destination may be. skip primitives.
if (!isPrimitiveType(effect.into.identifier)) {
effects.push({
// OK: recording information flow
kind: 'CreateFrom', // prev Alias
from: effect.from,
into: effect.into,
});
}
}
}
break;
Expand Down Expand Up @@ -1622,6 +1626,7 @@ function computeSignatureForInstruction(
suggestions: null,
},
});
effects.push({kind: 'Assign', from: value.value, into: lvalue});
break;
}
case 'TypeCastExpression': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ import {printIdentifier, printPlace} from '../HIR/PrintHIR';
import {MutationKind} from './InferMutationAliasingFunctionEffects';

const DEBUG = false;
const VERBOSE = false;

/**
* Infers mutable ranges for all values.
*/
export function inferMutationAliasingRanges(fn: HIRFunction): void {
if (DEBUG) {
if (VERBOSE) {
console.log();
console.log();
console.log();
Expand Down Expand Up @@ -81,7 +82,7 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
const seenBlocks = new Set<BlockId>();
for (const block of fn.body.blocks.values()) {
for (const phi of block.phis) {
state.create(phi.place);
state.create(phi.place, true);
for (const [pred, operand] of phi.operands) {
if (!seenBlocks.has(pred)) {
// NOTE: annotation required to actually typecheck and not silently infer `any`
Expand Down Expand Up @@ -172,7 +173,7 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
}
}

if (DEBUG) {
if (VERBOSE) {
console.log(state.debug());
console.log(pretty(mutations));
}
Expand All @@ -185,7 +186,7 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
mutation.kind,
);
}
if (DEBUG) {
if (VERBOSE) {
console.log(state.debug());
}
fn.aliasingEffects ??= [];
Expand Down Expand Up @@ -364,7 +365,7 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
}
}

if (DEBUG) {
if (VERBOSE) {
console.log(printFunction(fn));
}
}
Expand All @@ -377,11 +378,12 @@ type Node = {
edges: Array<{index: number; node: Identifier; kind: 'capture' | 'alias'}>;
transitive: MutationKind;
local: MutationKind;
phi: boolean;
};
class AliasingState {
nodes: Map<Identifier, Node> = new Map();

create(place: Place): void {
create(place: Place, phi: boolean = false): void {
this.nodes.set(place.identifier, {
id: place.identifier,
createdFrom: new Map(),
Expand All @@ -390,6 +392,7 @@ class AliasingState {
edges: [],
transitive: MutationKind.None,
local: MutationKind.None,
phi,
});
}

Expand All @@ -398,7 +401,7 @@ class AliasingState {
const fromNode = this.nodes.get(from.identifier);
const toNode = this.nodes.get(into.identifier);
if (fromNode == null || toNode == null) {
if (DEBUG) {
if (VERBOSE) {
console.log(
`skip: createFrom ${printPlace(from)}${!!fromNode} -> ${printPlace(into)}${!!toNode}`,
);
Expand All @@ -415,7 +418,7 @@ class AliasingState {
const fromNode = this.nodes.get(from.identifier);
const toNode = this.nodes.get(into.identifier);
if (fromNode == null || toNode == null) {
if (DEBUG) {
if (VERBOSE) {
console.log(
`skip: capture ${printPlace(from)}${!!fromNode} -> ${printPlace(into)}${!!toNode}`,
);
Expand All @@ -432,7 +435,7 @@ class AliasingState {
const fromNode = this.nodes.get(from.identifier);
const toNode = this.nodes.get(into.identifier);
if (fromNode == null || toNode == null) {
if (DEBUG) {
if (VERBOSE) {
console.log(
`skip: assign ${printPlace(from)}${!!fromNode} -> ${printPlace(into)}${!!toNode}`,
);
Expand All @@ -453,9 +456,13 @@ class AliasingState {
kind: MutationKind,
): void {
const seen = new Set<Identifier>();
const queue = [{place: start, transitive}];
const queue: Array<{
place: Identifier;
transitive: boolean;
direction: 'backwards' | 'forwards';
}> = [{place: start, transitive, direction: 'backwards'}];
while (queue.length !== 0) {
const {place: current, transitive} = queue.pop()!;
const {place: current, transitive, direction} = queue.pop()!;
if (seen.has(current)) {
continue;
}
Expand All @@ -471,7 +478,7 @@ class AliasingState {
}
if (DEBUG) {
console.log(
`mutate $${node.id.id} via ${printIdentifier(start)} at [${end}]`,
`[${end}] mutate index=${index} ${printIdentifier(start)}: ${printIdentifier(node.id)}`,
);
}
node.id.mutableRange.end = makeInstructionId(
Expand All @@ -491,24 +498,31 @@ class AliasingState {
if (edge.index >= index) {
break;
}
queue.push({place: edge.node, transitive});
queue.push({place: edge.node, transitive, direction: 'forwards'});
}
for (const [alias, when] of node.createdFrom) {
if (when >= index) {
continue;
}
queue.push({place: alias, transitive: true});
queue.push({place: alias, transitive: true, direction: 'backwards'});
}
/**
* all mutations affect backward alias edges by the rules:
* - Alias a -> b, mutate(b) => mutate(a)
* - Alias a -> b, mutateTransitive(b) => mutate(a)
*/
for (const [alias, when] of node.aliases) {
if (when >= index) {
continue;
if (direction === 'backwards' || !node.phi) {
/**
* all mutations affect backward alias edges by the rules:
* - Alias a -> b, mutate(b) => mutate(a)
* - Alias a -> b, mutateTransitive(b) => mutate(a)
*
* However, if we reached a phi because one of its inputs was mutated
* (and we're advancing "forwards" through that node's edges), then
* we know we've already processed the mutation at its source. The
* phi's other inputs can't be affected.
*/
for (const [alias, when] of node.aliases) {
if (when >= index) {
continue;
}
queue.push({place: alias, transitive, direction: 'backwards'});
}
queue.push({place: alias, transitive});
}
/**
* but only transitive mutations affect captures
Expand All @@ -518,10 +532,18 @@ class AliasingState {
if (when >= index) {
continue;
}
queue.push({place: capture, transitive});
queue.push({place: capture, transitive, direction: 'backwards'});
}
}
}
if (DEBUG) {
const nodes = new Map();
for (const id of seen) {
const node = this.nodes.get(id);
nodes.set(id.id, node);
}
console.log(pretty(nodes));
}
}

debug(): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
getHookKind,
makeIdentifierName,
} from '../HIR/HIR';
import {printIdentifier, printPlace} from '../HIR/PrintHIR';
import {printIdentifier, printInstruction, printPlace} from '../HIR/PrintHIR';
import {eachPatternOperand} from '../HIR/visitors';
import {Err, Ok, Result} from '../Utils/Result';
import {GuardKind} from '../Utils/RuntimeDiagnosticConstants';
Expand Down Expand Up @@ -1310,7 +1310,7 @@ function codegenInstructionNullable(
});
CompilerError.invariant(value?.type === 'FunctionExpression', {
reason: 'Expected a function as a function declaration value',
description: null,
description: `Got ${value == null ? String(value) : value.type} at ${printInstruction(instr)}`,
loc: instr.value.loc,
suggestions: null,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

## Input

```javascript
// @enableNewMutationAliasingModel
import {Stringify} from 'shared-runtime';

function Component({a, b}) {
const x = {a, b};
const f = () => {
const y = [x];
return y[0];
};
const x0 = f();
const z = [x0];
const x1 = z[0];
x1.key = 'value';
return <Stringify x={x} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{a: 0, b: 1}],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { Stringify } from "shared-runtime";

function Component(t0) {
const $ = _c(3);
const { a, b } = t0;
let t1;
if ($[0] !== a || $[1] !== b) {
const x = { a, b };
const f = () => {
const y = [x];
return y[0];
};

const x0 = f();
const z = [x0];
const x1 = z[0];
x1.key = "value";
t1 = <Stringify x={x} />;
$[0] = a;
$[1] = b;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ a: 0, b: 1 }],
};

```

### Eval output
(kind: ok) <div>{"x":{"a":0,"b":1,"key":"value"}}</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @enableNewMutationAliasingModel
import {Stringify} from 'shared-runtime';

function Component({a, b}) {
const x = {a, b};
const f = () => {
const y = [x];
return y[0];
};
const x0 = f();
const z = [x0];
const x1 = z[0];
x1.key = 'value';
return <Stringify x={x} />;
}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{a: 0, b: 1}],
};
Loading
Loading