[strong] Implement strong mode semantics for the count operation.

Also fixes a crankshaft bug with strong implicit conversions.

It turns out that the implicit conversion of oddball values
is smushed into so many places in crankshaft that it would
have been pretty invasive surgery to make everything fall
out naturally.

BUG=v8:3956
LOG=N

Review URL: https://codereview.chromium.org/1216463003

Cr-Commit-Position: refs/heads/master@{#29381}
This commit is contained in:
conradw 2015-06-30 07:21:51 -07:00 committed by Commit bot
parent 7374e6dc89
commit f5cc091f8f
21 changed files with 594 additions and 104 deletions

View File

@ -5024,9 +5024,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ jmp(&stub_call);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -5068,6 +5070,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in r0.
switch (assign_type) {
case VARIABLE:

View File

@ -4712,9 +4712,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ B(&stub_call);
__ Bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -4759,6 +4761,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
}
__ Bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in x0.
switch (assign_type) {
case VARIABLE:

View File

@ -2661,9 +2661,11 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
}
// Convert old value into a number.
old_value = NewNode(javascript()->ToNumber(), old_value);
PrepareFrameState(old_value, expr->ToNumberId(),
OutputFrameStateCombine::Push());
if (!is_strong(language_mode())) {
old_value = NewNode(javascript()->ToNumber(), old_value);
PrepareFrameState(old_value, expr->ToNumberId(),
OutputFrameStateCombine::Push());
}
// TODO(titzer): combine this framestate with the above?
FrameStateBeforeAndAfter store_states(this, assign_type == KEYED_PROPERTY
@ -2679,9 +2681,10 @@ void AstGraphBuilder::VisitCountOperation(CountOperation* expr) {
FrameStateBeforeAndAfter states(this, BailoutId::None());
value =
BuildBinaryOp(old_value, jsgraph()->OneConstant(), expr->binary_op());
// This should never deoptimize because we have converted to number
// before.
states.AddToNode(value, BailoutId::None(),
// This should never deoptimize outside strong mode because otherwise we
// have converted to number before.
states.AddToNode(value, is_strong(language_mode()) ? expr->ToNumberId()
: BailoutId::None(),
OutputFrameStateCombine::Ignore());
}

View File

@ -3404,7 +3404,7 @@ void HCompareNumericAndBranch::InferRepresentation(
// (false). Therefore, any comparisons other than ordered relational
// comparisons must cause a deopt when one of their arguments is undefined.
// See also v8:1434
if (Token::IsOrderedRelationalCompareOp(token_)) {
if (Token::IsOrderedRelationalCompareOp(token_) && !is_strong(strength())) {
SetFlag(kAllowUndefinedAsNaN);
}
}

View File

@ -4151,7 +4151,7 @@ class HBitwiseBinaryOperation : public HBinaryOperation {
: HBinaryOperation(context, left, right, strength, type) {
SetFlag(kFlexibleRepresentation);
SetFlag(kTruncatingToInt32);
SetFlag(kAllowUndefinedAsNaN);
if (!is_strong(strength)) SetFlag(kAllowUndefinedAsNaN);
SetAllSideEffects();
}
@ -4232,7 +4232,7 @@ class HArithmeticBinaryOperation : public HBinaryOperation {
HType::TaggedNumber()) {
SetAllSideEffects();
SetFlag(kFlexibleRepresentation);
SetFlag(kAllowUndefinedAsNaN);
if (!is_strong(strength)) SetFlag(kAllowUndefinedAsNaN);
}
void RepresentationChanged(Representation to) override {
@ -4289,11 +4289,22 @@ class HCompareGeneric final : public HBinaryOperation {
class HCompareNumericAndBranch : public HTemplateControlInstruction<2, 2> {
public:
DECLARE_INSTRUCTION_FACTORY_P3(HCompareNumericAndBranch,
HValue*, HValue*, Token::Value);
DECLARE_INSTRUCTION_FACTORY_P5(HCompareNumericAndBranch,
HValue*, HValue*, Token::Value,
HBasicBlock*, HBasicBlock*);
static HCompareNumericAndBranch* New(Isolate* isolate, Zone* zone,
HValue* context, HValue* left,
HValue* right, Token::Value token,
HBasicBlock* true_target = NULL,
HBasicBlock* false_target = NULL,
Strength strength = Strength::WEAK) {
return new (zone) HCompareNumericAndBranch(left, right, token, true_target,
false_target, strength);
}
static HCompareNumericAndBranch* New(Isolate* isolate, Zone* zone,
HValue* context, HValue* left,
HValue* right, Token::Value token,
Strength strength) {
return new (zone)
HCompareNumericAndBranch(left, right, token, NULL, NULL, strength);
}
HValue* left() const { return OperandAt(0); }
HValue* right() const { return OperandAt(1); }
@ -4316,6 +4327,8 @@ class HCompareNumericAndBranch : public HTemplateControlInstruction<2, 2> {
bool KnownSuccessorBlock(HBasicBlock** block) override;
Strength strength() const { return strength_; }
std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT
void SetOperandPositions(Zone* zone, SourcePosition left_pos,
@ -4327,12 +4340,10 @@ class HCompareNumericAndBranch : public HTemplateControlInstruction<2, 2> {
DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch)
private:
HCompareNumericAndBranch(HValue* left,
HValue* right,
Token::Value token,
HBasicBlock* true_target = NULL,
HBasicBlock* false_target = NULL)
: token_(token) {
HCompareNumericAndBranch(HValue* left, HValue* right, Token::Value token,
HBasicBlock* true_target, HBasicBlock* false_target,
Strength strength)
: token_(token), strength_(strength) {
SetFlag(kFlexibleRepresentation);
DCHECK(Token::IsCompareOp(token));
SetOperandAt(0, left);
@ -4343,6 +4354,7 @@ class HCompareNumericAndBranch : public HTemplateControlInstruction<2, 2> {
Representation observed_input_representation_[2];
Token::Value token_;
Strength strength_;
};

View File

@ -10305,7 +10305,7 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement(
rep = Representation::Smi();
}
if (returns_original_input) {
if (returns_original_input && !is_strong(function_language_mode())) {
// We need an explicit HValue representing ToNumber(input). The
// actual HChange instruction we need is (sometimes) added in a later
// phase, so it is not available now to be used as an input to HAdd and
@ -10331,8 +10331,12 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement(
add->set_observed_input_representation(1, rep);
add->set_observed_input_representation(2, Representation::Smi());
}
if (!is_strong(function_language_mode())) {
instr->ClearAllSideEffects();
} else {
Add<HSimulate>(expr->ToNumberId(), REMOVABLE_SIMULATE);
}
instr->SetFlag(HInstruction::kCannotBeTagged);
instr->ClearAllSideEffects();
return instr;
}
@ -10616,10 +10620,10 @@ HValue* HOptimizedGraphBuilder::BuildBinaryOperation(
if (FLAG_allocation_site_pretenuring && !allocation_site.is_null()) {
allocation_mode = HAllocationMode(allocation_site);
}
HValue* result = HGraphBuilder::BuildBinaryOperation(
expr->op(), left, right, left_type, right_type, result_type,
fixed_right_arg, allocation_mode, strength(function_language_mode()));
fixed_right_arg, allocation_mode, strength(function_language_mode()),
expr->id());
// Add a simulate after instructions with observable side effects, and
// after phis, which are the result of BuildBinaryOperation when we
// inlined some complex subgraph.
@ -10636,12 +10640,10 @@ HValue* HOptimizedGraphBuilder::BuildBinaryOperation(
}
HValue* HGraphBuilder::BuildBinaryOperation(Token::Value op, HValue* left,
HValue* right, Type* left_type,
Type* right_type, Type* result_type,
Maybe<int> fixed_right_arg,
HAllocationMode allocation_mode,
Strength strength) {
HValue* HGraphBuilder::BuildBinaryOperation(
Token::Value op, HValue* left, HValue* right, Type* left_type,
Type* right_type, Type* result_type, Maybe<int> fixed_right_arg,
HAllocationMode allocation_mode, Strength strength, BailoutId opt_id) {
bool maybe_string_add = false;
if (op == Token::ADD) {
// If we are adding constant string with something for which we don't have
@ -10684,7 +10686,7 @@ HValue* HGraphBuilder::BuildBinaryOperation(Token::Value op, HValue* left,
maybe_string_add = op == Token::ADD;
}
if (!maybe_string_add) {
if (!maybe_string_add && !is_strong(strength)) {
left = TruncateToNumber(left, &left_type);
right = TruncateToNumber(right, &right_type);
}
@ -10794,6 +10796,20 @@ HValue* HGraphBuilder::BuildBinaryOperation(Token::Value op, HValue* left,
Add<HPushArguments>(left, right);
instr = AddUncasted<HInvokeFunction>(function, 2);
} else {
if (is_strong(strength) && Token::IsBitOp(op)) {
IfBuilder if_builder(this);
if_builder.If<HHasInstanceTypeAndBranch>(left, ODDBALL_TYPE);
if_builder.OrIf<HHasInstanceTypeAndBranch>(right, ODDBALL_TYPE);
if_builder.Then();
Add<HCallRuntime>(
isolate()->factory()->empty_string(),
Runtime::FunctionForId(Runtime::kThrowStrongModeImplicitConversion),
0);
if (!graph()->info()->IsStub()) {
Add<HSimulate>(opt_id, REMOVABLE_SIMULATE);
}
if_builder.End();
}
switch (op) {
case Token::ADD:
instr = AddUncasted<HAdd>(left, right, strength);
@ -11251,8 +11267,8 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
HBranch* branch = New<HBranch>(result);
return branch;
} else {
HCompareNumericAndBranch* result =
New<HCompareNumericAndBranch>(left, right, op);
HCompareNumericAndBranch* result = New<HCompareNumericAndBranch>(
left, right, op, strength(function_language_mode()));
result->set_observed_input_representation(left_rep, right_rep);
if (top_info()->is_tracking_positions()) {
result->SetOperandPositions(zone(), left_position, right_position);

View File

@ -1433,7 +1433,8 @@ class HGraphBuilder {
Type* left_type, Type* right_type,
Type* result_type, Maybe<int> fixed_right_arg,
HAllocationMode allocation_mode,
Strength strength);
Strength strength,
BailoutId opt_id = BailoutId::None());
HLoadNamedField* AddLoadFixedArrayLength(HValue *object,
HValue *dependency = NULL);

View File

@ -4961,9 +4961,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ jmp(&stub_call, Label::kNear);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -5004,6 +5006,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in eax.
switch (assign_type) {
case VARIABLE:

View File

@ -223,6 +223,7 @@ Type* BinaryOpICState::GetResultType(Zone* zone) const {
std::ostream& operator<<(std::ostream& os, const BinaryOpICState& s) {
os << "(" << Token::Name(s.op_);
if (s.CouldCreateAllocationMementos()) os << "_CreateAllocationMementos";
if (is_strong(s.strength())) os << "_Strong";
os << ":" << BinaryOpICState::KindToString(s.left_kind_) << "*";
if (s.fixed_right_arg_.IsJust()) {
os << s.fixed_right_arg_.FromJust();

View File

@ -230,7 +230,8 @@ class CallSite {
"In strong mode, calling a function with too few arguments is deprecated") \
T(StrongDeleteProperty, \
"On strong object %, deletion of property % is deprecated") \
T(StrongImplicitCast, "In strong mode, implicit conversions are deprecated") \
T(StrongImplicitConversion, \
"In strong mode, implicit conversions are deprecated") \
T(StrongRedefineDisallowed, \
"On strong object %, redefining writable, non-configurable property '%' " \
"to be non-writable is deprecated") \

View File

@ -5043,9 +5043,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ jmp(&stub_call);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -5086,6 +5088,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in v0.
switch (assign_type) {
case VARIABLE:

View File

@ -5045,9 +5045,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ jmp(&stub_call);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -5088,6 +5090,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in v0.
switch (assign_type) {
case VARIABLE:

View File

@ -5050,9 +5050,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ b(&stub_call);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -5093,6 +5095,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in r3.
switch (assign_type) {
case VARIABLE:

View File

@ -211,7 +211,7 @@ COMPARE_STRONG = function COMPARE_STRONG(x, ncr) {
if (IS_STRING(this) && IS_STRING(x)) return %_StringCompare(this, x);
if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberCompare(this, x, ncr);
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -246,7 +246,7 @@ ADD_STRONG = function ADD_STRONG(x) {
if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x);
if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x);
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -270,7 +270,7 @@ STRING_ADD_LEFT_STRONG = function STRING_ADD_LEFT_STRONG(y) {
if (IS_STRING(y)) {
return %_StringAdd(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -295,7 +295,7 @@ STRING_ADD_RIGHT_STRONG = function STRING_ADD_RIGHT_STRONG(y) {
if (IS_STRING(this)) {
return %_StringAdd(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -312,7 +312,7 @@ SUB_STRONG = function SUB_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSub(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -329,7 +329,7 @@ MUL_STRONG = function MUL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberMul(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -346,7 +346,7 @@ DIV_STRONG = function DIV_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberDiv(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -363,7 +363,7 @@ MOD_STRONG = function MOD_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberMod(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -385,7 +385,7 @@ BIT_OR_STRONG = function BIT_OR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberOr(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -416,7 +416,7 @@ BIT_AND_STRONG = function BIT_AND_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberAnd(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -433,7 +433,7 @@ BIT_XOR_STRONG = function BIT_XOR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberXor(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -450,7 +450,7 @@ SHL_STRONG = function SHL_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShl(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -481,7 +481,7 @@ SAR_STRONG = function SAR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberSar(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}
@ -498,7 +498,7 @@ SHR_STRONG = function SHR_STRONG(y) {
if (IS_NUMBER(this) && IS_NUMBER(y)) {
return %NumberShr(this, y);
}
throw %MakeTypeError(kStrongImplicitCast);
throw %MakeTypeError(kStrongImplicitConversion);
}

View File

@ -103,6 +103,14 @@ RUNTIME_FUNCTION(Runtime_ThrowIteratorResultNotAnObject) {
}
RUNTIME_FUNCTION(Runtime_ThrowStrongModeImplicitConversion) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kStrongImplicitConversion));
}
RUNTIME_FUNCTION(Runtime_PromiseRejectEvent) {
DCHECK(args.length() == 3);
HandleScope scope(isolate);

View File

@ -300,6 +300,7 @@ namespace internal {
F(NewSyntaxError, 2, 1) \
F(NewReferenceError, 2, 1) \
F(ThrowIteratorResultNotAnObject, 1, 1) \
F(ThrowStrongModeImplicitConversion, 0, 1) \
F(PromiseRejectEvent, 3, 1) \
F(PromiseRevokeReject, 1, 1) \
F(PromiseHasHandlerSymbol, 0, 1) \

View File

@ -4981,10 +4981,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ jmp(&stub_call, Label::kNear);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -5025,6 +5026,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in rax.
switch (assign_type) {
case VARIABLE:

View File

@ -4943,9 +4943,11 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ jmp(&stub_call, Label::kNear);
__ bind(&slow);
}
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
if (!is_strong(language_mode())) {
ToNumberStub convert_stub(isolate());
__ CallStub(&convert_stub);
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Save result for postfix expressions.
if (expr->is_postfix()) {
@ -4986,6 +4988,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
patch_site.EmitPatchInfo();
__ bind(&done);
if (is_strong(language_mode())) {
PrepareForBailoutForId(expr->ToNumberId(), TOS_REG);
}
// Store the value returned in eax.
switch (assign_type) {
case VARIABLE:

View File

@ -0,0 +1,203 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --strong-mode --allow-natives-syntax
"use strict";
function getTestFuncs() {
"use strong";
return [
function(x){return 1 + true;},
function(x){return 1 - true;},
function(x){return 1 * true;},
function(x){return 1 / true;},
function(x){return 1 % true;},
function(x){return 1 | true;},
function(x){return 1 & true;},
function(x){return 1 ^ true;},
function(x){return 1 << true;},
function(x){return 1 >> true;},
function(x){return 1 >>> true;},
function(x){return 1 < true;},
function(x){return 1 > true;},
function(x){return 1 <= true;},
function(x){return 1 >= true;},
function(x){return 1 + undefined;},
function(x){return 1 - undefined;},
function(x){return 1 * undefined;},
function(x){return 1 / undefined;},
function(x){return 1 % undefined;},
function(x){return 1 | undefined;},
function(x){return 1 & undefined;},
function(x){return 1 ^ undefined;},
function(x){return 1 << undefined;},
function(x){return 1 >> undefined;},
function(x){return 1 >>> undefined;},
function(x){return 1 < undefined;},
function(x){return 1 > undefined;},
function(x){return 1 <= undefined;},
function(x){return 1 >= undefined;},
function(x){return 1 + null;},
function(x){return 1 - null;},
function(x){return 1 * null;},
function(x){return 1 / null;},
function(x){return 1 % null;},
function(x){return 1 | null;},
function(x){return 1 & null;},
function(x){return 1 ^ null;},
function(x){return 1 << null;},
function(x){return 1 >> null;},
function(x){return 1 >>> null;},
function(x){return 1 < null;},
function(x){return 1 > null;},
function(x){return 1 <= null;},
function(x){return 1 >= null;},
function(x){return NaN + true;},
function(x){return NaN - true;},
function(x){return NaN * true;},
function(x){return NaN / true;},
function(x){return NaN % true;},
function(x){return NaN | true;},
function(x){return NaN & true;},
function(x){return NaN ^ true;},
function(x){return NaN << true;},
function(x){return NaN >> true;},
function(x){return NaN >>> true;},
function(x){return NaN < true;},
function(x){return NaN > true;},
function(x){return NaN <= true;},
function(x){return NaN >= true;},
function(x){return NaN + undefined;},
function(x){return NaN - undefined;},
function(x){return NaN * undefined;},
function(x){return NaN / undefined;},
function(x){return NaN % undefined;},
function(x){return NaN | undefined;},
function(x){return NaN & undefined;},
function(x){return NaN ^ undefined;},
function(x){return NaN << undefined;},
function(x){return NaN >> undefined;},
function(x){return NaN >>> undefined;},
function(x){return NaN < undefined;},
function(x){return NaN > undefined;},
function(x){return NaN <= undefined;},
function(x){return NaN >= undefined;},
function(x){return NaN + null;},
function(x){return NaN - null;},
function(x){return NaN * null;},
function(x){return NaN / null;},
function(x){return NaN % null;},
function(x){return NaN | null;},
function(x){return NaN & null;},
function(x){return NaN ^ null;},
function(x){return NaN << null;},
function(x){return NaN >> null;},
function(x){return NaN >>> null;},
function(x){return NaN < null;},
function(x){return NaN > null;},
function(x){return NaN <= null;},
function(x){return NaN >= null;},
function(x){return true + 1;},
function(x){return true - 1;},
function(x){return true * 1;},
function(x){return true / 1;},
function(x){return true % 1;},
function(x){return true | 1;},
function(x){return true & 1;},
function(x){return true ^ 1;},
function(x){return true << 1;},
function(x){return true >> 1;},
function(x){return true >>> 1;},
function(x){return true < 1;},
function(x){return true > 1;},
function(x){return true <= 1;},
function(x){return true >= 1;},
function(x){return undefined + 1;},
function(x){return undefined - 1;},
function(x){return undefined * 1;},
function(x){return undefined / 1;},
function(x){return undefined % 1;},
function(x){return undefined | 1;},
function(x){return undefined & 1;},
function(x){return undefined ^ 1;},
function(x){return undefined << 1;},
function(x){return undefined >> 1;},
function(x){return undefined >>> 1;},
function(x){return undefined < 1;},
function(x){return undefined > 1;},
function(x){return undefined <= 1;},
function(x){return undefined >= 1;},
function(x){return null + 1;},
function(x){return null - 1;},
function(x){return null * 1;},
function(x){return null / 1;},
function(x){return null % 1;},
function(x){return null | 1;},
function(x){return null & 1;},
function(x){return null ^ 1;},
function(x){return null << 1;},
function(x){return null >> 1;},
function(x){return null >>> 1;},
function(x){return null < 1;},
function(x){return null > 1;},
function(x){return null <= 1;},
function(x){return null >= 1;},
function(x){return true + NaN;},
function(x){return true - NaN;},
function(x){return true * NaN;},
function(x){return true / NaN;},
function(x){return true % NaN;},
function(x){return true | NaN;},
function(x){return true & NaN;},
function(x){return true ^ NaN;},
function(x){return true << NaN;},
function(x){return true >> NaN;},
function(x){return true >>> NaN;},
function(x){return true < NaN;},
function(x){return true > NaN;},
function(x){return true <= NaN;},
function(x){return true >= NaN;},
function(x){return undefined + NaN;},
function(x){return undefined - NaN;},
function(x){return undefined * NaN;},
function(x){return undefined / NaN;},
function(x){return undefined % NaN;},
function(x){return undefined | NaN;},
function(x){return undefined & NaN;},
function(x){return undefined ^ NaN;},
function(x){return undefined << NaN;},
function(x){return undefined >> NaN;},
function(x){return undefined >>> NaN;},
function(x){return undefined < NaN;},
function(x){return undefined > NaN;},
function(x){return undefined <= NaN;},
function(x){return undefined >= NaN;},
function(x){return null + NaN;},
function(x){return null - NaN;},
function(x){return null * NaN;},
function(x){return null / NaN;},
function(x){return null % NaN;},
function(x){return null | NaN;},
function(x){return null & NaN;},
function(x){return null ^ NaN;},
function(x){return null << NaN;},
function(x){return null >> NaN;},
function(x){return null >>> NaN;},
function(x){return null < NaN;},
function(x){return null > NaN;},
function(x){return null <= NaN;},
function(x){return null >= NaN;}
];
}
for (let func of getTestFuncs()) {
assertThrows(func, TypeError);
assertThrows(func, TypeError);
assertThrows(func, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(func, TypeError);
%DeoptimizeFunction(func);
assertThrows(func, TypeError);
}

View File

@ -0,0 +1,168 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --strong-mode --allow-natives-syntax
"use strict";
function pre_inc(x) {
return ++x;
}
function post_inc(x) {
return x++;
}
function pre_dec(x) {
return --x;
}
function post_dec(x) {
return x--;
}
function getTestFuncs() {
return [
function(x){
"use strong";
let y = x;
assertEquals(++y, pre_inc(x));
try {
assertEquals(x+1, y)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let y = x;
assertEquals(y++, post_inc(x));
try {
assertEquals(x+1, y)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let y = x;
assertEquals(--y, pre_dec(x));
try {
assertEquals(x-1, y)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let y = x;
assertEquals(y--, post_dec(x));
try {
assertEquals(x-1, y)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let obj = { foo: x };
let y = ++obj.foo;
assertEquals(y, pre_inc(x));
try {
assertEquals(x+1, obj.foo)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let obj = { foo: x };
let y = obj.foo++;
assertEquals(y, post_inc(x));
try {
assertEquals(x+1, obj.foo)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let obj = { foo: x };
let y = --obj.foo;
assertEquals(y, pre_dec(x));
try {
assertEquals(x-1, obj.foo)
} catch (e) {
assertUnreachable();
}
},
function(x){
"use strong";
let obj = { foo: x };
let y = obj.foo--;
assertEquals(y, post_dec(x));
try {
assertEquals(x-1, obj.foo)
} catch (e) {
assertUnreachable();
}
},
];
}
let nonNumberValues = [
{},
(function(){}),
[],
(class Foo {}),
"",
"foo",
"NaN",
Object(""),
false,
null,
undefined
];
// Check prior input of None works
for (let func of getTestFuncs()) {
for (let value of nonNumberValues) {
assertThrows(function(){func(value)}, TypeError);
assertThrows(function(){func(value)}, TypeError);
assertThrows(function(){func(value)}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(value)}, TypeError);
%DeoptimizeFunction(func);
}
}
// Check prior input of Smi works
for (let func of getTestFuncs()) {
func(1);
func(1);
func(1);
for (let value of nonNumberValues) {
assertThrows(function(){func(value)}, TypeError);
assertThrows(function(){func(value)}, TypeError);
assertThrows(function(){func(value)}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(value)}, TypeError);
%DeoptimizeFunction(func);
}
}
// Check prior input of Number works
for (let func of getTestFuncs()) {
func(9999999999999);
func(9999999999999);
func(9999999999999);
for (let value of nonNumberValues) {
assertThrows(function(){func(value)}, TypeError);
assertThrows(function(){func(value)}, TypeError);
assertThrows(function(){func(value)}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(value)}, TypeError);
%DeoptimizeFunction(func);
}
}

View File

@ -61,23 +61,17 @@ let numberValues = [
"0",
"(-0)",
"1",
"0.79",
"(-0.79)",
"4294967295",
"4294967296",
"(-4294967295)",
"(-4294967296)",
"9999999999999",
"(-9999999999999)",
"1.5e10",
"(-1.5e10)",
"0xFFF",
"(-0xFFF)",
"NaN",
"Infinity",
"(-Infinity)"
];
//******************************************************************************
// Relational comparison function declarations
function add_strong(x, y) {
"use strong";
return x + y;
@ -253,6 +247,18 @@ function typed_greater_equal_strong(x, y) {
return (+x) >= (+y);
}
//******************************************************************************
// (in)equality function declarations
function str_equal_strong(x, y) {
"use strong";
return x === y;
}
function str_ineq_strong(x, y) {
"use strong";
return x !== y;
}
let strongNumberFuncs = [add_num_strong, sub_strong, mul_strong, div_strong,
mod_strong, or_strong, and_strong, xor_strong,
shl_strong, shr_strong, sar_strong, less_num_strong,
@ -332,44 +338,75 @@ for (let op of strongUnops) {
for (let func of strongNumberFuncs) {
// Check IC None*None->None throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
for (let v of nonNumberValues) {
let value = eval(v);
assertThrows(function(){func(2, value);}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, value);}, TypeError);
%DeoptimizeFunction(func);
}
func(4, 5);
func(4, 5);
// Check IC Smi*Smi->Smi throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
for (let v of nonNumberValues) {
let value = eval(v);
assertThrows(function(){func(2, value);}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, value);}, TypeError);
%DeoptimizeFunction(func);
}
func(NaN, NaN);
func(NaN, NaN);
// Check IC Number*Number->Number throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
for (let v of nonNumberValues) {
let value = eval(v);
assertThrows(function(){func(2, value);}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, value);}, TypeError);
%DeoptimizeFunction(func);
}
}
for (let func of strongStringOrNumberFuncs) {
// Check IC None*None->None throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
for (let v of nonNumberValues) {
let value = eval(v);
assertThrows(function(){func(2, value);}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, value);}, TypeError);
%DeoptimizeFunction(func);
}
func("foo", "bar");
func("foo", "bar");
// Check IC String*String->String throws
assertThrows(function(){func(2, "foo");}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
%DeoptimizeFunction(func);
for (let v of nonNumberValues) {
let value = eval(v);
assertThrows(function(){func(2, value);}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, value);}, TypeError);
%DeoptimizeFunction(func);
}
func(NaN, NaN);
func(NaN, NaN);
// Check IC Generic*Generic->Generic throws
assertThrows(function(){func(2, "foo");}, TypeError);
for (let v of nonNumberValues) {
let value = eval(v);
assertThrows(function(){func(2, value);}, TypeError);
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, value);}, TypeError);
%DeoptimizeFunction(func);
}
}
for (let func of [str_equal_strong, str_ineq_strong]) {
assertDoesNotThrow(function(){func(2, undefined)});
assertDoesNotThrow(function(){func(2, undefined)});
%OptimizeFunctionOnNextCall(func);
assertThrows(function(){func(2, "foo");}, TypeError);
assertDoesNotThrow(function(){func(2, undefined)});
%DeoptimizeFunction(func);
assertDoesNotThrow(function(){func(true, {})});
assertDoesNotThrow(function(){func(true, {})});
%OptimizeFunctionOnNextCall(func);
assertDoesNotThrow(function(){func(true, {})});
%DeoptimizeFunction(func);
}