[dart2wasm] Generate inline code for int ~/ operator

The implementation of `~/` is a bit more complicated than other
operators as it needs to handle some special cases, so we currently
don't generate inline code for it.

This causes generating indirect calls to `~/` in some cases even when
the static Dart type of the arguments are `int`s, see [1] for details.

Update the intrinsic matcher to specially handle this operator by
calling `_BoxedInt._truncDiv`, which is then inlined.

This leads to huge performance wins in [2] as typed data `length`
getters currently compiled to indirect calls to `~/`.

[1]: https://github.com/dart-lang/sdk/issues/53921
[2]: https://dart-review.googlesource.com/c/sdk/+/328920

Change-Id: I92d89db76af38b1f843e25a841074f00ff2ef30a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333381
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
This commit is contained in:
Ömer Sinan Ağacan 2023-11-02 14:46:37 +00:00 committed by Commit Queue
parent ee79d8e4b8
commit 12e0690dfe
2 changed files with 57 additions and 54 deletions

View file

@ -12,7 +12,7 @@ import 'package:kernel/ast.dart';
import 'package:wasm_builder/wasm_builder.dart' as w; import 'package:wasm_builder/wasm_builder.dart' as w;
import 'abi.dart' show kWasmAbiEnumIndex; import 'abi.dart' show kWasmAbiEnumIndex;
typedef CodeGenCallback = void Function(w.InstructionsBuilder); typedef CodeGenCallback = void Function(CodeGenerator);
/// Specialized code generation for external members. /// Specialized code generation for external members.
/// ///
@ -29,42 +29,43 @@ class Intrinsifier {
_binaryOperatorMap = { _binaryOperatorMap = {
boolType: { boolType: {
boolType: { boolType: {
'|': (b) => b.i32_or(), '|': (c) => c.b.i32_or(),
'^': (b) => b.i32_xor(), '^': (c) => c.b.i32_xor(),
'&': (b) => b.i32_and(), '&': (c) => c.b.i32_and(),
} }
}, },
intType: { intType: {
intType: { intType: {
'+': (b) => b.i64_add(), '+': (c) => c.b.i64_add(),
'-': (b) => b.i64_sub(), '-': (c) => c.b.i64_sub(),
'*': (b) => b.i64_mul(), '*': (c) => c.b.i64_mul(),
'&': (b) => b.i64_and(), '&': (c) => c.b.i64_and(),
'|': (b) => b.i64_or(), '|': (c) => c.b.i64_or(),
'^': (b) => b.i64_xor(), '^': (c) => c.b.i64_xor(),
'<': (b) => b.i64_lt_s(), '<': (c) => c.b.i64_lt_s(),
'<=': (b) => b.i64_le_s(), '<=': (c) => c.b.i64_le_s(),
'>': (b) => b.i64_gt_s(), '>': (c) => c.b.i64_gt_s(),
'>=': (b) => b.i64_ge_s(), '>=': (c) => c.b.i64_ge_s(),
'_div_s': (b) => b.i64_div_s(), '_div_s': (c) => c.b.i64_div_s(),
'_shl': (b) => b.i64_shl(), '_shl': (c) => c.b.i64_shl(),
'_shr_s': (b) => b.i64_shr_s(), '_shr_s': (c) => c.b.i64_shr_s(),
'_shr_u': (b) => b.i64_shr_u(), '_shr_u': (c) => c.b.i64_shr_u(),
'_le_u': (b) => b.i64_le_u(), '_le_u': (c) => c.b.i64_le_u(),
'_lt_u': (b) => b.i64_lt_u(), '_lt_u': (c) => c.b.i64_lt_u(),
'~/': (c) => c.call(c.translator.truncDiv.reference),
} }
}, },
doubleType: { doubleType: {
doubleType: { doubleType: {
'+': (b) => b.f64_add(), '+': (c) => c.b.f64_add(),
'-': (b) => b.f64_sub(), '-': (c) => c.b.f64_sub(),
'*': (b) => b.f64_mul(), '*': (c) => c.b.f64_mul(),
'/': (b) => b.f64_div(), '/': (c) => c.b.f64_div(),
'<': (b) => b.f64_lt(), '<': (c) => c.b.f64_lt(),
'<=': (b) => b.f64_le(), '<=': (c) => c.b.f64_le(),
'>': (b) => b.f64_gt(), '>': (c) => c.b.f64_gt(),
'>=': (b) => b.f64_ge(), '>=': (c) => c.b.f64_ge(),
'_copysign': (b) => b.f64_copysign(), '_copysign': (c) => c.b.f64_copysign(),
} }
}, },
}; };
@ -72,33 +73,33 @@ class Intrinsifier {
static final Map<w.ValueType, Map<String, CodeGenCallback>> static final Map<w.ValueType, Map<String, CodeGenCallback>>
_unaryOperatorMap = { _unaryOperatorMap = {
intType: { intType: {
'unary-': (b) { 'unary-': (c) {
b.i64_const(-1); c.b.i64_const(-1);
b.i64_mul(); c.b.i64_mul();
}, },
'~': (b) { '~': (c) {
b.i64_const(-1); c.b.i64_const(-1);
b.i64_xor(); c.b.i64_xor();
}, },
'toDouble': (b) { 'toDouble': (c) {
b.f64_convert_i64_s(); c.b.f64_convert_i64_s();
}, },
}, },
doubleType: { doubleType: {
'unary-': (b) { 'unary-': (c) {
b.f64_neg(); c.b.f64_neg();
}, },
'floorToDouble': (b) { 'floorToDouble': (c) {
b.f64_floor(); c.b.f64_floor();
}, },
'ceilToDouble': (b) { 'ceilToDouble': (c) {
b.f64_ceil(); c.b.f64_ceil();
}, },
'truncateToDouble': (b) { 'truncateToDouble': (c) {
b.f64_trunc(); c.b.f64_trunc();
}, },
'_toInt': (b) { '_toInt': (c) {
b.i64_trunc_sat_f64_s(); c.b.i64_trunc_sat_f64_s();
}, },
}, },
}; };
@ -443,7 +444,7 @@ class Intrinsifier {
w.ValueType outType = isComparison(name) ? w.NumType.i32 : leftType; w.ValueType outType = isComparison(name) ? w.NumType.i32 : leftType;
codeGen.wrap(left, leftType); codeGen.wrap(left, leftType);
codeGen.wrap(right, rightType); codeGen.wrap(right, rightType);
code(b); code(codeGen);
return outType; return outType;
} }
} else if (node.arguments.positional.isEmpty) { } else if (node.arguments.positional.isEmpty) {
@ -453,7 +454,7 @@ class Intrinsifier {
var code = _unaryOperatorMap[opType]?[name]; var code = _unaryOperatorMap[opType]?[name];
if (code != null) { if (code != null) {
codeGen.wrap(operand, opType); codeGen.wrap(operand, opType);
code(b); code(codeGen);
return _unaryResultMap[name] ?? opType; return _unaryResultMap[name] ?? opType;
} }
} }
@ -1318,7 +1319,7 @@ class Intrinsifier {
w.ValueType outputType = function.type.outputs.single; w.ValueType outputType = function.type.outputs.single;
b.local_get(function.locals[0]); b.local_get(function.locals[0]);
translator.convertType(function, inputType, intType); translator.convertType(function, inputType, intType);
code(b); code(codeGen);
translator.convertType(function, resultType, outputType); translator.convertType(function, resultType, outputType);
return true; return true;
} }
@ -1333,7 +1334,7 @@ class Intrinsifier {
b.local_get(function.locals[0]); b.local_get(function.locals[0]);
translator.convertType(function, leftType, intType); translator.convertType(function, leftType, intType);
b.local_get(function.locals[1]); b.local_get(function.locals[1]);
code(b); code(codeGen);
if (!isComparison(op)) { if (!isComparison(op)) {
translator.convertType(function, intType, outputType); translator.convertType(function, intType, outputType);
} }
@ -1355,7 +1356,7 @@ class Intrinsifier {
// Inline double op // Inline double op
CodeGenCallback doubleCode = CodeGenCallback doubleCode =
_binaryOperatorMap[doubleType]![doubleType]![op]!; _binaryOperatorMap[doubleType]![doubleType]![op]!;
doubleCode(b); doubleCode(codeGen);
if (!isComparison(op)) { if (!isComparison(op)) {
translator.convertType(function, doubleType, outputType); translator.convertType(function, doubleType, outputType);
} }
@ -1368,7 +1369,7 @@ class Intrinsifier {
b.local_get(function.locals[0]); b.local_get(function.locals[0]);
translator.convertType(function, leftType, intType); translator.convertType(function, leftType, intType);
b.local_get(rightTemp); b.local_get(rightTemp);
code(b); code(codeGen);
if (!isComparison(op)) { if (!isComparison(op)) {
translator.convertType(function, intType, outputType); translator.convertType(function, intType, outputType);
} }
@ -1389,7 +1390,7 @@ class Intrinsifier {
w.ValueType outputType = function.type.outputs.single; w.ValueType outputType = function.type.outputs.single;
b.local_get(function.locals[0]); b.local_get(function.locals[0]);
translator.convertType(function, inputType, doubleType); translator.convertType(function, inputType, doubleType);
code(b); code(codeGen);
translator.convertType(function, resultType, outputType); translator.convertType(function, resultType, outputType);
return true; return true;
} }

View file

@ -199,6 +199,8 @@ mixin KernelNodes {
index.getProcedure("dart:_string", "StringBase", "=="); index.getProcedure("dart:_string", "StringBase", "==");
late final Procedure stringInterpolate = late final Procedure stringInterpolate =
index.getProcedure("dart:_string", "StringBase", "_interpolate"); index.getProcedure("dart:_string", "StringBase", "_interpolate");
late final Procedure truncDiv =
index.getProcedure("dart:core", "_BoxedInt", "_truncDiv");
// dart:core invocation/exception procedures // dart:core invocation/exception procedures
late final Procedure invocationGetterFactory = late final Procedure invocationGetterFactory =