Skip to content

Commit 21f7ffc

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Revert switch-case optimizations that introduced a bug.
This reverts commit b654350 This reverts commit 2254755 The switch-case optimizations introduced a bug that is surfaced when running the dart2wasm self-compilation test in [0]. With that test running ``` % python3 tools/test.py -n unittest-mac pkg/dart2wasm/test/self_compile_test ``` will fail with ``` ... Exception in StaticInvocation at file:///FakeSdkRoot/sdk/lib/_internal/wasm/lib/boxed_double.dart:346:29 Bad state: Unhandled WasmArray intrinsic: StaticIntrinsic.wasmArrayIndex at module0.Error._throwWithCurrentStackTrace (wasm://wasm/module0-0121906a:wasm-function[160]:0xd0d76) at module0.AstCodeGenerator.visitStaticInvocation (checked entry) (wasm://wasm/module0-0121906a:wasm-function[6304]:0x1668e5) at module0._TreeVisitor1Default&Object&TreeVisitor1DefaultMixin&ExpressionVisitor1DefaultMixin.visitStaticInvocation (checked entry) (wasm://wasm/module0-0121906a:wasm-function[6306]:0x167b61) at module0.StaticInvocation.accept1 (wasm://wasm/module0-0121906a:wasm-function[6297]:0x165f5f) at module0.AstCodeGenerator.translateExpression (wasm://wasm/module0-0121906a:wasm-function[1742]:0xfcd1b) at module0.AstCodeGenerator.visitEqualsCall (checked entry) (wasm://wasm/module0-0121906a:wasm-function[6960]:0x1777e4) at module0.EqualsCall.accept1 (wasm://wasm/module0-0121906a:wasm-function[6944]:0x1773b4) at module0.AstCodeGenerator.translateExpression (wasm://wasm/module0-0121906a:wasm-function[1742]:0xfcd1b) /Users/kustermann/src/dart-sdk/sdk/pkg/dart2wasm/bin/run_wasm.js:346: [object WebAssembly.Exception] ``` The switch-case in the intrinsifier seems to be miscompiled. Change-Id: I3bfe8887fa133573379c32d52e15769a4e6db43e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/443082 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Ömer Ağacan <[email protected]>
1 parent bd0fa88 commit 21f7ffc

File tree

2 files changed

+20
-178
lines changed

2 files changed

+20
-178
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 20 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,44 +1424,29 @@ abstract class AstCodeGenerator
14241424
b.end();
14251425
}
14261426

