Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5008fd1d18 | ||
|
|
d2b2a1b195 | ||
|
|
1671142ea8 |
@@ -25,7 +25,6 @@ import {
|
|||||||
IdentifierId,
|
IdentifierId,
|
||||||
Instruction,
|
Instruction,
|
||||||
InstructionKind,
|
InstructionKind,
|
||||||
InstructionValue,
|
|
||||||
isArrayType,
|
isArrayType,
|
||||||
isJsxType,
|
isJsxType,
|
||||||
isMapType,
|
isMapType,
|
||||||
@@ -57,7 +56,6 @@ import {
|
|||||||
printAliasingSignature,
|
printAliasingSignature,
|
||||||
printIdentifier,
|
printIdentifier,
|
||||||
printInstruction,
|
printInstruction,
|
||||||
printInstructionValue,
|
|
||||||
printPlace,
|
printPlace,
|
||||||
printSourceLocation,
|
printSourceLocation,
|
||||||
} from '../HIR/PrintHIR';
|
} from '../HIR/PrintHIR';
|
||||||
@@ -108,11 +106,11 @@ export function inferMutationAliasingEffects(
|
|||||||
const statesByBlock: Map<BlockId, InferenceState> = new Map();
|
const statesByBlock: Map<BlockId, InferenceState> = new Map();
|
||||||
|
|
||||||
for (const ref of fn.context) {
|
for (const ref of fn.context) {
|
||||||
// TODO: using InstructionValue as a bit of a hack, but it's pragmatic
|
const value: AliasingEffect = {
|
||||||
const value: InstructionValue = {
|
kind: 'Create',
|
||||||
kind: 'ObjectExpression',
|
into: ref,
|
||||||
properties: [],
|
value: ValueKind.Context,
|
||||||
loc: ref.loc,
|
reason: ValueReason.Other,
|
||||||
};
|
};
|
||||||
initialState.initialize(value, {
|
initialState.initialize(value, {
|
||||||
kind: ValueKind.Context,
|
kind: ValueKind.Context,
|
||||||
@@ -145,10 +143,11 @@ export function inferMutationAliasingEffects(
|
|||||||
}
|
}
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
const place = ref.kind === 'Identifier' ? ref : ref.place;
|
const place = ref.kind === 'Identifier' ? ref : ref.place;
|
||||||
const value: InstructionValue = {
|
const value: AliasingEffect = {
|
||||||
kind: 'ObjectExpression',
|
kind: 'Create',
|
||||||
properties: [],
|
into: place,
|
||||||
loc: place.loc,
|
value: ValueKind.Mutable,
|
||||||
|
reason: ValueReason.Other,
|
||||||
};
|
};
|
||||||
initialState.initialize(value, {
|
initialState.initialize(value, {
|
||||||
kind: ValueKind.Mutable,
|
kind: ValueKind.Mutable,
|
||||||
@@ -265,8 +264,6 @@ function findHoistedContextDeclarations(
|
|||||||
class Context {
|
class Context {
|
||||||
internedEffects: Map<string, AliasingEffect> = new Map();
|
internedEffects: Map<string, AliasingEffect> = new Map();
|
||||||
instructionSignatureCache: Map<Instruction, InstructionSignature> = new Map();
|
instructionSignatureCache: Map<Instruction, InstructionSignature> = new Map();
|
||||||
effectInstructionValueCache: Map<AliasingEffect, InstructionValue> =
|
|
||||||
new Map();
|
|
||||||
applySignatureCache: Map<
|
applySignatureCache: Map<
|
||||||
AliasingSignature,
|
AliasingSignature,
|
||||||
Map<AliasingEffect, Array<AliasingEffect> | null>
|
Map<AliasingEffect, Array<AliasingEffect> | null>
|
||||||
@@ -318,10 +315,11 @@ function inferParam(
|
|||||||
paramKind: AbstractValue,
|
paramKind: AbstractValue,
|
||||||
): void {
|
): void {
|
||||||
const place = param.kind === 'Identifier' ? param : param.place;
|
const place = param.kind === 'Identifier' ? param : param.place;
|
||||||
const value: InstructionValue = {
|
const value: AliasingEffect = {
|
||||||
kind: 'Primitive',
|
kind: 'Create',
|
||||||
loc: place.loc,
|
into: place,
|
||||||
value: undefined,
|
value: paramKind.kind,
|
||||||
|
reason: ValueReason.Other,
|
||||||
};
|
};
|
||||||
initialState.initialize(value, paramKind);
|
initialState.initialize(value, paramKind);
|
||||||
initialState.define(place, value);
|
initialState.define(place, value);
|
||||||
@@ -542,20 +540,11 @@ function applyEffect(
|
|||||||
});
|
});
|
||||||
initialized.add(effect.into.identifier.id);
|
initialized.add(effect.into.identifier.id);
|
||||||
|
|
||||||
let value = context.effectInstructionValueCache.get(effect);
|
state.initialize(effect, {
|
||||||
if (value == null) {
|
|
||||||
value = {
|
|
||||||
kind: 'ObjectExpression',
|
|
||||||
properties: [],
|
|
||||||
loc: effect.into.loc,
|
|
||||||
};
|
|
||||||
context.effectInstructionValueCache.set(effect, value);
|
|
||||||
}
|
|
||||||
state.initialize(value, {
|
|
||||||
kind: effect.value,
|
kind: effect.value,
|
||||||
reason: new Set([effect.reason]),
|
reason: new Set([effect.reason]),
|
||||||
});
|
});
|
||||||
state.define(effect.into, value);
|
state.define(effect.into, effect);
|
||||||
effects.push(effect);
|
effects.push(effect);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -582,20 +571,11 @@ function applyEffect(
|
|||||||
initialized.add(effect.into.identifier.id);
|
initialized.add(effect.into.identifier.id);
|
||||||
|
|
||||||
const fromValue = state.kind(effect.from);
|
const fromValue = state.kind(effect.from);
|
||||||
let value = context.effectInstructionValueCache.get(effect);
|
state.initialize(effect, {
|
||||||
if (value == null) {
|
|
||||||
value = {
|
|
||||||
kind: 'ObjectExpression',
|
|
||||||
properties: [],
|
|
||||||
loc: effect.into.loc,
|
|
||||||
};
|
|
||||||
context.effectInstructionValueCache.set(effect, value);
|
|
||||||
}
|
|
||||||
state.initialize(value, {
|
|
||||||
kind: fromValue.kind,
|
kind: fromValue.kind,
|
||||||
reason: new Set(fromValue.reason),
|
reason: new Set(fromValue.reason),
|
||||||
});
|
});
|
||||||
state.define(effect.into, value);
|
state.define(effect.into, effect);
|
||||||
switch (fromValue.kind) {
|
switch (fromValue.kind) {
|
||||||
case ValueKind.Primitive:
|
case ValueKind.Primitive:
|
||||||
case ValueKind.Global: {
|
case ValueKind.Global: {
|
||||||
@@ -683,11 +663,11 @@ function applyEffect(
|
|||||||
operand.effect = Effect.Read;
|
operand.effect = Effect.Read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.initialize(effect.function, {
|
state.initialize(effect, {
|
||||||
kind: isMutable ? ValueKind.Mutable : ValueKind.Frozen,
|
kind: isMutable ? ValueKind.Mutable : ValueKind.Frozen,
|
||||||
reason: new Set([]),
|
reason: new Set([]),
|
||||||
});
|
});
|
||||||
state.define(effect.into, effect.function);
|
state.define(effect.into, effect);
|
||||||
for (const capture of effect.captures) {
|
for (const capture of effect.captures) {
|
||||||
applyEffect(
|
applyEffect(
|
||||||
context,
|
context,
|
||||||
@@ -793,38 +773,20 @@ function applyEffect(
|
|||||||
initialized,
|
initialized,
|
||||||
effects,
|
effects,
|
||||||
);
|
);
|
||||||
let value = context.effectInstructionValueCache.get(effect);
|
state.initialize(effect, {
|
||||||
if (value == null) {
|
|
||||||
value = {
|
|
||||||
kind: 'Primitive',
|
|
||||||
value: undefined,
|
|
||||||
loc: effect.from.loc,
|
|
||||||
};
|
|
||||||
context.effectInstructionValueCache.set(effect, value);
|
|
||||||
}
|
|
||||||
state.initialize(value, {
|
|
||||||
kind: fromKind,
|
kind: fromKind,
|
||||||
reason: new Set(fromValue.reason),
|
reason: new Set(fromValue.reason),
|
||||||
});
|
});
|
||||||
state.define(effect.into, value);
|
state.define(effect.into, effect);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueKind.Global:
|
case ValueKind.Global:
|
||||||
case ValueKind.Primitive: {
|
case ValueKind.Primitive: {
|
||||||
let value = context.effectInstructionValueCache.get(effect);
|
state.initialize(effect, {
|
||||||
if (value == null) {
|
|
||||||
value = {
|
|
||||||
kind: 'Primitive',
|
|
||||||
value: undefined,
|
|
||||||
loc: effect.from.loc,
|
|
||||||
};
|
|
||||||
context.effectInstructionValueCache.set(effect, value);
|
|
||||||
}
|
|
||||||
state.initialize(value, {
|
|
||||||
kind: fromKind,
|
kind: fromKind,
|
||||||
reason: new Set(fromValue.reason),
|
reason: new Set(fromValue.reason),
|
||||||
});
|
});
|
||||||
state.define(effect.into, value);
|
state.define(effect.into, effect);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -839,14 +801,15 @@ function applyEffect(
|
|||||||
const functionValues = state.values(effect.function);
|
const functionValues = state.values(effect.function);
|
||||||
if (
|
if (
|
||||||
functionValues.length === 1 &&
|
functionValues.length === 1 &&
|
||||||
functionValues[0].kind === 'FunctionExpression' &&
|
functionValues[0].kind === 'CreateFunction' &&
|
||||||
functionValues[0].loweredFunc.func.aliasingEffects != null
|
functionValues[0].function.kind === 'FunctionExpression' &&
|
||||||
|
functionValues[0].function.loweredFunc.func.aliasingEffects != null
|
||||||
) {
|
) {
|
||||||
/*
|
/*
|
||||||
* We're calling a locally declared function, we already know it's effects!
|
* We're calling a locally declared function, we already know it's effects!
|
||||||
* We just have to substitute in the args for the params
|
* We just have to substitute in the args for the params
|
||||||
*/
|
*/
|
||||||
const functionExpr = functionValues[0];
|
const functionExpr = functionValues[0].function;
|
||||||
let signature = context.functionSignatureCache.get(functionExpr);
|
let signature = context.functionSignatureCache.get(functionExpr);
|
||||||
if (signature == null) {
|
if (signature == null) {
|
||||||
signature = buildSignatureFromFunctionExpression(
|
signature = buildSignatureFromFunctionExpression(
|
||||||
@@ -1136,19 +1099,19 @@ class InferenceState {
|
|||||||
#isFunctionExpression: boolean;
|
#isFunctionExpression: boolean;
|
||||||
|
|
||||||
// The kind of each value, based on its allocation site
|
// The kind of each value, based on its allocation site
|
||||||
#values: Map<InstructionValue, AbstractValue>;
|
#values: Map<AliasingEffect, AbstractValue>;
|
||||||
/*
|
/*
|
||||||
* The set of values pointed to by each identifier. This is a set
|
* The set of values pointed to by each identifier. This is a set
|
||||||
* to accomodate phi points (where a variable may have different
|
* to accomodate phi points (where a variable may have different
|
||||||
* values from different control flow paths).
|
* values from different control flow paths).
|
||||||
*/
|
*/
|
||||||
#variables: Map<IdentifierId, Set<InstructionValue>>;
|
#variables: Map<IdentifierId, Set<AliasingEffect>>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
env: Environment,
|
env: Environment,
|
||||||
isFunctionExpression: boolean,
|
isFunctionExpression: boolean,
|
||||||
values: Map<InstructionValue, AbstractValue>,
|
values: Map<AliasingEffect, AbstractValue>,
|
||||||
variables: Map<IdentifierId, Set<InstructionValue>>,
|
variables: Map<IdentifierId, Set<AliasingEffect>>,
|
||||||
) {
|
) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.#isFunctionExpression = isFunctionExpression;
|
this.#isFunctionExpression = isFunctionExpression;
|
||||||
@@ -1168,18 +1131,11 @@ class InferenceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// (Re)initializes a @param value with its default @param kind.
|
// (Re)initializes a @param value with its default @param kind.
|
||||||
initialize(value: InstructionValue, kind: AbstractValue): void {
|
initialize(value: AliasingEffect, kind: AbstractValue): void {
|
||||||
CompilerError.invariant(value.kind !== 'LoadLocal', {
|
|
||||||
reason:
|
|
||||||
'[InferMutationAliasingEffects] Expected all top-level identifiers to be defined as variables, not values',
|
|
||||||
description: null,
|
|
||||||
loc: value.loc,
|
|
||||||
suggestions: null,
|
|
||||||
});
|
|
||||||
this.#values.set(value, kind);
|
this.#values.set(value, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
values(place: Place): Array<InstructionValue> {
|
values(place: Place): Array<AliasingEffect> {
|
||||||
const values = this.#variables.get(place.identifier.id);
|
const values = this.#variables.get(place.identifier.id);
|
||||||
CompilerError.invariant(values != null, {
|
CompilerError.invariant(values != null, {
|
||||||
reason: `[InferMutationAliasingEffects] Expected value kind to be initialized`,
|
reason: `[InferMutationAliasingEffects] Expected value kind to be initialized`,
|
||||||
@@ -1242,13 +1198,13 @@ class InferenceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Defines (initializing or updating) a variable with a specific kind of value.
|
// Defines (initializing or updating) a variable with a specific kind of value.
|
||||||
define(place: Place, value: InstructionValue): void {
|
define(place: Place, value: AliasingEffect): void {
|
||||||
CompilerError.invariant(this.#values.has(value), {
|
CompilerError.invariant(this.#values.has(value), {
|
||||||
reason: `[InferMutationAliasingEffects] Expected value to be initialized at '${printSourceLocation(
|
reason: `[InferMutationAliasingEffects] Expected value to be initialized at '${printSourceLocation(
|
||||||
value.loc,
|
place.loc,
|
||||||
)}'`,
|
)}'`,
|
||||||
description: printInstructionValue(value),
|
description: printAliasingEffect(value),
|
||||||
loc: value.loc,
|
loc: place.loc,
|
||||||
suggestions: null,
|
suggestions: null,
|
||||||
});
|
});
|
||||||
this.#variables.set(place.identifier.id, new Set([value]));
|
this.#variables.set(place.identifier.id, new Set([value]));
|
||||||
@@ -1288,17 +1244,17 @@ class InferenceState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
freezeValue(value: InstructionValue, reason: ValueReason): void {
|
freezeValue(value: AliasingEffect, reason: ValueReason): void {
|
||||||
this.#values.set(value, {
|
this.#values.set(value, {
|
||||||
kind: ValueKind.Frozen,
|
kind: ValueKind.Frozen,
|
||||||
reason: new Set([reason]),
|
reason: new Set([reason]),
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
value.kind === 'FunctionExpression' &&
|
value.kind === 'CreateFunction' &&
|
||||||
(this.env.config.enablePreserveExistingMemoizationGuarantees ||
|
(this.env.config.enablePreserveExistingMemoizationGuarantees ||
|
||||||
this.env.config.enableTransitivelyFreezeFunctionExpressions)
|
this.env.config.enableTransitivelyFreezeFunctionExpressions)
|
||||||
) {
|
) {
|
||||||
for (const place of value.loweredFunc.func.context) {
|
for (const place of value.function.loweredFunc.func.context) {
|
||||||
this.freeze(place, reason);
|
this.freeze(place, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1373,8 +1329,8 @@ class InferenceState {
|
|||||||
* termination.
|
* termination.
|
||||||
*/
|
*/
|
||||||
merge(other: InferenceState): InferenceState | null {
|
merge(other: InferenceState): InferenceState | null {
|
||||||
let nextValues: Map<InstructionValue, AbstractValue> | null = null;
|
let nextValues: Map<AliasingEffect, AbstractValue> | null = null;
|
||||||
let nextVariables: Map<IdentifierId, Set<InstructionValue>> | null = null;
|
let nextVariables: Map<IdentifierId, Set<AliasingEffect>> | null = null;
|
||||||
|
|
||||||
for (const [id, thisValue] of this.#values) {
|
for (const [id, thisValue] of this.#values) {
|
||||||
const otherValue = other.#values.get(id);
|
const otherValue = other.#values.get(id);
|
||||||
@@ -1398,7 +1354,7 @@ class InferenceState {
|
|||||||
for (const [id, thisValues] of this.#variables) {
|
for (const [id, thisValues] of this.#variables) {
|
||||||
const otherValues = other.#variables.get(id);
|
const otherValues = other.#variables.get(id);
|
||||||
if (otherValues !== undefined) {
|
if (otherValues !== undefined) {
|
||||||
let mergedValues: Set<InstructionValue> | null = null;
|
let mergedValues: Set<AliasingEffect> | null = null;
|
||||||
for (const otherValue of otherValues) {
|
for (const otherValue of otherValues) {
|
||||||
if (!thisValues.has(otherValue)) {
|
if (!thisValues.has(otherValue)) {
|
||||||
mergedValues = mergedValues ?? new Set(thisValues);
|
mergedValues = mergedValues ?? new Set(thisValues);
|
||||||
@@ -1451,8 +1407,8 @@ class InferenceState {
|
|||||||
*/
|
*/
|
||||||
debug(): any {
|
debug(): any {
|
||||||
const result: any = {values: {}, variables: {}};
|
const result: any = {values: {}, variables: {}};
|
||||||
const objects: Map<InstructionValue, number> = new Map();
|
const objects: Map<AliasingEffect, number> = new Map();
|
||||||
function identify(value: InstructionValue): number {
|
function identify(value: AliasingEffect): number {
|
||||||
let id = objects.get(value);
|
let id = objects.get(value);
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
id = objects.size;
|
id = objects.size;
|
||||||
@@ -1464,7 +1420,7 @@ class InferenceState {
|
|||||||
const id = identify(value);
|
const id = identify(value);
|
||||||
result.values[id] = {
|
result.values[id] = {
|
||||||
abstract: this.debugAbstractValue(kind),
|
abstract: this.debugAbstractValue(kind),
|
||||||
value: printInstructionValue(value),
|
value: printAliasingEffect(value),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
for (const [variable, values] of this.#variables) {
|
for (const [variable, values] of this.#variables) {
|
||||||
@@ -1481,7 +1437,7 @@ class InferenceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inferPhi(phi: Phi): void {
|
inferPhi(phi: Phi): void {
|
||||||
const values: Set<InstructionValue> = new Set();
|
const values: Set<AliasingEffect> = new Set();
|
||||||
for (const [_, operand] of phi.operands) {
|
for (const [_, operand] of phi.operands) {
|
||||||
const operandValues = this.#variables.get(operand.identifier.id);
|
const operandValues = this.#variables.get(operand.identifier.id);
|
||||||
// This is a backedge that will be handled later by State.merge
|
// This is a backedge that will be handled later by State.merge
|
||||||
@@ -2347,8 +2303,9 @@ function areArgumentsImmutableAndNonMutating(
|
|||||||
const values = state.values(place);
|
const values = state.values(place);
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
if (
|
if (
|
||||||
value.kind === 'FunctionExpression' &&
|
value.kind === 'CreateFunction' &&
|
||||||
value.loweredFunc.func.params.some(param => {
|
value.function.kind === 'FunctionExpression' &&
|
||||||
|
value.function.loweredFunc.func.params.some(param => {
|
||||||
const place = param.kind === 'Identifier' ? param : param.place;
|
const place = param.kind === 'Identifier' ? param : param.place;
|
||||||
const range = place.identifier.mutableRange;
|
const range = place.identifier.mutableRange;
|
||||||
return range.end > range.start + 1;
|
return range.end > range.start + 1;
|
||||||
@@ -2541,10 +2498,47 @@ function computeEffectsForSignature(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'CreateFunction': {
|
case 'CreateFunction': {
|
||||||
CompilerError.throwTodo({
|
const applyInto = substitutions.get(effect.into.identifier.id);
|
||||||
reason: `Support CreateFrom effects in signatures`,
|
if (applyInto == null || applyInto.length !== 1) {
|
||||||
loc: receiver.loc,
|
return null;
|
||||||
|
}
|
||||||
|
const captures: Array<Place> = [];
|
||||||
|
for (let i = 0; i < effect.captures.length; i++) {
|
||||||
|
const substitution = substitutions.get(
|
||||||
|
effect.captures[i].identifier.id,
|
||||||
|
);
|
||||||
|
if (substitution == null || substitution.length !== 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
captures.push(substitution[0]);
|
||||||
|
}
|
||||||
|
const context: Array<Place> = [];
|
||||||
|
const originalContext = effect.function.loweredFunc.func.context;
|
||||||
|
for (let i = 0; i < originalContext.length; i++) {
|
||||||
|
const substitution = substitutions.get(
|
||||||
|
originalContext[i].identifier.id,
|
||||||
|
);
|
||||||
|
if (substitution == null || substitution.length !== 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
context.push(substitution[0]);
|
||||||
|
}
|
||||||
|
effects.push({
|
||||||
|
kind: 'CreateFunction',
|
||||||
|
into: applyInto[0],
|
||||||
|
function: {
|
||||||
|
...effect.function,
|
||||||
|
loweredFunc: {
|
||||||
|
...effect.function.loweredFunc,
|
||||||
|
func: {
|
||||||
|
...effect.function.loweredFunc.func,
|
||||||
|
context,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
captures,
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
assertExhaustive(
|
assertExhaustive(
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export function inferMutationAliasingRanges(
|
|||||||
} else if (effect.kind === 'CreateFunction') {
|
} else if (effect.kind === 'CreateFunction') {
|
||||||
state.create(effect.into, {
|
state.create(effect.into, {
|
||||||
kind: 'Function',
|
kind: 'Function',
|
||||||
function: effect.function.loweredFunc.func,
|
effect,
|
||||||
});
|
});
|
||||||
} else if (effect.kind === 'CreateFrom') {
|
} else if (effect.kind === 'CreateFrom') {
|
||||||
state.createFrom(index++, effect.from, effect.into);
|
state.createFrom(index++, effect.from, effect.into);
|
||||||
@@ -156,7 +156,7 @@ export function inferMutationAliasingRanges(
|
|||||||
* invariant here.
|
* invariant here.
|
||||||
*/
|
*/
|
||||||
if (!state.nodes.has(effect.into.identifier)) {
|
if (!state.nodes.has(effect.into.identifier)) {
|
||||||
state.create(effect.into, {kind: 'Object'});
|
state.create(effect.into, {kind: 'Assign'});
|
||||||
}
|
}
|
||||||
state.assign(index++, effect.from, effect.into);
|
state.assign(index++, effect.from, effect.into);
|
||||||
} else if (effect.kind === 'Alias') {
|
} else if (effect.kind === 'Alias') {
|
||||||
@@ -474,6 +474,99 @@ export function inferMutationAliasingRanges(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tracked: Array<Place> = [];
|
||||||
|
for (const param of [...fn.params, ...fn.context, fn.returns]) {
|
||||||
|
const place = param.kind === 'Identifier' ? param : param.place;
|
||||||
|
tracked.push(place);
|
||||||
|
}
|
||||||
|
|
||||||
|
const returned: Set<Node> = new Set();
|
||||||
|
const queue: Array<Node> = [state.nodes.get(fn.returns.identifier)!];
|
||||||
|
const seen: Set<Node> = new Set();
|
||||||
|
while (queue.length !== 0) {
|
||||||
|
const node = queue.pop()!;
|
||||||
|
if (seen.has(node)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen.add(node);
|
||||||
|
for (const id of node.aliases.keys()) {
|
||||||
|
queue.push(state.nodes.get(id)!);
|
||||||
|
}
|
||||||
|
for (const id of node.createdFrom.keys()) {
|
||||||
|
queue.push(state.nodes.get(id)!);
|
||||||
|
}
|
||||||
|
if (node.id.id === fn.returns.identifier.id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (node.value.kind) {
|
||||||
|
case 'Assign':
|
||||||
|
case 'CreateFrom': {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'Phi':
|
||||||
|
case 'Object':
|
||||||
|
case 'Function': {
|
||||||
|
returned.add(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
assertExhaustive(
|
||||||
|
node.value,
|
||||||
|
`Unexpected node value kind '${(node.value as any).kind}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const returnedValues = [...returned];
|
||||||
|
if (
|
||||||
|
returnedValues.length === 1 &&
|
||||||
|
returnedValues[0].value.kind === 'Object' &&
|
||||||
|
tracked.some(place => place.identifier.id === returnedValues[0].id.id)
|
||||||
|
) {
|
||||||
|
const from = tracked.find(
|
||||||
|
place => place.identifier.id === returnedValues[0].id.id,
|
||||||
|
)!;
|
||||||
|
functionEffects.push({
|
||||||
|
kind: 'Assign',
|
||||||
|
from,
|
||||||
|
into: fn.returns,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
returnedValues.length === 1 &&
|
||||||
|
returnedValues[0].value.kind === 'Function'
|
||||||
|
) {
|
||||||
|
const outerContext = new Set(fn.context.map(p => p.identifier.id));
|
||||||
|
const effect = returnedValues[0].value.effect;
|
||||||
|
functionEffects.push({
|
||||||
|
kind: 'CreateFunction',
|
||||||
|
function: {
|
||||||
|
...effect.function,
|
||||||
|
loweredFunc: {
|
||||||
|
func: {
|
||||||
|
...effect.function.loweredFunc.func,
|
||||||
|
context: effect.function.loweredFunc.func.context.filter(p =>
|
||||||
|
outerContext.has(p.identifier.id),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
captures: effect.captures.filter(p => outerContext.has(p.identifier.id)),
|
||||||
|
into: fn.returns,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const returns = fn.returns.identifier;
|
||||||
|
functionEffects.push({
|
||||||
|
kind: 'Create',
|
||||||
|
into: fn.returns,
|
||||||
|
value: isPrimitiveType(returns)
|
||||||
|
? ValueKind.Primitive
|
||||||
|
: isJsxType(returns.type)
|
||||||
|
? ValueKind.Frozen
|
||||||
|
: ValueKind.Mutable,
|
||||||
|
reason: ValueReason.KnownReturnSignature,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part 3
|
* Part 3
|
||||||
* Finish populating the externally visible effects. Above we bubble-up the side effects
|
* Finish populating the externally visible effects. Above we bubble-up the side effects
|
||||||
@@ -481,28 +574,12 @@ export function inferMutationAliasingRanges(
|
|||||||
* Here we populate an effect to create the return value as well as populating alias/capture
|
* Here we populate an effect to create the return value as well as populating alias/capture
|
||||||
* effects for how data flows between the params, context vars, and return.
|
* effects for how data flows between the params, context vars, and return.
|
||||||
*/
|
*/
|
||||||
const returns = fn.returns.identifier;
|
|
||||||
functionEffects.push({
|
|
||||||
kind: 'Create',
|
|
||||||
into: fn.returns,
|
|
||||||
value: isPrimitiveType(returns)
|
|
||||||
? ValueKind.Primitive
|
|
||||||
: isJsxType(returns.type)
|
|
||||||
? ValueKind.Frozen
|
|
||||||
: ValueKind.Mutable,
|
|
||||||
reason: ValueReason.KnownReturnSignature,
|
|
||||||
});
|
|
||||||
/**
|
/**
|
||||||
* Determine precise data-flow effects by simulating transitive mutations of the params/
|
* Determine precise data-flow effects by simulating transitive mutations of the params/
|
||||||
* captures and seeing what other params/context variables are affected. Anything that
|
* captures and seeing what other params/context variables are affected. Anything that
|
||||||
* would be transitively mutated needs a capture relationship.
|
* would be transitively mutated needs a capture relationship.
|
||||||
*/
|
*/
|
||||||
const tracked: Array<Place> = [];
|
|
||||||
const ignoredErrors = new CompilerError();
|
const ignoredErrors = new CompilerError();
|
||||||
for (const param of [...fn.params, ...fn.context, fn.returns]) {
|
|
||||||
const place = param.kind === 'Identifier' ? param : param.place;
|
|
||||||
tracked.push(place);
|
|
||||||
}
|
|
||||||
for (const into of tracked) {
|
for (const into of tracked) {
|
||||||
const mutationIndex = index++;
|
const mutationIndex = index++;
|
||||||
state.mutate(
|
state.mutate(
|
||||||
@@ -588,9 +665,14 @@ type Node = {
|
|||||||
lastMutated: number;
|
lastMutated: number;
|
||||||
mutationReason: MutationReason | null;
|
mutationReason: MutationReason | null;
|
||||||
value:
|
value:
|
||||||
|
| {kind: 'Assign'}
|
||||||
|
| {kind: 'CreateFrom'}
|
||||||
| {kind: 'Object'}
|
| {kind: 'Object'}
|
||||||
| {kind: 'Phi'}
|
| {kind: 'Phi'}
|
||||||
| {kind: 'Function'; function: HIRFunction};
|
| {
|
||||||
|
kind: 'Function';
|
||||||
|
effect: Extract<AliasingEffect, {kind: 'CreateFunction'}>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
class AliasingState {
|
class AliasingState {
|
||||||
nodes: Map<Identifier, Node> = new Map();
|
nodes: Map<Identifier, Node> = new Map();
|
||||||
@@ -612,7 +694,7 @@ class AliasingState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createFrom(index: number, from: Place, into: Place): void {
|
createFrom(index: number, from: Place, into: Place): void {
|
||||||
this.create(into, {kind: 'Object'});
|
this.create(into, {kind: 'CreateFrom'});
|
||||||
const fromNode = this.nodes.get(from.identifier);
|
const fromNode = this.nodes.get(from.identifier);
|
||||||
const toNode = this.nodes.get(into.identifier);
|
const toNode = this.nodes.get(into.identifier);
|
||||||
if (fromNode == null || toNode == null) {
|
if (fromNode == null || toNode == null) {
|
||||||
@@ -674,7 +756,10 @@ class AliasingState {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (node.value.kind === 'Function') {
|
if (node.value.kind === 'Function') {
|
||||||
appendFunctionErrors(errors, node.value.function);
|
appendFunctionErrors(
|
||||||
|
errors,
|
||||||
|
node.value.effect.function.loweredFunc.func,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for (const [alias, when] of node.createdFrom) {
|
for (const [alias, when] of node.createdFrom) {
|
||||||
if (when >= index) {
|
if (when >= index) {
|
||||||
@@ -738,7 +823,10 @@ class AliasingState {
|
|||||||
node.transitive == null &&
|
node.transitive == null &&
|
||||||
node.local == null
|
node.local == null
|
||||||
) {
|
) {
|
||||||
appendFunctionErrors(errors, node.value.function);
|
appendFunctionErrors(
|
||||||
|
errors,
|
||||||
|
node.value.effect.function.loweredFunc.func,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (transitive) {
|
if (transitive) {
|
||||||
if (node.transitive == null || node.transitive.kind < kind) {
|
if (node.transitive == null || node.transitive.kind < kind) {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
function Component() {
|
||||||
|
const f = () => () => {
|
||||||
|
global.property = true;
|
||||||
|
};
|
||||||
|
f()();
|
||||||
|
return <div>Ooops</div>;
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@ import { makeArray, Stringify, useIdentity } from "shared-runtime";
|
|||||||
*/
|
*/
|
||||||
function Foo(t0) {
|
function Foo(t0) {
|
||||||
"use memo";
|
"use memo";
|
||||||
const $ = _c(3);
|
const $ = _c(5);
|
||||||
const { b } = t0;
|
const { b } = t0;
|
||||||
|
|
||||||
const fnFactory = () => () => {
|
const fnFactory = () => () => {
|
||||||
@@ -66,18 +66,26 @@ function Foo(t0) {
|
|||||||
useIdentity();
|
useIdentity();
|
||||||
|
|
||||||
const fn = fnFactory();
|
const fn = fnFactory();
|
||||||
const arr = makeArray(b);
|
|
||||||
fn(arr);
|
|
||||||
let t1;
|
let t1;
|
||||||
if ($[0] !== arr || $[1] !== myVar) {
|
if ($[0] !== b) {
|
||||||
t1 = <Stringify cb={myVar} value={arr} shouldInvokeFns={true} />;
|
t1 = makeArray(b);
|
||||||
$[0] = arr;
|
$[0] = b;
|
||||||
$[1] = myVar;
|
$[1] = t1;
|
||||||
$[2] = t1;
|
|
||||||
} else {
|
} else {
|
||||||
t1 = $[2];
|
t1 = $[1];
|
||||||
}
|
}
|
||||||
return t1;
|
const arr = t1;
|
||||||
|
fn(arr);
|
||||||
|
let t2;
|
||||||
|
if ($[2] !== arr || $[3] !== myVar) {
|
||||||
|
t2 = <Stringify cb={myVar} value={arr} shouldInvokeFns={true} />;
|
||||||
|
$[2] = arr;
|
||||||
|
$[3] = myVar;
|
||||||
|
$[4] = t2;
|
||||||
|
} else {
|
||||||
|
t2 = $[4];
|
||||||
|
}
|
||||||
|
return t2;
|
||||||
}
|
}
|
||||||
function _temp2() {
|
function _temp2() {
|
||||||
return console.log("b");
|
return console.log("b");
|
||||||
|
|||||||
Reference in New Issue
Block a user