mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 03:17:55 +00:00
[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:
parent
ee79d8e4b8
commit
12e0690dfe
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
Loading…
Reference in a new issue