1427-
final brTable = switchInfo.brTable;
1428-
if (brTable != null) {
1429-
// Map each entry in the range to the appropriate jump table entry.
1430-
final indexBlocks = <w.Label>[];
1431-
final caseMap = brTable.caseMap;
1432-
final defaultLabel =
1433-
defaultCase != null ? switchLabels[defaultCase]! : doneLabel;
1434-
for (int i = 0; i < brTable.rangeSize; i++) {
1435-
final c = caseMap[i];
1436-
indexBlocks.add(c == null ? defaultLabel : switchLabels[c]!);
1437-
}
1438-
brTable.brTableExpr(switchValueNonNullableLocal);
1439-
b.br_table(indexBlocks, defaultLabel);
1440-
} else {
1441-
// Compare against all case values
1442-
for (SwitchCase c in node.cases) {
1443-
for (Expression exp in c.expressions) {
1444-
if (exp is NullLiteral ||
1445-
exp is ConstantExpression && exp.constant is NullConstant) {
1446-
// Null already checked, skip
1447-
} else {
1448-
switchInfo.compare(
1449-
switchValueNonNullableLocal,
1450-
() => translateExpression(exp, switchInfo.nonNullableType),
1451-
);
1452-
b.br_if(switchLabels[c]!);
1453-
}
1427+
// Compare against all case values
1428+
for (SwitchCase c in node.cases) {
1429+
for (Expression exp in c.expressions) {
1430+
if (exp is NullLiteral ||
1431+
exp is ConstantExpression && exp.constant is NullConstant) {
1432+
// Null already checked, skip
1433+
} else {
1434+
switchInfo.compare(
1435+
switchValueNonNullableLocal,
1436+
() => translateExpression(exp, switchInfo.nonNullableType),
1437+
);
1438+
b.br_if(switchLabels[c]!);
14541439
}
14551440
}
1441+
}
14561442

1457-
// No explicit cases matched
1458-
if (node.isExplicitlyExhaustive) {
1459-
b.unreachable();
1460-
} else {
1461-
w.Label defaultLabel =
1462-
defaultCase != null ? switchLabels[defaultCase]! : doneLabel;
1463-
b.br(defaultLabel);
1464-
}
1443+
// No explicit cases matched
1444+
if (node.isExplicitlyExhaustive) {
1445+
b.unreachable();
1446+
} else {
1447+
w.Label defaultLabel =
1448+
defaultCase != null ? switchLabels[defaultCase]! : doneLabel;
1449+
b.br(defaultLabel);
14651450
}
14661451

14671452
// Emit case bodies
@@ -4312,47 +4297,6 @@ class SwitchBackwardJumpInfo {
43124297
: defaultLoopLabel = null;
43134298
}
43144299

4315-
/// Info needed to represent a switch statement using a br_table instruction.
4316-
///
4317-
/// This is used for switches on integers and enums (using their indicies). We
4318-
/// map each option to an index in the jump table and then jump directly to the
4319-
/// target case. This is much faster than iteratively comparing each cases's
4320-
/// expression to the switch expression.
4321-
///
4322-
/// Sometimes switches over ranges of ints are sparse enough that a table would
4323-
/// bloat the code compared to the iterative comparison.
4324-
class BrTableInfo {
4325-
// This is the minimum ratio of br_table indicies to actual mapped cases. If
4326-
// this gets too large then most of the br_table indicies are unmapped and the
4327-
// extra code size is not worth using a br_table.
4328-
static const double _minTableSparseness = 2;
4329-
// This is the maximum size where it's always worth it to use a br_table.
4330-
// If the br_table is bigger than this then we start to check sparseness.
4331-
// Below this point we always accept the potential code size hit.
4332-
static const int _maxSparseSize = 50;
4333-
4334-
final int rangeSize;
4335-
final void Function(w.Local switchExprLocal) brTableExpr;
4336-
final Map<int, SwitchCase> caseMap;
4337-
4338-
BrTableInfo._(this.rangeSize, this.caseMap, this.brTableExpr)
4339-
: assert(!isTooSparse(rangeSize, caseMap));
4340-
4341-
/// Heuristically validate whether the provided table would be too sparse and
4342-
/// if so return null. Otherwise return the expected table.
4343-
static BrTableInfo? build(int rangeSize, Map<int, SwitchCase> caseMap,
4344-
void Function(w.Local switchExprLocal) brTableExpr) {
4345-
// Validate the table density and size is worth putting into a br_table.
4346-
if (isTooSparse(rangeSize, caseMap)) return null;
4347-
4348-
return BrTableInfo._(rangeSize, caseMap, brTableExpr);
4349-
}
4350-
4351-
static bool isTooSparse(int rangeSize, Map<int, SwitchCase> caseMap) =>
4352-
(rangeSize / caseMap.length) > _minTableSparseness &&
4353-
rangeSize > _maxSparseSize;
4354-
}
4355-
43564300
class SwitchInfo {
43574301
/// Non-nullable Wasm type of the `switch` expression. Used when the
43584302
/// expression is not nullable, and after the null check.
@@ -4379,11 +4323,6 @@ class SwitchInfo {
43794323
/// The `null: ...` case, if exists.
43804324
late final SwitchCase? nullCase;
43814325

4382-
/// Info needed to compile this switch statement into a wasm br_table. If null
4383-
/// this switch statement should not use a br_table and should use comparison
4384-
/// based case matching instead.
4385-
BrTableInfo? brTable;
4386-
43874326
SwitchInfo(AstCodeGenerator codeGen, SwitchStatement node) {
43884327
final translator = codeGen.translator;
43894328

@@ -4514,39 +4453,6 @@ class SwitchInfo {
45144453
nonNullableType = w.NumType.i64;
45154454
nullableType =
45164455
translator.classInfo[translator.boxedIntClass]!.nullableType;
4517-
4518-
// Calculate the range covered by the cases and create the jump table.
4519-
int? minValue;
4520-
int? maxValue;
4521-
Map<int, SwitchCase> caseMap = {};
4522-
for (final c in node.cases) {
4523-
for (final e in c.expressions) {
4524-
final value = e is IntLiteral
4525-
? e.value
4526-
: ((e as ConstantExpression).constant as IntConstant).value;
4527-
caseMap[value] = c;
4528-
if (minValue == null || value < minValue) minValue = value;
4529-
if (maxValue == null || value > maxValue) maxValue = value;
4530-
}
4531-
}
4532-
if (maxValue != null) {
4533-
final range = maxValue - minValue! + 1;
4534-
if (minValue != 0) {
4535-
caseMap = caseMap.map((i, c) => MapEntry(i - minValue!, c));
4536-
}
4537-
brTable = BrTableInfo.build(range, caseMap, (switchExprLocal) {
4538-
codeGen.b.local_get(switchExprLocal);
4539-
// Normalize the value on 0 if necessary.
4540-
if (minValue != 0) {
4541-
codeGen.b.i64_const(minValue!);
4542-
codeGen.b.i64_sub();
4543-
}
4544-
// Now that we've normalized to 0 it should be safe to switch to i32.
4545-
codeGen.b.i32_wrap_i64();
4546-
});
4547-
}
4548-
4549-
// Provide a compare as a fallback in case the range is too sparse.
45504456
compare = (switchExprLocal, pushCaseExpr) {
45514457
codeGen.b.local_get(switchExprLocal);
45524458
pushCaseExpr();
@@ -4561,68 +4467,6 @@ class SwitchInfo {
45614467
pushCaseExpr();
45624468
codeGen.call(translator.jsStringEquals.reference);
45634469
};
4564-
} else if (switchExprClass.isEnum) {
4565-
// If this is an applicable switch over enums, create a jump table.
4566-
bool isValid = true;
4567-
final caseMap = <int, SwitchCase>{};
4568-
int? minIndex;
4569-
int maxIndex = 0;
4570-
outer:
4571-
for (final c in node.cases) {
4572-
for (final e in c.expressions) {
4573-
if (e is! ConstantExpression) {
4574-
isValid = false;
4575-
break outer;
4576-
}
4577-
final constant = e.constant;
4578-
if (constant is! InstanceConstant) {
4579-
isValid = false;
4580-
break outer;
4581-
}
4582-
if (constant.classNode != switchExprClass) {
4583-
isValid = false;
4584-
break outer;
4585-
}
4586-
final enumIndex =
4587-
(constant.fieldValues[translator.enumIndexField.fieldReference]
4588-
as IntConstant)
4589-
.value;
4590-
caseMap[enumIndex] = c;
4591-
if (enumIndex > maxIndex) maxIndex = enumIndex;
4592-
if (minIndex == null || enumIndex < minIndex) minIndex = enumIndex;
4593-
}
4594-
}
4595-
4596-
if (isValid && minIndex != null) {
4597-
final range = maxIndex - minIndex + 1;
4598-
brTable = BrTableInfo.build(range, caseMap, (switchExprLocal) {
4599-
codeGen.b.local_get(switchExprLocal);
4600-
codeGen.call(translator.enumIndexField.getterReference);
4601-
if (minIndex != 0) {
4602-
codeGen.b.i64_const(minIndex!);
4603-
codeGen.b.i64_sub();
4604-
}
4605-
// Now that we've normalized to 0 it should be safe to switch to i32.
4606-
codeGen.b.i32_wrap_i64();
4607-
});
4608-
}
4609-
4610-
if (brTable == null) {
4611-
// Object identity switch
4612-
nonNullableType = translator.topTypeNonNullable;
4613-
nullableType = translator.topType;
4614-
} else {
4615-
nonNullableType =
4616-
translator.classInfo[switchExprClass]!.nonNullableType;
4617-
nullableType = translator.classInfo[switchExprClass]!.nullableType;
4618-
}
4619-
4620-
// Set compare anyway for state machine handling
4621-
compare = (switchExprLocal, pushCaseExpr) {
4622-
codeGen.b.local_get(switchExprLocal);
4623-
pushCaseExpr();
4624-
codeGen.call(translator.coreTypes.identicalProcedure.reference);
4625-
};
46264470
} else {
46274471
// Object identity switch
46284472
nonNullableType = translator.topTypeNonNullable;

pkg/dart2wasm/lib/kernel_nodes.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ mixin KernelNodes {
5757
late final Class typeErrorClass = index.getClass("dart:core", "_TypeError");
5858
late final Class javaScriptErrorClass =
5959
index.getClass("dart:core", "_JavaScriptError");
60-
late final Field enumIndexField =
61-
index.getField('dart:core', '_Enum', 'index');
6260

6361
// dart:core runtime type classes
6462
late final Class typeClass = index.getClass("dart:core", "_Type");

0 commit comments

Comments
 (0)