blob: 9fb3e199559cbcf28f756de9b25cb6bca39a1d44 [file] [log] [blame]
// Copyright (c) 2013, 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.
part of ssa;
/**
* [InvokeDynamicSpecializer] and its subclasses are helpers to
* optimize intercepted dynamic calls. It knows what input types
* would be beneficial for performance, and how to change a invoke
* dynamic to a builtin instruction (e.g. HIndex, HBitNot).
*/
class InvokeDynamicSpecializer {
const InvokeDynamicSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
Selector selector = instruction.selector;
return TypeMaskFactory.inferredTypeForSelector(selector, compiler);
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
return null;
}
Operation operation(ConstantSystem constantSystem) => null;
static InvokeDynamicSpecializer lookupSpecializer(Selector selector) {
if (selector.kind == SelectorKind.INDEX) {
return selector.name == '[]'
? const IndexSpecializer()
: const IndexAssignSpecializer();
} else if (selector.kind == SelectorKind.OPERATOR) {
if (selector.name == 'unary-') {
return const UnaryNegateSpecializer();
} else if (selector.name == '~') {
return const BitNotSpecializer();
} else if (selector.name == '+') {
return const AddSpecializer();
} else if (selector.name == '-') {
return const SubtractSpecializer();
} else if (selector.name == '*') {
return const MultiplySpecializer();
} else if (selector.name == '/') {
return const DivideSpecializer();
} else if (selector.name == '~/') {
return const TruncatingDivideSpecializer();
} else if (selector.name == '%') {
return const ModuloSpecializer();
} else if (selector.name == '>>') {
return const ShiftRightSpecializer();
} else if (selector.name == '<<') {
return const ShiftLeftSpecializer();
} else if (selector.name == '&') {
return const BitAndSpecializer();
} else if (selector.name == '|') {
return const BitOrSpecializer();
} else if (selector.name == '^') {
return const BitXorSpecializer();
} else if (selector.name == '==') {
return const EqualsSpecializer();
} else if (selector.name == '<') {
return const LessSpecializer();
} else if (selector.name == '<=') {
return const LessEqualSpecializer();
} else if (selector.name == '>') {
return const GreaterSpecializer();
} else if (selector.name == '>=') {
return const GreaterEqualSpecializer();
}
}
return const InvokeDynamicSpecializer();
}
}
class IndexAssignSpecializer extends InvokeDynamicSpecializer {
const IndexAssignSpecializer();
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
if (instruction.inputs[1].isMutableIndexable(compiler)) {
if (!instruction.inputs[2].isInteger(compiler)
&& compiler.enableTypeAssertions) {
// We want the right checked mode error.
return null;
}
return new HIndexAssign(instruction.inputs[1],
instruction.inputs[2],
instruction.inputs[3],
instruction.selector);
}
return null;
}
}
class IndexSpecializer extends InvokeDynamicSpecializer {
const IndexSpecializer();
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
if (!instruction.inputs[1].isIndexablePrimitive(compiler)) return null;
if (!instruction.inputs[2].isInteger(compiler)
&& compiler.enableTypeAssertions) {
// We want the right checked mode error.
return null;
}
TypeMask receiverType =
instruction.getDartReceiver(compiler).instructionType;
Selector refined = new TypedSelector(receiverType, instruction.selector);
TypeMask type = TypeMaskFactory.inferredTypeForSelector(refined, compiler);
return new HIndex(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, type);
}
}
class BitNotSpecializer extends InvokeDynamicSpecializer {
const BitNotSpecializer();
UnaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.bitNot;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
// All bitwise operations on primitive types either produce an
// integer or throw an error.
JavaScriptBackend backend = compiler.backend;
if (instruction.inputs[1].isPrimitiveOrNull(compiler)) {
return backend.uint32Type;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
HInstruction input = instruction.inputs[1];
if (input.isNumber(compiler)) {
return new HBitNot(input, instruction.selector,
computeTypeFromInputTypes(instruction, compiler));
}
return null;
}
}
class UnaryNegateSpecializer extends InvokeDynamicSpecializer {
const UnaryNegateSpecializer();
UnaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.negate;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
TypeMask operandType = instruction.inputs[1].instructionType;
if (instruction.inputs[1].isNumberOrNull(compiler)) return operandType;
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction input = instruction.inputs[1];
if (input.isNumber(compiler)) {
return new HNegate(input, instruction.selector, input.instructionType);
}
return null;
}
}
abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer {
const BinaryArithmeticSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
JavaScriptBackend backend = compiler.backend;
if (left.isIntegerOrNull(compiler) && right.isIntegerOrNull(compiler)) {
return backend.intType;
}
if (left.isNumberOrNull(compiler)) {
if (left.isDoubleOrNull(compiler) || right.isDoubleOrNull(compiler)) {
return backend.doubleType;
}
return backend.numType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
bool isBuiltin(HInvokeDynamic instruction, Compiler compiler) {
return instruction.inputs[1].isNumber(compiler)
&& instruction.inputs[2].isNumber(compiler);
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
if (isBuiltin(instruction, compiler)) {
HInstruction builtin = newBuiltinVariant(instruction, compiler);
if (builtin != null) return builtin;
// Even if there is no builtin equivalent instruction, we know
// the instruction does not have any side effect, and that it
// can be GVN'ed.
clearAllSideEffects(instruction);
}
return null;
}
void clearAllSideEffects(HInstruction instruction) {
instruction.sideEffects.clearAllSideEffects();
instruction.sideEffects.clearAllDependencies();
instruction.setUseGvn();
}
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);
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction, Compiler compiler);
Selector renameToOptimizedSelector(String name,
Selector selector,
Compiler compiler) {
if (selector.name == name) return selector;
return new TypedSelector(
selector.mask,
new Selector(SelectorKind.CALL,
name,
compiler.interceptorsLibrary,
selector.argumentCount));
}
}
class AddSpecializer extends BinaryArithmeticSpecializer {
const AddSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
if (inputsArePositiveIntegers(instruction, compiler)) {
JavaScriptBackend backend = compiler.backend;
return backend.positiveIntType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.add;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
return new HAdd(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
class DivideSpecializer extends BinaryArithmeticSpecializer {
const DivideSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.divide;
}
TypeMask computeTypeFromInputTypes(HInstruction instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
JavaScriptBackend backend = compiler.backend;
if (left.isNumberOrNull(compiler)) {
return backend.doubleType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HDivide(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, backend.doubleType);
}
}
class ModuloSpecializer extends BinaryArithmeticSpecializer {
const ModuloSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
if (inputsArePositiveIntegers(instruction, compiler)) {
JavaScriptBackend backend = compiler.backend;
return backend.positiveIntType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.modulo;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
// Modulo cannot be mapped to the native operator (different semantics).
return null;
}
}
class MultiplySpecializer extends BinaryArithmeticSpecializer {
const MultiplySpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.multiply;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
if (inputsArePositiveIntegers(instruction, compiler)) {
JavaScriptBackend backend = compiler.backend;
return backend.positiveIntType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
return new HMultiply(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
class SubtractSpecializer extends BinaryArithmeticSpecializer {
const SubtractSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.subtract;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
return new HSubtract(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
class TruncatingDivideSpecializer extends BinaryArithmeticSpecializer {
const TruncatingDivideSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.truncatingDivide;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
if (inputsArePositiveIntegers(instruction, compiler)) {
JavaScriptBackend backend = compiler.backend;
return backend.positiveIntType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
bool isNotZero(HInstruction instruction, Compiler compiler) {
if (!instruction.isConstantInteger()) return false;
HConstant rightConstant = instruction;
IntConstant intConstant = rightConstant.constant;
int count = intConstant.value;
return count != 0;
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
if (isBuiltin(instruction, compiler)) {
if (right.isPositiveInteger(compiler) && isNotZero(right, compiler)) {
if (left.isUInt31(compiler)) {
return newBuiltinVariant(instruction, compiler);
}
// We can call _tdivFast because the rhs is a 32bit integer
// and not 0, nor -1.
instruction.selector = renameToOptimizedSelector(
'_tdivFast', instruction.selector, compiler);
}
clearAllSideEffects(instruction);
}
return null;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
return new HTruncatingDivide(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
abstract class BinaryBitOpSpecializer extends BinaryArithmeticSpecializer {
const BinaryBitOpSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
// All bitwise operations on primitive types either produce an
// integer or throw an error.
HInstruction left = instruction.inputs[1];
JavaScriptBackend backend = compiler.backend;
if (left.isPrimitiveOrNull(compiler)) {
return backend.uint32Type;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
bool argumentLessThan32(HInstruction instruction) {
if (!instruction.isConstantInteger()) return false;
HConstant rightConstant = instruction;
IntConstant intConstant = rightConstant.constant;
int count = intConstant.value;
return count >= 0 && count <= 31;
}
bool isPositive(HInstruction instruction, Compiler compiler) {
// TODO: We should use the value range analysis. Currently, ranges
// are discarded just after the analysis.
return instruction.isPositiveInteger(compiler);
}
}
class ShiftLeftSpecializer extends BinaryBitOpSpecializer {
const ShiftLeftSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.shiftLeft;
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
if (left.isNumber(compiler)) {
if (argumentLessThan32(right)) {
return newBuiltinVariant(instruction, compiler);
}
// Even if there is no builtin equivalent instruction, we know
// the instruction does not have any side effect, and that it
// can be GVN'ed.
clearAllSideEffects(instruction);
Selector selector = instruction.selector;
if (isPositive(right, compiler)) {
instruction.selector = renameToOptimizedSelector(
'_shlPositive', instruction.selector, compiler);
}
}
return null;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HShiftLeft(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
class ShiftRightSpecializer extends BinaryBitOpSpecializer {
const ShiftRightSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
JavaScriptBackend backend = compiler.backend;
if (left.isUInt32(compiler)) return left.instructionType;
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
if (left.isNumber(compiler)) {
if (argumentLessThan32(right) && isPositive(left, compiler)) {
return newBuiltinVariant(instruction, compiler);
}
// Even if there is no builtin equivalent instruction, we know
// the instruction does not have any side effect, and that it
// can be GVN'ed.
clearAllSideEffects(instruction);
if (isPositive(right, compiler) && isPositive(left, compiler)) {
instruction.selector = renameToOptimizedSelector(
'_shrBothPositive', instruction.selector, compiler);
} else if (isPositive(left, compiler) && right.isNumber(compiler)) {
instruction.selector = renameToOptimizedSelector(
'_shrReceiverPositive', instruction.selector, compiler);
} else if (isPositive(right, compiler)) {
instruction.selector = renameToOptimizedSelector(
'_shrOtherPositive', instruction.selector, compiler);
}
}
return null;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HShiftRight(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.shiftRight;
}
}
class BitOrSpecializer extends BinaryBitOpSpecializer {
const BitOrSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.bitOr;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
JavaScriptBackend backend = compiler.backend;
if (left.isUInt31(compiler) && right.isUInt31(compiler)) {
return backend.uint31Type;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HBitOr(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
class BitAndSpecializer extends BinaryBitOpSpecializer {
const BitAndSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.bitAnd;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
JavaScriptBackend backend = compiler.backend;
if (left.isUInt31(compiler) || right.isUInt31(compiler)) {
return backend.uint31Type;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HBitAnd(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
class BitXorSpecializer extends BinaryBitOpSpecializer {
const BitXorSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.bitXor;
}
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
JavaScriptBackend backend = compiler.backend;
if (left.isUInt31(compiler) && right.isUInt31(compiler)) {
return backend.uint31Type;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HBitXor(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, computeTypeFromInputTypes(instruction, compiler));
}
}
abstract class RelationalSpecializer extends InvokeDynamicSpecializer {
const RelationalSpecializer();
TypeMask computeTypeFromInputTypes(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
if (instruction.inputs[1].isPrimitiveOrNull(compiler)) {
return backend.boolType;
}
return super.computeTypeFromInputTypes(instruction, compiler);
}
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
if (left.isNumber(compiler) && right.isNumber(compiler)) {
return newBuiltinVariant(instruction, compiler);
}
return null;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction, Compiler compiler);
}
class EqualsSpecializer extends RelationalSpecializer {
const EqualsSpecializer();
HInstruction tryConvertToBuiltin(HInvokeDynamic instruction,
Compiler compiler) {
HInstruction left = instruction.inputs[1];
HInstruction right = instruction.inputs[2];
TypeMask instructionType = left.instructionType;
if (right.isConstantNull() || left.isPrimitiveOrNull(compiler)) {
return newBuiltinVariant(instruction, compiler);
}
Selector selector =
new TypedSelector(instructionType, instruction.selector);
World world = compiler.world;
JavaScriptBackend backend = compiler.backend;
Iterable<Element> matches = world.allFunctions.filter(selector);
// This test relies the on `Object.==` and `Interceptor.==` always being
// implemented because if the selector matches by subtype, it still will be
// a regular object or an interceptor.
if (matches.every(backend.isDefaultEqualityImplementation)) {
return newBuiltinVariant(instruction, compiler);
}
return null;
}
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.equal;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HIdentity(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, backend.boolType);
}
}
class LessSpecializer extends RelationalSpecializer {
const LessSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.less;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HLess(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, backend.boolType);
}
}
class GreaterSpecializer extends RelationalSpecializer {
const GreaterSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.greater;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HGreater(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, backend.boolType);
}
}
class GreaterEqualSpecializer extends RelationalSpecializer {
const GreaterEqualSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.greaterEqual;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HGreaterEqual(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, backend.boolType);
}
}
class LessEqualSpecializer extends RelationalSpecializer {
const LessEqualSpecializer();
BinaryOperation operation(ConstantSystem constantSystem) {
return constantSystem.lessEqual;
}
HInstruction newBuiltinVariant(HInvokeDynamic instruction,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return new HLessEqual(
instruction.inputs[1], instruction.inputs[2],
instruction.selector, backend.boolType);
}
}