}
-HConstant* HConstant::CopyToTruncatedInt32(Zone* zone) const {
+Maybe<HConstant*> HConstant::CopyToTruncatedInt32(Zone* zone) {
+ HConstant* res = NULL;
if (has_int32_value_) {
- return new(zone) HConstant(int32_value_,
- Representation::Integer32(),
- is_not_in_new_space_,
- handle_);
+ res = new(zone) HConstant(int32_value_,
+ Representation::Integer32(),
+ is_not_in_new_space_,
+ handle_);
+ } else if (has_double_value_) {
+ res = new(zone) HConstant(DoubleToInt32(double_value_),
+ Representation::Integer32(),
+ is_not_in_new_space_,
+ handle_);
+ } else {
+ ASSERT(!HasNumberValue());
+ Maybe<HConstant*> number = CopyToTruncatedNumber(zone);
+ if (number.has_value) return number.value->CopyToTruncatedInt32(zone);
}
- if (has_double_value_) {
- return new(zone) HConstant(DoubleToInt32(double_value_),
- Representation::Integer32(),
- is_not_in_new_space_,
- handle_);
+ return Maybe<HConstant*>(res != NULL, res);
+}
+
+
+Maybe<HConstant*> HConstant::CopyToTruncatedNumber(Zone* zone) {
+ HConstant* res = NULL;
+ if (handle()->IsBoolean()) {
+ res = handle()->BooleanValue() ?
+ new(zone) HConstant(1) : new(zone) HConstant(0);
+ } else if (handle()->IsUndefined()) {
+ res = new(zone) HConstant(OS::nan_value());
+ } else if (handle()->IsNull()) {
+ res = new(zone) HConstant(0);
}
- return NULL;
+ return Maybe<HConstant*>(res != NULL, res);
}
return ((type_ & kString) == kString);
}
+ bool IsNonString() const {
+ return IsTaggedPrimitive() || IsSmi() || IsHeapNumber() ||
+ IsBoolean() || IsJSArray();
+ }
+
bool IsBoolean() const {
ASSERT(type_ != kUninitialized);
return ((type_ & kBoolean) == kBoolean);
virtual HType CalculateInferredType();
bool IsInteger() { return handle()->IsSmi(); }
HConstant* CopyToRepresentation(Representation r, Zone* zone) const;
- HConstant* CopyToTruncatedInt32(Zone* zone) const;
+ Maybe<HConstant*> CopyToTruncatedInt32(Zone* zone);
+ Maybe<HConstant*> CopyToTruncatedNumber(Zone* zone);
bool HasInteger32Value() const { return has_int32_value_; }
int32_t Integer32Value() const {
ASSERT(HasInteger32Value());
if (value->IsConstant()) {
HConstant* constant = HConstant::cast(value);
// Try to create a new copy of the constant with the new representation.
- new_value = (is_truncating && to.IsInteger32())
- ? constant->CopyToTruncatedInt32(graph()->zone())
- : constant->CopyToRepresentation(to, graph()->zone());
+ if (is_truncating && to.IsInteger32()) {
+ Maybe<HConstant*> res = constant->CopyToTruncatedInt32(graph()->zone());
+ if (res.has_value) new_value = res.value;
+ } else {
+ new_value = constant->CopyToRepresentation(to, graph()->zone());
+ }
}
if (new_value == NULL) {
}
-void HOptimizedGraphBuilder::PushAndAdd(HInstruction* instr) {
+void HGraphBuilder::PushAndAdd(HInstruction* instr) {
Push(instr);
AddInstruction(instr);
}
void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression()));
- HValue* value = Pop();
Handle<Type> operand_type = expr->expression()->bounds().lower;
+ HValue* value = TruncateToNumber(Pop(), &operand_type);
HInstruction* instr = BuildUnaryMathOp(value, operand_type, Token::SUB);
return ast_context()->ReturnInstruction(instr, expr->id());
}
void HOptimizedGraphBuilder::VisitBitNot(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression()));
- HValue* value = Pop();
Handle<Type> operand_type = expr->expression()->bounds().lower;
+ HValue* value = TruncateToNumber(Pop(), &operand_type);
HInstruction* instr = BuildUnaryMathOp(value, operand_type, Token::BIT_NOT);
return ast_context()->ReturnInstruction(instr, expr->id());
}
}
+HValue* HGraphBuilder::TruncateToNumber(HValue* value, Handle<Type>* expected) {
+ if (value->IsConstant()) {
+ HConstant* constant = HConstant::cast(value);
+ Maybe<HConstant*> number = constant->CopyToTruncatedNumber(zone());
+ if (number.has_value) {
+ *expected = handle(Type::Number(), isolate());
+ return AddInstruction(number.value);
+ }
+ return value;
+ }
+
+ Handle<Type> expected_type = *expected;
+ Representation rep = Representation::FromType(expected_type);
+ if (!rep.IsTagged()) return value;
+
+ // If our type feedback suggests that we can non-observably truncate to number
+ // we introduce the appropriate check here. This avoids 'value' having a
+ // tagged representation later on.
+ if (expected_type->Is(Type::Oddball())) {
+ // TODO(olivf) The BinaryOpStub only records undefined. It might pay off to
+ // also record booleans and convert them to 0/1 here.
+ IfBuilder if_nan(this);
+ if_nan.If<HCompareObjectEqAndBranch>(value,
+ graph()->GetConstantUndefined());
+ if_nan.Then();
+ if_nan.ElseDeopt();
+ if_nan.End();
+ return Add<HConstant>(OS::nan_value(), Representation::Double());
+ }
+
+ return value;
+}
+
+
HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
BinaryOperation* expr,
HValue* left,
Representation right_rep = Representation::FromType(right_type);
Representation result_rep = Representation::FromType(result_type);
+ if (expr->op() != Token::ADD ||
+ (left->type().IsNonString() && right->type().IsNonString())) {
+ // For addition we can only truncate the arguments to number if we can
+ // prove that we will not end up in string concatenation mode.
+ left = TruncateToNumber(left, &left_type);
+ right = TruncateToNumber(right, &right_type);
+ }
+
if (left_type->Is(Type::None())) {
AddSoftDeoptimize();
// TODO(rossberg): we should be able to get rid of non-continuous defaults.
HValue* AddLoadJSBuiltin(Builtins::JavaScript builtin, HValue* context);
+ HValue* TruncateToNumber(HValue* value, Handle<Type>* expected);
+
+ void PushAndAdd(HInstruction* instr);
+
enum SoftDeoptimizeMode {
MUST_EMIT_SOFT_DEOPT,
CAN_OMIT_SOFT_DEOPT
// Visit a list of expressions from left to right, each in a value context.
void VisitExpressions(ZoneList<Expression*>* exprs);
- void PushAndAdd(HInstruction* instr);
-
// Remove the arguments from the bailout environment and emit instructions
// to push them as outgoing parameters.
template <class Instruction> HInstruction* PreProcessCall(Instruction* call);