[dart2wasm] Specialize string interpolation expressions for 1/2/3/4 arguments

This affects very common string interpolation expressions where we now
avoid creating arrays and looping over arrays and casting references
when getting things out of arrays.

e.g. `StringBuffer.write()` uses "$obj" in it's implementation
which will benefit from this.

Change-Id: Ie6615e5f76a4a8ccb4ff9aa85c05c7e39eab6f00
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371660
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2024-06-14 12:33:51 +00:00 committed by Commit Queue
parent 4b704b8f0b
commit 7078310249
3 changed files with 142 additions and 5 deletions

View file

@ -2876,11 +2876,35 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
return visitStringLiteral(expr, expectedType);
}
makeArrayFromExpressions(node.expressions,
translator.coreTypes.objectRawType(Nullability.nullable));
return translator.outputOrVoid(call(translator.options.jsCompatibility
? translator.jsStringInterpolate.reference
: translator.stringInterpolate.reference));
late final Procedure target;
final expressions = node.expressions;
// We have special cases for 1/2/3/4 arguments in non-JSCM mode.
if (!translator.options.jsCompatibility && expressions.length <= 4) {
final nullableObjectType =
translator.translateType(translator.coreTypes.objectNullableRawType);
for (final expression in expressions) {
wrap(expression, nullableObjectType);
}
if (expressions.length == 1) {
target = translator.stringInterpolate1;
} else if (expressions.length == 2) {
target = translator.stringInterpolate2;
} else if (expressions.length == 3) {
target = translator.stringInterpolate3;
} else {
assert(expressions.length == 4);
target = translator.stringInterpolate4;
}
} else {
final nullableObjectType = translator.coreTypes.objectNullableRawType;
makeArrayFromExpressions(node.expressions, nullableObjectType);
target = translator.options.jsCompatibility
? translator.jsStringInterpolate
: translator.stringInterpolate;
}
return translator.outputOrVoid(call(target.reference));
}
@override

View file

@ -240,6 +240,14 @@ mixin KernelNodes {
index.getProcedure("dart:_string", "StringBase", "_equals");
late final Procedure stringInterpolate =
index.getProcedure("dart:_string", "StringBase", "_interpolate");
late final Procedure stringInterpolate1 =
index.getProcedure("dart:_string", "StringBase", "_interpolate1");
late final Procedure stringInterpolate2 =
index.getProcedure("dart:_string", "StringBase", "_interpolate2");
late final Procedure stringInterpolate3 =
index.getProcedure("dart:_string", "StringBase", "_interpolate3");
late final Procedure stringInterpolate4 =
index.getProcedure("dart:_string", "StringBase", "_interpolate4");
late final Procedure truncDiv =
index.getProcedure("dart:core", "_BoxedInt", "_truncDiv");
late final Procedure runtimeTypeEquals =

View file

@ -918,6 +918,53 @@ abstract final class StringBase extends WasmStringBase {
return StringBase._concatAllFallback(values, totalLength);
}
@pragma("wasm:entry-point", "call")
static String _interpolate1(Object? value) {
return value is String ? value : value.toString();
}
@pragma("wasm:entry-point", "call")
static String _interpolate2(Object? value1, Object? value2) {
final String string1 = value1 is String ? value1 : value1.toString();
final String string2 = value2 is String ? value2 : value2.toString();
if (string1 is OneByteString && string2 is OneByteString) {
return OneByteString._concat2(string1, string2);
}
return StringBase._interpolate(
WasmArray<Object?>.literal([string1, string2]));
}
@pragma("wasm:entry-point", "call")
static String _interpolate3(Object? value1, Object? value2, Object? value3) {
final String string1 = value1 is String ? value1 : value1.toString();
final String string2 = value2 is String ? value2 : value2.toString();
final String string3 = value3 is String ? value3 : value3.toString();
if (string1 is OneByteString &&
string2 is OneByteString &&
string3 is OneByteString) {
return OneByteString._concat3(string1, string2, string3);
}
return StringBase._interpolate(
WasmArray<Object?>.literal([string1, string2, string3]));
}
@pragma("wasm:entry-point", "call")
static String _interpolate4(
Object? value1, Object? value2, Object? value3, Object? value4) {
final String string1 = value1 is String ? value1 : value1.toString();
final String string2 = value2 is String ? value2 : value2.toString();
final String string3 = value3 is String ? value3 : value3.toString();
final String string4 = value4 is String ? value4 : value4.toString();
if (string1 is OneByteString &&
string2 is OneByteString &&
string3 is OneByteString &&
string4 is OneByteString) {
return OneByteString._concat4(string1, string2, string3, string4);
}
return StringBase._interpolate(
WasmArray<Object?>.literal([string1, string2, string3, string4]));
}
@pragma('wasm:entry-point')
static bool _equals(String left, String? right) {
return left == right;
@ -1154,6 +1201,64 @@ final class OneByteString extends StringBase {
return result;
}
static OneByteString _concat2(OneByteString string1, OneByteString string2) {
final bytes1 = string1._array;
final bytes2 = string2._array;
final result = OneByteString.withLength(bytes1.length + bytes2.length);
final resultBytes = result._array;
int resultOffset = 0;
resultBytes.copy(resultOffset, bytes1, 0, bytes1.length);
resultOffset += bytes1.length;
resultBytes.copy(resultOffset, bytes2, 0, bytes2.length);
return result;
}
static OneByteString _concat3(
OneByteString string1, OneByteString string2, OneByteString string3) {
final bytes1 = string1._array;
final bytes2 = string2._array;
final bytes3 = string3._array;
final result =
OneByteString.withLength(bytes1.length + bytes2.length + bytes3.length);
final resultBytes = result._array;
int resultOffset = 0;
resultBytes.copy(resultOffset, bytes1, 0, bytes1.length);
resultOffset += bytes1.length;
resultBytes.copy(resultOffset, bytes2, 0, bytes2.length);
resultOffset += bytes2.length;
resultBytes.copy(resultOffset, bytes3, 0, bytes3.length);
return result;
}
static OneByteString _concat4(OneByteString string1, OneByteString string2,
OneByteString string3, OneByteString string4) {
final bytes1 = string1._array;
final bytes2 = string2._array;
final bytes3 = string3._array;
final bytes4 = string4._array;
final result = OneByteString.withLength(
bytes1.length + bytes2.length + bytes3.length + bytes4.length);
final resultBytes = result._array;
int resultOffset = 0;
resultBytes.copy(resultOffset, bytes1, 0, bytes1.length);
resultOffset += bytes1.length;
resultBytes.copy(resultOffset, bytes2, 0, bytes2.length);
resultOffset += bytes2.length;
resultBytes.copy(resultOffset, bytes3, 0, bytes3.length);
resultOffset += bytes3.length;
resultBytes.copy(resultOffset, bytes4, 0, bytes4.length);
return result;
}
// All element of 'strings' must be OneByteStrings.
static OneByteString _concatRange(
WasmArray<String> strings, int start, int end, int totalLength) {