mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:17:14 +00:00
Optimize DateTime properties
Several small pieces fit together to improve calendar arithmetic code in a customer's app. 1. Mark DateTime primitives that return small integers as returning uint31. 2. uint31 + uint31 -> uint32 in type inference. 3. uint32 / N can be generated as (uint32 / N) | 0 when N >= 2. R=floitsch@google.com Review URL: https://codereview.chromium.org//1106443003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45621 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
194d48f9da
commit
532a7948d8
|
@ -804,11 +804,17 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
|
|||
// Dart code, for example:
|
||||
// int + int -> int
|
||||
// uint31 | uint31 -> uint31
|
||||
if (name == '*' || name == '+' || name == '%' || name == 'remainder' ||
|
||||
if (name == '*' || name == '+' ||name == '%' || name == 'remainder' ||
|
||||
name == '~/') {
|
||||
if (isPositiveInt(receiver) &&
|
||||
arguments.hasOnePositionalArgumentThatMatches(isPositiveInt)) {
|
||||
return inferrer.types.positiveIntType;
|
||||
// uint31 + uint31 -> uint32
|
||||
if (name == '+' && isUInt31(receiver) &&
|
||||
arguments.hasOnePositionalArgumentThatMatches(isUInt31)) {
|
||||
return inferrer.types.uint32Type;
|
||||
} else {
|
||||
return inferrer.types.positiveIntType;
|
||||
}
|
||||
} else if (arguments.hasOnePositionalArgumentThatMatches(isInt)) {
|
||||
return inferrer.types.intType;
|
||||
} else if (arguments.hasOnePositionalArgumentThatMatches(isEmpty)) {
|
||||
|
|
|
@ -223,11 +223,16 @@ abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer {
|
|||
bool inputsArePositiveIntegers(HInstruction instruction, Compiler compiler) {
|
||||
HInstruction left = instruction.inputs[1];
|
||||
HInstruction right = instruction.inputs[2];
|
||||
JavaScriptBackend backend = compiler.backend;
|
||||
return left.isPositiveIntegerOrNull(compiler)
|
||||
&& right.isPositiveIntegerOrNull(compiler);
|
||||
}
|
||||
|
||||
bool inputsAreUInt31(HInstruction instruction, Compiler compiler) {
|
||||
HInstruction left = instruction.inputs[1];
|
||||
HInstruction right = instruction.inputs[2];
|
||||
return left.isUInt31(compiler) && right.isUInt31(compiler);
|
||||
}
|
||||
|
||||
HInstruction newBuiltinVariant(HInvokeDynamic instruction, Compiler compiler);
|
||||
|
||||
Selector renameToOptimizedSelector(String name,
|
||||
|
@ -249,6 +254,10 @@ class AddSpecializer extends BinaryArithmeticSpecializer {
|
|||
|
||||
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
|
||||
Compiler compiler) {
|
||||
if (inputsAreUInt31(instruction, compiler)) {
|
||||
JavaScriptBackend backend = compiler.backend;
|
||||
return backend.uint32Type;
|
||||
}
|
||||
if (inputsArePositiveIntegers(instruction, compiler)) {
|
||||
JavaScriptBackend backend = compiler.backend;
|
||||
return backend.positiveIntType;
|
||||
|
@ -313,6 +322,7 @@ class ModuloSpecializer extends BinaryArithmeticSpecializer {
|
|||
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
|
||||
Compiler compiler) {
|
||||
// Modulo cannot be mapped to the native operator (different semantics).
|
||||
// TODO(sra): For non-negative values we can use JavaScript's %.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -380,6 +390,14 @@ class TruncatingDivideSpecializer extends BinaryArithmeticSpecializer {
|
|||
return count != 0;
|
||||
}
|
||||
|
||||
bool isTwoOrGreater(HInstruction instruction, Compiler compiler) {
|
||||
if (!instruction.isConstantInteger()) return false;
|
||||
HConstant rightConstant = instruction;
|
||||
IntConstantValue intConstant = rightConstant.constant;
|
||||
int count = intConstant.primitiveValue;
|
||||
return count >= 2;
|
||||
}
|
||||
|
||||
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
|
||||
Compiler compiler) {
|
||||
HInstruction left = instruction.inputs[1];
|
||||
|
@ -389,6 +407,9 @@ class TruncatingDivideSpecializer extends BinaryArithmeticSpecializer {
|
|||
if (left.isUInt31(compiler)) {
|
||||
return newBuiltinVariant(instruction, compiler);
|
||||
}
|
||||
if (left.isUInt32(compiler) && isTwoOrGreater(right, compiler)) {
|
||||
return newBuiltinVariant(instruction, compiler);
|
||||
}
|
||||
// We can call _tdivFast because the rhs is a 32bit integer
|
||||
// and not 0, nor -1.
|
||||
instruction.selector = renameToOptimizedSelector(
|
||||
|
|
|
@ -1034,38 +1034,38 @@ class Primitives {
|
|||
|
||||
static getMonth(receiver) {
|
||||
return (receiver.isUtc)
|
||||
? JS('int', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver))
|
||||
: JS('int', r'#.getMonth() + 1', lazyAsJsDate(receiver));
|
||||
? JS('JSUInt31', r'#.getUTCMonth() + 1', lazyAsJsDate(receiver))
|
||||
: JS('JSUInt31', r'#.getMonth() + 1', lazyAsJsDate(receiver));
|
||||
}
|
||||
|
||||
static getDay(receiver) {
|
||||
return (receiver.isUtc)
|
||||
? JS('int', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('int', r'(#.getDate() + 0)', lazyAsJsDate(receiver));
|
||||
? JS('JSUInt31', r'(#.getUTCDate() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('JSUInt31', r'(#.getDate() + 0)', lazyAsJsDate(receiver));
|
||||
}
|
||||
|
||||
static getHours(receiver) {
|
||||
return (receiver.isUtc)
|
||||
? JS('int', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('int', r'(#.getHours() + 0)', lazyAsJsDate(receiver));
|
||||
? JS('JSUInt31', r'(#.getUTCHours() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('JSUInt31', r'(#.getHours() + 0)', lazyAsJsDate(receiver));
|
||||
}
|
||||
|
||||
static getMinutes(receiver) {
|
||||
return (receiver.isUtc)
|
||||
? JS('int', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('int', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver));
|
||||
? JS('JSUInt31', r'(#.getUTCMinutes() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('JSUInt31', r'(#.getMinutes() + 0)', lazyAsJsDate(receiver));
|
||||
}
|
||||
|
||||
static getSeconds(receiver) {
|
||||
return (receiver.isUtc)
|
||||
? JS('int', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('int', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver));
|
||||
? JS('JSUInt31', r'(#.getUTCSeconds() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('JSUInt31', r'(#.getSeconds() + 0)', lazyAsJsDate(receiver));
|
||||
}
|
||||
|
||||
static getMilliseconds(receiver) {
|
||||
return (receiver.isUtc)
|
||||
? JS('int', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('int', r'(#.getMilliseconds() + 0)', lazyAsJsDate(receiver));
|
||||
? JS('JSUInt31', r'(#.getUTCMilliseconds() + 0)', lazyAsJsDate(receiver))
|
||||
: JS('JSUInt31', r'(#.getMilliseconds() + 0)', lazyAsJsDate(receiver));
|
||||
}
|
||||
|
||||
static getWeekday(receiver) {
|
||||
|
|
|
@ -582,6 +582,20 @@ class B extends A {
|
|||
returnInt9() => super.myField;
|
||||
}
|
||||
|
||||
class C {
|
||||
var myField = 42;
|
||||
C();
|
||||
|
||||
returnInt1() => ++myField;
|
||||
returnInt2() => ++this.myField;
|
||||
returnInt3() => this.myField += 42;
|
||||
returnInt4() => myField += 42;
|
||||
operator[](index) => myField;
|
||||
operator[]= (index, value) {}
|
||||
returnInt5() => ++this[0];
|
||||
returnInt6() => this[0] += 1;
|
||||
}
|
||||
|
||||
testCascade1() {
|
||||
return [1, 2, 3]..add(4)..add(5);
|
||||
}
|
||||
|
@ -684,6 +698,13 @@ main() {
|
|||
..returnInt7()
|
||||
..returnInt8()
|
||||
..returnInt9();
|
||||
|
||||
new C()..returnInt1()
|
||||
..returnInt2()
|
||||
..returnInt3()
|
||||
..returnInt4()
|
||||
..returnInt5()
|
||||
..returnInt6();
|
||||
testReturnElementOfConstList1();
|
||||
testReturnElementOfConstList2();
|
||||
testReturnItselfOrInt(topLevelGetter());
|
||||
|
@ -727,8 +748,8 @@ void main() {
|
|||
checkReturn('returnInt2', typesTask.uint31Type);
|
||||
checkReturn('returnDouble', typesTask.doubleType);
|
||||
checkReturn('returnGiveUp', interceptorType);
|
||||
checkReturn('returnInt5', typesTask.positiveIntType);
|
||||
checkReturn('returnInt6', typesTask.positiveIntType);
|
||||
checkReturn('returnInt5', typesTask.uint32Type); // uint31+uint31->uint32
|
||||
checkReturn('returnInt6', typesTask.uint32Type); // uint31+uint31->uint32
|
||||
checkReturn('returnIntOrNull', typesTask.uint31Type.nullable());
|
||||
checkReturn('returnInt3', typesTask.uint31Type);
|
||||
checkReturn('returnDynamic', typesTask.dynamicType);
|
||||
|
@ -789,7 +810,7 @@ void main() {
|
|||
checkReturn('testContinue1', interceptorType.nullable());
|
||||
checkReturn('testBreak1', interceptorType.nullable());
|
||||
checkReturn('testContinue2', interceptorType.nullable());
|
||||
checkReturn('testBreak2', typesTask.positiveIntType.nullable());
|
||||
checkReturn('testBreak2', typesTask.uint32Type.nullable());
|
||||
checkReturn('testReturnElementOfConstList1', typesTask.uint31Type);
|
||||
checkReturn('testReturnElementOfConstList2', typesTask.uint31Type);
|
||||
checkReturn('testReturnItselfOrInt', typesTask.uint31Type);
|
||||
|
@ -808,24 +829,31 @@ void main() {
|
|||
'$className:$methodName');
|
||||
}
|
||||
|
||||
checkReturnInClass('A', 'returnInt1', typesTask.positiveIntType);
|
||||
checkReturnInClass('A', 'returnInt2', typesTask.positiveIntType);
|
||||
checkReturnInClass('A', 'returnInt3', typesTask.positiveIntType);
|
||||
checkReturnInClass('A', 'returnInt4', typesTask.positiveIntType);
|
||||
checkReturnInClass('A', 'returnInt5', typesTask.positiveIntType);
|
||||
checkReturnInClass('A', 'returnInt6', typesTask.positiveIntType);
|
||||
checkReturnInClass('A', 'returnInt1', typesTask.uint32Type);
|
||||
checkReturnInClass('A', 'returnInt2', typesTask.uint32Type);
|
||||
checkReturnInClass('A', 'returnInt3', typesTask.uint32Type);
|
||||
checkReturnInClass('A', 'returnInt4', typesTask.uint32Type);
|
||||
checkReturnInClass('A', 'returnInt5', typesTask.uint32Type);
|
||||
checkReturnInClass('A', 'returnInt6', typesTask.uint32Type);
|
||||
checkReturnInClass('A', '==', interceptorType);
|
||||
|
||||
checkReturnInClass('B', 'returnInt1', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt2', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt3', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt4', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt5', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt6', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt7', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt8', typesTask.positiveIntType);
|
||||
checkReturnInClass('B', 'returnInt1', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt2', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt3', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt4', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt5', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt6', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt7', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt8', typesTask.uint32Type);
|
||||
checkReturnInClass('B', 'returnInt9', typesTask.uint31Type);
|
||||
|
||||
checkReturnInClass('C', 'returnInt1', typesTask.positiveIntType);
|
||||
checkReturnInClass('C', 'returnInt2', typesTask.positiveIntType);
|
||||
checkReturnInClass('C', 'returnInt3', typesTask.positiveIntType);
|
||||
checkReturnInClass('C', 'returnInt4', typesTask.positiveIntType);
|
||||
checkReturnInClass('C', 'returnInt5', typesTask.positiveIntType);
|
||||
checkReturnInClass('C', 'returnInt6', typesTask.positiveIntType);
|
||||
|
||||
checkFactoryConstructor(String className, String factoryName) {
|
||||
var cls = findElement(compiler, className);
|
||||
var element = cls.localLookup(factoryName);
|
||||
|
|
94
tests/compiler/dart2js/tdiv_test.dart
Normal file
94
tests/compiler/dart2js/tdiv_test.dart
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library tdiv_test;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'compiler_helper.dart';
|
||||
|
||||
const String TEST1 = r"""
|
||||
foo(param) {
|
||||
var a = param ? 0xFFFFFFFF : 1;
|
||||
return a ~/ 2;
|
||||
// Above can be compiled to division followed by truncate.
|
||||
// present: ' / 2 | 0'
|
||||
// absent: 'tdiv'
|
||||
}
|
||||
""";
|
||||
|
||||
const String TEST2 = r"""
|
||||
foo(param) {
|
||||
var a = param ? 0xFFFFFFFF : 1;
|
||||
return a ~/ 3;
|
||||
// Above can be compiled to division followed by truncate.
|
||||
// present: ' / 3 | 0'
|
||||
// absent: 'tdiv'
|
||||
}
|
||||
""";
|
||||
|
||||
const String TEST3 = r"""
|
||||
foo(param) {
|
||||
var a = param ? 0xFFFFFFFF : -1;
|
||||
return a ~/ 2;
|
||||
// Potentially negative inputs go via fast helper.
|
||||
// present: '_tdivFast'
|
||||
// absent: '/'
|
||||
}
|
||||
""";
|
||||
|
||||
const String TEST4 = r"""
|
||||
foo(param1, param2) {
|
||||
var a = param1 ? 0xFFFFFFFF : 0;
|
||||
return a ~/ param2;
|
||||
// Unknown divisor goes via full implementation.
|
||||
// present: '$tdiv'
|
||||
// absent: '/'
|
||||
}
|
||||
""";
|
||||
|
||||
const String TEST5 = r"""
|
||||
foo(param1, param2) {
|
||||
var a = param1 ? 0xFFFFFFFF : 0;
|
||||
var b = param2 ? 3 : 4;
|
||||
return a ~/ param2;
|
||||
// We could optimize this with range analysis, but type inference summarizes
|
||||
// '3 or 4' to uint31, which is not >= 2.
|
||||
// present: '$tdiv'
|
||||
// absent: '/'
|
||||
}
|
||||
""";
|
||||
|
||||
main() {
|
||||
RegExp directivePattern = new RegExp(
|
||||
// \1 \2 \3
|
||||
r'''// *(present|absent): (?:"([^"]*)"|'([^'']*)')''',
|
||||
multiLine: true);
|
||||
|
||||
Future check(String test) {
|
||||
return compile(test, entry: 'foo', check: (String generated) {
|
||||
for (Match match in directivePattern.allMatches(test)) {
|
||||
String directive = match.group(1);
|
||||
String pattern = match.groups([2, 3]).where((s) => s != null).single;
|
||||
if (directive == 'present') {
|
||||
Expect.isTrue(generated.contains(pattern),
|
||||
"Cannot find '$pattern' in:\n$generated");
|
||||
} else {
|
||||
assert(directive == 'absent');
|
||||
Expect.isFalse(generated.contains(pattern),
|
||||
"Must not find '$pattern' in:\n$generated");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asyncTest(() => Future.wait([
|
||||
check(TEST1),
|
||||
check(TEST2),
|
||||
check(TEST3),
|
||||
check(TEST4),
|
||||
check(TEST5),
|
||||
]));
|
||||
}
|
Loading…
Reference in a new issue