Skip to content
Merged
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 @@ -20,10 +20,11 @@ import {inferReactiveScopeVariables} from '../ReactiveScopes';
import {rewriteInstructionKindsBasedOnReassignment} from '../SSA';
import {inferMutableRanges} from './InferMutableRanges';
import inferReferenceEffects from './InferReferenceEffects';
import {assertExhaustive} from '../Utils/utils';
import {assertExhaustive, retainWhere} from '../Utils/utils';
import {inferMutationAliasingEffects} from './InferMutationAliasingEffects';
import {inferFunctionExpressionAliasingEffectsSignature} from './InferFunctionExpressionAliasingEffectsSignature';
import {inferMutationAliasingRanges} from './InferMutationAliasingRanges';
import {hashEffect} from './AliasingEffects';

export default function analyseFunctions(func: HIRFunction): void {
for (const [_, block] of func.body.blocks) {
Expand Down Expand Up @@ -81,6 +82,17 @@ function lowerWithMutationAliasing(fn: HIRFunction): void {
fn.aliasingEffects ??= [];
fn.aliasingEffects?.push(...effects);
}
if (fn.aliasingEffects != null) {
const seen = new Set<string>();
retainWhere(fn.aliasingEffects, effect => {
const hash = hashEffect(effect);
if (seen.has(hash)) {
return false;
}
seen.add(hash);
return true;
});
}

/**
* Phase 2: populate the Effect of each context variable to use in inferring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,11 @@ function inferBlock(
} else if (terminal.kind === 'maybe-throw') {
const handlerParam = context.catchHandlers.get(terminal.handler);
if (handlerParam != null) {
CompilerError.invariant(state.kind(handlerParam) != null, {
reason:
'Expected catch binding to be intialized with a DeclareLocal Catch instruction',
loc: terminal.loc,
});
const effects: Array<AliasingEffect> = [];
for (const instr of block.instructions) {
if (
Expand Down Expand Up @@ -476,14 +481,14 @@ function applySignature(
* Track which values we've already aliased once, so that we can switch to
* appendAlias() for subsequent aliases into the same value
*/
const aliased = new Set<IdentifierId>();
const initialized = new Set<IdentifierId>();

if (DEBUG) {
console.log(printInstruction(instruction));
}

for (const effect of signature.effects) {
applyEffect(context, state, effect, aliased, effects);
applyEffect(context, state, effect, initialized, effects);
}
if (DEBUG) {
console.log(
Expand All @@ -508,7 +513,7 @@ function applyEffect(
context: Context,
state: InferenceState,
_effect: AliasingEffect,
aliased: Set<IdentifierId>,
initialized: Set<IdentifierId>,
effects: Array<AliasingEffect>,
): void {
const effect = context.internEffect(_effect);
Expand All @@ -524,6 +529,13 @@ function applyEffect(
break;
}
case 'Create': {
CompilerError.invariant(!initialized.has(effect.into.identifier.id), {
reason: `Cannot re-initialize variable within an instruction`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Does something else also ensure that we don't have duplicate Create / CreateFroms for the same Identifier / IdentifierId across multiple instructions (i.e. StoreLocals to the same lvalue), or is that case not an issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a todo for this in InferMutationAliasingRanges. Only two fixtures fail if i enable that invariant though, for understandable reasons, so in general we are preserving this guarantee.

description: `Re-initialized ${printPlace(effect.into)} in ${printAliasingEffect(effect)}`,
loc: effect.into.loc,
});
initialized.add(effect.into.identifier.id);

let value = context.effectInstructionValueCache.get(effect);
if (value == null) {
value = {
Expand All @@ -538,6 +550,7 @@ function applyEffect(
reason: new Set([effect.reason]),
});
state.define(effect.into, value);
effects.push(effect);
break;
}
case 'ImmutableCapture': {
Expand All @@ -555,6 +568,13 @@ function applyEffect(
break;
}
case 'CreateFrom': {
CompilerError.invariant(!initialized.has(effect.into.identifier.id), {
reason: `Cannot re-initialize variable within an instruction`,
description: `Re-initialized ${printPlace(effect.into)} in ${printAliasingEffect(effect)}`,
loc: effect.into.loc,
});
initialized.add(effect.into.identifier.id);

const fromValue = state.kind(effect.from);
let value = context.effectInstructionValueCache.get(effect);
if (value == null) {
Expand All @@ -573,10 +593,21 @@ function applyEffect(
switch (fromValue.kind) {
case ValueKind.Primitive:
case ValueKind.Global: {
// no need to track this data flow
effects.push({
kind: 'Create',
value: fromValue.kind,
into: effect.into,
reason: [...fromValue.reason][0] ?? ValueReason.Other,
});
break;
}
case ValueKind.Frozen: {
effects.push({
kind: 'Create',
value: fromValue.kind,
into: effect.into,
reason: [...fromValue.reason][0] ?? ValueReason.Other,
});
applyEffect(
context,
state,
Expand All @@ -585,7 +616,7 @@ function applyEffect(
from: effect.from,
into: effect.into,
},
aliased,
initialized,
effects,
);
break;
Expand All @@ -597,6 +628,13 @@ function applyEffect(
break;
}
case 'CreateFunction': {
CompilerError.invariant(!initialized.has(effect.into.identifier.id), {
reason: `Cannot re-initialize variable within an instruction`,
description: `Re-initialized ${printPlace(effect.into)} in ${printAliasingEffect(effect)}`,
loc: effect.into.loc,
});
initialized.add(effect.into.identifier.id);

effects.push(effect);
/**
* We consider the function mutable if it has any mutable context variables or
Expand Down Expand Up @@ -653,14 +691,22 @@ function applyEffect(
from: capture,
into: effect.into,
},
aliased,
initialized,
effects,
);
}
break;
}
case 'Alias':
case 'Capture': {
CompilerError.invariant(
effect.kind === 'Capture' || initialized.has(effect.into.identifier.id),
{
reason: `Expected destination value to already be initialized within this instruction for Alias effect`,
description: `Destination ${printPlace(effect.into)} is not initialized in this instruction`,
loc: effect.into.loc,
},
);
/*
* Capture describes potential information flow: storing a pointer to one value
* within another. If the destination is not mutable, or the source value has
Expand Down Expand Up @@ -698,7 +744,7 @@ function applyEffect(
from: effect.from,
into: effect.into,
},
aliased,
initialized,
effects,
);
break;
Expand All @@ -714,6 +760,13 @@ function applyEffect(
break;
}
case 'Assign': {
CompilerError.invariant(!initialized.has(effect.into.identifier.id), {
reason: `Cannot re-initialize variable within an instruction`,
description: `Re-initialized ${printPlace(effect.into)} in ${printAliasingEffect(effect)}`,
loc: effect.into.loc,
});
initialized.add(effect.into.identifier.id);

/*
* Alias represents potential pointer aliasing. If the type is a global,
* a primitive (copy-on-write semantics) then we can prune the effect
Expand All @@ -730,7 +783,7 @@ function applyEffect(
from: effect.from,
into: effect.into,
},
aliased,
initialized,
effects,
);
let value = context.effectInstructionValueCache.get(effect);
Expand Down Expand Up @@ -768,12 +821,7 @@ function applyEffect(
break;
}
default: {
if (aliased.has(effect.into.identifier.id)) {
state.appendAlias(effect.into, effect.from);
} else {
aliased.add(effect.into.identifier.id);
state.alias(effect.into, effect.from);
}
state.assign(effect.into, effect.from);
effects.push(effect);
break;
}
Expand Down Expand Up @@ -826,11 +874,11 @@ function applyEffect(
context,
state,
{kind: 'MutateTransitiveConditionally', value: effect.function},
aliased,
initialized,
effects,
);
for (const signatureEffect of signatureEffects) {
applyEffect(context, state, signatureEffect, aliased, effects);
applyEffect(context, state, signatureEffect, initialized, effects);
}
break;
}
Expand Down Expand Up @@ -858,7 +906,7 @@ function applyEffect(
console.log('apply aliasing signature effects');
}
for (const signatureEffect of signatureEffects) {
applyEffect(context, state, signatureEffect, aliased, effects);
applyEffect(context, state, signatureEffect, initialized, effects);
}
} else if (effect.signature != null) {
if (DEBUG) {
Expand All @@ -873,7 +921,7 @@ function applyEffect(
effect.loc,
);
for (const legacyEffect of legacyEffects) {
applyEffect(context, state, legacyEffect, aliased, effects);
applyEffect(context, state, legacyEffect, initialized, effects);
}
} else {
if (DEBUG) {
Expand All @@ -888,7 +936,7 @@ function applyEffect(
value: ValueKind.Mutable,
reason: ValueReason.Other,
},
aliased,
initialized,
effects,
);
/*
Expand All @@ -911,21 +959,21 @@ function applyEffect(
kind: 'MutateTransitiveConditionally',
value: operand,
},
aliased,
initialized,
effects,
);
}
const mutateIterator =
arg.kind === 'Spread' ? conditionallyMutateIterator(operand) : null;
if (mutateIterator) {
applyEffect(context, state, mutateIterator, aliased, effects);
applyEffect(context, state, mutateIterator, initialized, effects);
}
applyEffect(
context,
state,
// OK: recording information flow
{kind: 'Alias', from: operand, into: effect.into},
aliased,
initialized,
effects,
);
for (const otherArg of [
Expand Down Expand Up @@ -953,7 +1001,7 @@ function applyEffect(
from: operand,
into: other,
},
aliased,
initialized,
effects,
);
}
Expand Down Expand Up @@ -1009,7 +1057,7 @@ function applyEffect(
suggestions: null,
},
},
aliased,
initialized,
effects,
);
}
Expand All @@ -1028,7 +1076,7 @@ function applyEffect(
suggestions: null,
},
},
aliased,
initialized,
effects,
);
} else {
Expand Down Expand Up @@ -1059,7 +1107,7 @@ function applyEffect(
suggestions: null,
},
},
aliased,
initialized,
effects,
);
}
Expand Down Expand Up @@ -1166,7 +1214,7 @@ class InferenceState {
}

// Updates the value at @param place to point to the same value as @param value.
alias(place: Place, value: Place): void {
assign(place: Place, value: Place): void {
const values = this.#variables.get(value.identifier.id);
CompilerError.invariant(values != null, {
reason: `[InferMutationAliasingEffects] Expected value for identifier to be initialized`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,9 @@ function Component({a, b, c}: {a: number; b: number; c: number}) {

return (
<>
<ValidateMemoization inputs={[a, b, c]} output={x} alwaysCheck={true} />;
<ValidateMemoization inputs={[a, b, c]} output={x} />;
{/* TODO: should only depend on c */}
<ValidateMemoization
inputs={[a, b, c]}
output={x[0]}
alwaysCheck={true}
/>
;
<ValidateMemoization inputs={[a, b, c]} output={x[0]} />;
</>
);
}
Expand Down Expand Up @@ -98,7 +93,7 @@ function Component(t0) {
}
let t3;
if ($[9] !== t2 || $[10] !== x) {
t3 = <ValidateMemoization inputs={t2} output={x} alwaysCheck={true} />;
t3 = <ValidateMemoization inputs={t2} output={x} />;
$[9] = t2;
$[10] = x;
$[11] = t3;
Expand All @@ -117,7 +112,7 @@ function Component(t0) {
}
let t5;
if ($[16] !== t4 || $[17] !== x[0]) {
t5 = <ValidateMemoization inputs={t4} output={x[0]} alwaysCheck={true} />;
t5 = <ValidateMemoization inputs={t4} output={x[0]} />;
$[16] = t4;
$[17] = x[0];
$[18] = t5;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,9 @@ function Component({a, b, c}: {a: number; b: number; c: number}) {

return (
<>
<ValidateMemoization inputs={[a, b, c]} output={x} alwaysCheck={true} />;
<ValidateMemoization inputs={[a, b, c]} output={x} />;
{/* TODO: should only depend on c */}
<ValidateMemoization
inputs={[a, b, c]}
output={x[0]}
alwaysCheck={true}
/>
;
<ValidateMemoization inputs={[a, b, c]} output={x[0]} />;
</>
);
}
Expand Down
Loading
Loading