Allow control intructions to have side effects.
As a first application convert HCompareGeneric to a control Instruction, thus avoid materializing a boolean result value. BUG= R=titzer@chromium.org Review URL: https://codereview.chromium.org/23710070 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16902 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
39ad675441
commit
3e13af46e4
@ -630,12 +630,16 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
if (hinstr->HasObservableSideEffects()) {
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
||||||
|
if (!hinstr->IsControlInstruction()) {
|
||||||
|
ASSERT(hinstr->next()->IsSimulate());
|
||||||
|
HSimulate* sim = HSimulate::cast(hinstr->next());
|
||||||
|
pending_deoptimization_ast_id_ = sim->ast_id();
|
||||||
|
} else {
|
||||||
|
pending_deoptimization_ast_id_ = BailoutId::PendingMarker();
|
||||||
|
}
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
instruction_pending_deoptimization_environment_ = instr;
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
@ -1716,13 +1720,13 @@ LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
|
LInstruction* LChunkBuilder::DoCompareGenericAndBranch(
|
||||||
|
HCompareGenericAndBranch* instr) {
|
||||||
ASSERT(instr->left()->representation().IsTagged());
|
ASSERT(instr->left()->representation().IsTagged());
|
||||||
ASSERT(instr->right()->representation().IsTagged());
|
ASSERT(instr->right()->representation().IsTagged());
|
||||||
LOperand* left = UseFixed(instr->left(), r1);
|
LOperand* left = UseFixed(instr->left(), r1);
|
||||||
LOperand* right = UseFixed(instr->right(), r0);
|
LOperand* right = UseFixed(instr->right(), r0);
|
||||||
LCmpT* result = new(zone()) LCmpT(left, right);
|
return MarkAsCall(new(zone()) LCompareGenericAndBranch(left, right), instr);
|
||||||
return MarkAsCall(DefineFixed(result, r0), instr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2515,7 +2519,9 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
|||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
// If there is an instruction pending deoptimization environment create a
|
||||||
// lazy bailout instruction to capture the environment.
|
// lazy bailout instruction to capture the environment.
|
||||||
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
|
if (!pending_deoptimization_ast_id_.IsNone()) {
|
||||||
|
ASSERT(pending_deoptimization_ast_id_ == instr->ast_id() ||
|
||||||
|
pending_deoptimization_ast_id_.IsPendingMarker());
|
||||||
LInstruction* result = new(zone()) LLazyBailout;
|
LInstruction* result = new(zone()) LLazyBailout;
|
||||||
result = AssignEnvironment(result);
|
result = AssignEnvironment(result);
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
// Store the lazy deopt environment with the instruction if needed. Right
|
||||||
|
@ -76,7 +76,7 @@ class LCodeGen;
|
|||||||
V(CmpObjectEqAndBranch) \
|
V(CmpObjectEqAndBranch) \
|
||||||
V(CmpHoleAndBranch) \
|
V(CmpHoleAndBranch) \
|
||||||
V(CmpMapAndBranch) \
|
V(CmpMapAndBranch) \
|
||||||
V(CmpT) \
|
V(CompareGenericAndBranch) \
|
||||||
V(ConstantD) \
|
V(ConstantD) \
|
||||||
V(ConstantE) \
|
V(ConstantE) \
|
||||||
V(ConstantI) \
|
V(ConstantI) \
|
||||||
@ -1073,9 +1073,9 @@ class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 1> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
class LCompareGenericAndBranch V8_FINAL : public LControlInstruction<2, 0> {
|
||||||
public:
|
public:
|
||||||
LCmpT(LOperand* left, LOperand* right) {
|
LCompareGenericAndBranch(LOperand* left, LOperand* right) {
|
||||||
inputs_[0] = left;
|
inputs_[0] = left;
|
||||||
inputs_[1] = right;
|
inputs_[1] = right;
|
||||||
}
|
}
|
||||||
@ -1083,8 +1083,9 @@ class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
|||||||
LOperand* left() { return inputs_[0]; }
|
LOperand* left() { return inputs_[0]; }
|
||||||
LOperand* right() { return inputs_[1]; }
|
LOperand* right() { return inputs_[1]; }
|
||||||
|
|
||||||
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
|
DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch,
|
||||||
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
|
"compare-generic-and-branch")
|
||||||
|
DECLARE_HYDROGEN_ACCESSOR(CompareGenericAndBranch)
|
||||||
|
|
||||||
Token::Value op() const { return hydrogen()->token(); }
|
Token::Value op() const { return hydrogen()->token(); }
|
||||||
};
|
};
|
||||||
|
@ -2888,7 +2888,7 @@ void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoCmpT(LCmpT* instr) {
|
void LCodeGen::DoCompareGenericAndBranch(LCompareGenericAndBranch* instr) {
|
||||||
Token::Value op = instr->op();
|
Token::Value op = instr->op();
|
||||||
|
|
||||||
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
|
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
|
||||||
@ -2897,12 +2897,7 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
|
|||||||
__ cmp(r0, Operand::Zero());
|
__ cmp(r0, Operand::Zero());
|
||||||
|
|
||||||
Condition condition = ComputeCompareCondition(op);
|
Condition condition = ComputeCompareCondition(op);
|
||||||
__ LoadRoot(ToRegister(instr->result()),
|
EmitBranch(instr, condition);
|
||||||
Heap::kTrueValueRootIndex,
|
|
||||||
condition);
|
|
||||||
__ LoadRoot(ToRegister(instr->result()),
|
|
||||||
Heap::kFalseValueRootIndex,
|
|
||||||
NegateCondition(condition));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -794,7 +794,7 @@ void HInstruction::Verify() {
|
|||||||
|
|
||||||
// Verify that instructions that may have side-effects are followed
|
// Verify that instructions that may have side-effects are followed
|
||||||
// by a simulate instruction.
|
// by a simulate instruction.
|
||||||
if (HasObservableSideEffects() && !IsOsrEntry()) {
|
if (HasObservableSideEffects() && !IsOsrEntry() && !IsControlInstruction()) {
|
||||||
ASSERT(next()->IsSimulate());
|
ASSERT(next()->IsSimulate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,6 +1011,21 @@ void HControlInstruction::PrintDataTo(StringStream* stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void HControlInstruction::Verify() {
|
||||||
|
HInstruction::Verify();
|
||||||
|
if (!HasObservableSideEffects()) return;
|
||||||
|
for (HSuccessorIterator it(this); !it.Done(); it.Advance()) {
|
||||||
|
// For ControlInstructions we need to verify that the successors all start
|
||||||
|
// with a Simulate.
|
||||||
|
HInstruction* first = it.Current()->first()->next();
|
||||||
|
ASSERT(first->IsSimulate() ||
|
||||||
|
(first->IsLeaveInlined() && first->next()->IsSimulate()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void HUnaryControlInstruction::PrintDataTo(StringStream* stream) {
|
void HUnaryControlInstruction::PrintDataTo(StringStream* stream) {
|
||||||
value()->PrintNameTo(stream);
|
value()->PrintNameTo(stream);
|
||||||
HControlInstruction::PrintDataTo(stream);
|
HControlInstruction::PrintDataTo(stream);
|
||||||
@ -2823,10 +2838,14 @@ Range* HLoadKeyed::InferRange(Zone* zone) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HCompareGeneric::PrintDataTo(StringStream* stream) {
|
void HCompareGenericAndBranch::PrintDataTo(StringStream* stream) {
|
||||||
stream->Add(Token::Name(token()));
|
stream->Add(Token::Name(token()));
|
||||||
stream->Add(" ");
|
stream->Add(" ");
|
||||||
HBinaryOperation::PrintDataTo(stream);
|
left()->PrintNameTo(stream);
|
||||||
|
stream->Add(" ");
|
||||||
|
right()->PrintNameTo(stream);
|
||||||
|
if (CheckFlag(kCanOverflow)) stream->Add(" !");
|
||||||
|
if (CheckFlag(kBailoutOnMinusZero)) stream->Add(" -0?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ class LChunkBuilder;
|
|||||||
V(ClassOfTestAndBranch) \
|
V(ClassOfTestAndBranch) \
|
||||||
V(CompareNumericAndBranch) \
|
V(CompareNumericAndBranch) \
|
||||||
V(CompareHoleAndBranch) \
|
V(CompareHoleAndBranch) \
|
||||||
V(CompareGeneric) \
|
V(CompareGenericAndBranch) \
|
||||||
V(CompareObjectEqAndBranch) \
|
V(CompareObjectEqAndBranch) \
|
||||||
V(CompareMap) \
|
V(CompareMap) \
|
||||||
V(Constant) \
|
V(Constant) \
|
||||||
@ -1069,26 +1069,56 @@ class HValue : public ZoneObject {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P0(I) \
|
||||||
|
static I* New(Zone* zone, HValue* context) { \
|
||||||
|
return new(zone) I(context); \
|
||||||
|
}
|
||||||
|
|
||||||
#define DECLARE_INSTRUCTION_FACTORY_P0(I) \
|
#define DECLARE_INSTRUCTION_FACTORY_P0(I) \
|
||||||
static I* New(Zone* zone, HValue* context) { \
|
static I* New(Zone* zone, HValue* context) { \
|
||||||
return new(zone) I(); \
|
return new(zone) I(); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P1(I, P1) \
|
||||||
|
static I* New(Zone* zone, HValue* context, P1 p1) { \
|
||||||
|
return new(zone) I(context, p1); \
|
||||||
|
}
|
||||||
|
|
||||||
#define DECLARE_INSTRUCTION_FACTORY_P1(I, P1) \
|
#define DECLARE_INSTRUCTION_FACTORY_P1(I, P1) \
|
||||||
static I* New(Zone* zone, HValue* context, P1 p1) { \
|
static I* New(Zone* zone, HValue* context, P1 p1) { \
|
||||||
return new(zone) I(p1); \
|
return new(zone) I(p1); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P2(I, P1, P2) \
|
||||||
|
static I* New(Zone* zone, HValue* context, P1 p1, P2 p2) { \
|
||||||
|
return new(zone) I(context, p1, p2); \
|
||||||
|
}
|
||||||
|
|
||||||
#define DECLARE_INSTRUCTION_FACTORY_P2(I, P1, P2) \
|
#define DECLARE_INSTRUCTION_FACTORY_P2(I, P1, P2) \
|
||||||
static I* New(Zone* zone, HValue* context, P1 p1, P2 p2) { \
|
static I* New(Zone* zone, HValue* context, P1 p1, P2 p2) { \
|
||||||
return new(zone) I(p1, p2); \
|
return new(zone) I(p1, p2); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(I, P1, P2, P3) \
|
||||||
|
static I* New(Zone* zone, HValue* context, P1 p1, P2 p2, P3 p3) { \
|
||||||
|
return new(zone) I(context, p1, p2, p3); \
|
||||||
|
}
|
||||||
|
|
||||||
#define DECLARE_INSTRUCTION_FACTORY_P3(I, P1, P2, P3) \
|
#define DECLARE_INSTRUCTION_FACTORY_P3(I, P1, P2, P3) \
|
||||||
static I* New(Zone* zone, HValue* context, P1 p1, P2 p2, P3 p3) { \
|
static I* New(Zone* zone, HValue* context, P1 p1, P2 p2, P3 p3) { \
|
||||||
return new(zone) I(p1, p2, p3); \
|
return new(zone) I(p1, p2, p3); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(I, P1, P2, P3, P4) \
|
||||||
|
static I* New(Zone* zone, \
|
||||||
|
HValue* context, \
|
||||||
|
P1 p1, \
|
||||||
|
P2 p2, \
|
||||||
|
P3 p3, \
|
||||||
|
P4 p4) { \
|
||||||
|
return new(zone) I(context, p1, p2, p3, p4); \
|
||||||
|
}
|
||||||
|
|
||||||
#define DECLARE_INSTRUCTION_FACTORY_P4(I, P1, P2, P3, P4) \
|
#define DECLARE_INSTRUCTION_FACTORY_P4(I, P1, P2, P3, P4) \
|
||||||
static I* New(Zone* zone, \
|
static I* New(Zone* zone, \
|
||||||
HValue* context, \
|
HValue* context, \
|
||||||
@ -1099,6 +1129,17 @@ class HValue : public ZoneObject {
|
|||||||
return new(zone) I(p1, p2, p3, p4); \
|
return new(zone) I(p1, p2, p3, p4); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(I, P1, P2, P3, P4, P5) \
|
||||||
|
static I* New(Zone* zone, \
|
||||||
|
HValue* context, \
|
||||||
|
P1 p1, \
|
||||||
|
P2 p2, \
|
||||||
|
P3 p3, \
|
||||||
|
P4 p4, \
|
||||||
|
P5 p5) { \
|
||||||
|
return new(zone) I(context, p1, p2, p3, p4, p5); \
|
||||||
|
}
|
||||||
|
|
||||||
#define DECLARE_INSTRUCTION_FACTORY_P5(I, P1, P2, P3, P4, P5) \
|
#define DECLARE_INSTRUCTION_FACTORY_P5(I, P1, P2, P3, P4, P5) \
|
||||||
static I* New(Zone* zone, \
|
static I* New(Zone* zone, \
|
||||||
HValue* context, \
|
HValue* context, \
|
||||||
@ -1213,6 +1254,10 @@ class HControlInstruction : public HInstruction {
|
|||||||
SetSuccessorAt(1, swap);
|
SetSuccessorAt(1, swap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
virtual void Verify() V8_OVERRIDE;
|
||||||
|
#endif
|
||||||
|
|
||||||
DECLARE_ABSTRACT_INSTRUCTION(ControlInstruction)
|
DECLARE_ABSTRACT_INSTRUCTION(ControlInstruction)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4008,18 +4053,18 @@ class HArithmeticBinaryOperation : public HBinaryOperation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class HCompareGeneric V8_FINAL : public HBinaryOperation {
|
class HCompareGenericAndBranch V8_FINAL
|
||||||
|
: public HTemplateControlInstruction<2, 3> {
|
||||||
public:
|
public:
|
||||||
HCompareGeneric(HValue* context,
|
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P3(HCompareGenericAndBranch,
|
||||||
HValue* left,
|
HValue*, HValue*, Token::Value);
|
||||||
HValue* right,
|
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(HCompareGenericAndBranch,
|
||||||
Token::Value token)
|
HValue*, HValue*, Token::Value,
|
||||||
: HBinaryOperation(context, left, right, HType::Boolean()),
|
HBasicBlock*, HBasicBlock*);
|
||||||
token_(token) {
|
HValue* context() { return OperandAt(0); }
|
||||||
ASSERT(Token::IsCompareOp(token));
|
HValue* left() { return OperandAt(1); }
|
||||||
set_representation(Representation::Tagged());
|
HValue* right() { return OperandAt(2); }
|
||||||
SetAllSideEffects();
|
Token::Value token() const { return token_; }
|
||||||
}
|
|
||||||
|
|
||||||
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
|
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
|
||||||
return index == 0
|
return index == 0
|
||||||
@ -4027,12 +4072,34 @@ class HCompareGeneric V8_FINAL : public HBinaryOperation {
|
|||||||
: representation();
|
: representation();
|
||||||
}
|
}
|
||||||
|
|
||||||
Token::Value token() const { return token_; }
|
void set_observed_input_representation(Representation left,
|
||||||
|
Representation right) {
|
||||||
|
observed_input_representation_[0] = left;
|
||||||
|
observed_input_representation_[1] = right;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
|
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
|
||||||
|
|
||||||
DECLARE_CONCRETE_INSTRUCTION(CompareGeneric)
|
DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
HCompareGenericAndBranch(HValue* context,
|
||||||
|
HValue* left,
|
||||||
|
HValue* right,
|
||||||
|
Token::Value token,
|
||||||
|
HBasicBlock* true_target = NULL,
|
||||||
|
HBasicBlock* false_target = NULL)
|
||||||
|
: token_(token) {
|
||||||
|
set_representation(Representation::Tagged());
|
||||||
|
SetAllSideEffects();
|
||||||
|
SetOperandAt(0, context);
|
||||||
|
SetOperandAt(1, left);
|
||||||
|
SetOperandAt(2, right);
|
||||||
|
SetSuccessorAt(0, true_target);
|
||||||
|
SetSuccessorAt(1, false_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
Representation observed_input_representation_[2];
|
||||||
Token::Value token_;
|
Token::Value token_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4234,9 +4301,9 @@ class HIsUndetectableAndBranch V8_FINAL : public HUnaryControlInstruction {
|
|||||||
class HStringCompareAndBranch : public HTemplateControlInstruction<2, 3> {
|
class HStringCompareAndBranch : public HTemplateControlInstruction<2, 3> {
|
||||||
public:
|
public:
|
||||||
HStringCompareAndBranch(HValue* context,
|
HStringCompareAndBranch(HValue* context,
|
||||||
HValue* left,
|
HValue* left,
|
||||||
HValue* right,
|
HValue* right,
|
||||||
Token::Value token)
|
Token::Value token)
|
||||||
: token_(token) {
|
: token_(token) {
|
||||||
ASSERT(Token::IsCompareOp(token));
|
ASSERT(Token::IsCompareOp(token));
|
||||||
SetOperandAt(0, context);
|
SetOperandAt(0, context);
|
||||||
|
@ -2655,7 +2655,6 @@ void EffectContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
|
|||||||
|
|
||||||
void EffectContext::ReturnControl(HControlInstruction* instr,
|
void EffectContext::ReturnControl(HControlInstruction* instr,
|
||||||
BailoutId ast_id) {
|
BailoutId ast_id) {
|
||||||
ASSERT(!instr->HasObservableSideEffects());
|
|
||||||
HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
|
HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
|
||||||
HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
|
HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
|
||||||
instr->SetSuccessorAt(0, empty_true);
|
instr->SetSuccessorAt(0, empty_true);
|
||||||
@ -2696,7 +2695,6 @@ void ValueContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
|
|||||||
|
|
||||||
|
|
||||||
void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
|
void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
|
||||||
ASSERT(!instr->HasObservableSideEffects());
|
|
||||||
if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
|
if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
|
||||||
return owner()->Bailout(kBadValueContextForArgumentsObjectValue);
|
return owner()->Bailout(kBadValueContextForArgumentsObjectValue);
|
||||||
}
|
}
|
||||||
@ -2754,7 +2752,9 @@ void TestContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
|
|||||||
|
|
||||||
|
|
||||||
void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
|
void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
|
||||||
ASSERT(!instr->HasObservableSideEffects());
|
// We can ignore ObservableSideEffects here since both HGoto instructions
|
||||||
|
// insert a different Simulate, thus we will directly deoptimize into the
|
||||||
|
// correct branch.
|
||||||
HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
|
HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock();
|
||||||
HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
|
HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock();
|
||||||
instr->SetSuccessorAt(0, empty_true);
|
instr->SetSuccessorAt(0, empty_true);
|
||||||
@ -8201,12 +8201,11 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
|||||||
return ast_context()->ReturnControl(result, expr->id());
|
return ast_context()->ReturnControl(result, expr->id());
|
||||||
} else {
|
} else {
|
||||||
if (combined_rep.IsTagged() || combined_rep.IsNone()) {
|
if (combined_rep.IsTagged() || combined_rep.IsNone()) {
|
||||||
HCompareGeneric* result =
|
HCompareGenericAndBranch* result =
|
||||||
new(zone()) HCompareGeneric(context, left, right, op);
|
New<HCompareGenericAndBranch>(left, right, op);
|
||||||
result->set_observed_input_representation(1, left_rep);
|
result->set_observed_input_representation(left_rep, right_rep);
|
||||||
result->set_observed_input_representation(2, right_rep);
|
|
||||||
result->set_position(expr->position());
|
result->set_position(expr->position());
|
||||||
return ast_context()->ReturnInstruction(result, expr->id());
|
return ast_context()->ReturnControl(result, expr->id());
|
||||||
} else {
|
} else {
|
||||||
HCompareNumericAndBranch* result =
|
HCompareNumericAndBranch* result =
|
||||||
New<HCompareNumericAndBranch>(left, right, op);
|
New<HCompareNumericAndBranch>(left, right, op);
|
||||||
|
@ -3044,21 +3044,15 @@ void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoCmpT(LCmpT* instr) {
|
void LCodeGen::DoCompareGenericAndBranch(LCompareGenericAndBranch* instr) {
|
||||||
Token::Value op = instr->op();
|
Token::Value op = instr->op();
|
||||||
|
|
||||||
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
|
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
|
||||||
CallCode(ic, RelocInfo::CODE_TARGET, instr);
|
CallCode(ic, RelocInfo::CODE_TARGET, instr);
|
||||||
|
|
||||||
Condition condition = ComputeCompareCondition(op);
|
Condition condition = ComputeCompareCondition(op);
|
||||||
Label true_value, done;
|
|
||||||
__ test(eax, Operand(eax));
|
__ test(eax, Operand(eax));
|
||||||
__ j(condition, &true_value, Label::kNear);
|
EmitBranch(instr, condition);
|
||||||
__ mov(ToRegister(instr->result()), factory()->false_value());
|
|
||||||
__ jmp(&done, Label::kNear);
|
|
||||||
__ bind(&true_value);
|
|
||||||
__ mov(ToRegister(instr->result()), factory()->true_value());
|
|
||||||
__ bind(&done);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -682,12 +682,16 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
if (hinstr->HasObservableSideEffects()) {
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
||||||
|
if (!hinstr->IsControlInstruction()) {
|
||||||
|
ASSERT(hinstr->next()->IsSimulate());
|
||||||
|
HSimulate* sim = HSimulate::cast(hinstr->next());
|
||||||
|
pending_deoptimization_ast_id_ = sim->ast_id();
|
||||||
|
} else {
|
||||||
|
pending_deoptimization_ast_id_ = BailoutId::PendingMarker();
|
||||||
|
}
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
instruction_pending_deoptimization_environment_ = instr;
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
@ -1700,14 +1704,15 @@ LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
|
LInstruction* LChunkBuilder::DoCompareGenericAndBranch(
|
||||||
|
HCompareGenericAndBranch* instr) {
|
||||||
ASSERT(instr->left()->representation().IsSmiOrTagged());
|
ASSERT(instr->left()->representation().IsSmiOrTagged());
|
||||||
ASSERT(instr->right()->representation().IsSmiOrTagged());
|
ASSERT(instr->right()->representation().IsSmiOrTagged());
|
||||||
LOperand* context = UseFixed(instr->context(), esi);
|
LOperand* context = UseFixed(instr->context(), esi);
|
||||||
LOperand* left = UseFixed(instr->left(), edx);
|
LOperand* left = UseFixed(instr->left(), edx);
|
||||||
LOperand* right = UseFixed(instr->right(), eax);
|
LOperand* right = UseFixed(instr->right(), eax);
|
||||||
LCmpT* result = new(zone()) LCmpT(context, left, right);
|
return MarkAsCall(new(zone()) LCompareGenericAndBranch(context, left, right),
|
||||||
return MarkAsCall(DefineFixed(result, eax), instr);
|
instr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2626,7 +2631,8 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
|||||||
// If there is an instruction pending deoptimization environment create a
|
// If there is an instruction pending deoptimization environment create a
|
||||||
// lazy bailout instruction to capture the environment.
|
// lazy bailout instruction to capture the environment.
|
||||||
if (!pending_deoptimization_ast_id_.IsNone()) {
|
if (!pending_deoptimization_ast_id_.IsNone()) {
|
||||||
ASSERT(pending_deoptimization_ast_id_ == instr->ast_id());
|
ASSERT(pending_deoptimization_ast_id_ == instr->ast_id() ||
|
||||||
|
pending_deoptimization_ast_id_.IsPendingMarker());
|
||||||
LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
|
LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
|
||||||
LInstruction* result = AssignEnvironment(lazy_bailout);
|
LInstruction* result = AssignEnvironment(lazy_bailout);
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
// Store the lazy deopt environment with the instruction if needed. Right
|
||||||
|
@ -78,7 +78,7 @@ class LCodeGen;
|
|||||||
V(CmpObjectEqAndBranch) \
|
V(CmpObjectEqAndBranch) \
|
||||||
V(CmpHoleAndBranch) \
|
V(CmpHoleAndBranch) \
|
||||||
V(CmpMapAndBranch) \
|
V(CmpMapAndBranch) \
|
||||||
V(CmpT) \
|
V(CompareGenericAndBranch) \
|
||||||
V(ConstantD) \
|
V(ConstantD) \
|
||||||
V(ConstantE) \
|
V(ConstantE) \
|
||||||
V(ConstantI) \
|
V(ConstantI) \
|
||||||
@ -1073,16 +1073,17 @@ class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 2> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LCmpT V8_FINAL : public LTemplateInstruction<1, 3, 0> {
|
class LCompareGenericAndBranch V8_FINAL : public LControlInstruction<3, 0> {
|
||||||
public:
|
public:
|
||||||
LCmpT(LOperand* context, LOperand* left, LOperand* right) {
|
LCompareGenericAndBranch(LOperand* context, LOperand* left, LOperand* right) {
|
||||||
inputs_[0] = context;
|
inputs_[0] = context;
|
||||||
inputs_[1] = left;
|
inputs_[1] = left;
|
||||||
inputs_[2] = right;
|
inputs_[2] = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
|
DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch,
|
||||||
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
|
"compare-generic-and-branch")
|
||||||
|
DECLARE_HYDROGEN_ACCESSOR(CompareGenericAndBranch)
|
||||||
|
|
||||||
Token::Value op() const { return hydrogen()->token(); }
|
Token::Value op() const { return hydrogen()->token(); }
|
||||||
};
|
};
|
||||||
|
@ -1115,8 +1115,10 @@ class BailoutId {
|
|||||||
static BailoutId Declarations() { return BailoutId(kDeclarationsId); }
|
static BailoutId Declarations() { return BailoutId(kDeclarationsId); }
|
||||||
static BailoutId FirstUsable() { return BailoutId(kFirstUsableId); }
|
static BailoutId FirstUsable() { return BailoutId(kFirstUsableId); }
|
||||||
static BailoutId StubEntry() { return BailoutId(kStubEntryId); }
|
static BailoutId StubEntry() { return BailoutId(kStubEntryId); }
|
||||||
|
static BailoutId PendingMarker() { return BailoutId(kPendingMarkerId); }
|
||||||
|
|
||||||
bool IsNone() const { return id_ == kNoneId; }
|
bool IsNone() const { return id_ == kNoneId; }
|
||||||
|
bool IsPendingMarker() const { return id_ == kPendingMarkerId; }
|
||||||
bool operator==(const BailoutId& other) const { return id_ == other.id_; }
|
bool operator==(const BailoutId& other) const { return id_ == other.id_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1136,6 +1138,10 @@ class BailoutId {
|
|||||||
// Every compiled stub starts with this id.
|
// Every compiled stub starts with this id.
|
||||||
static const int kStubEntryId = 5;
|
static const int kStubEntryId = 5;
|
||||||
|
|
||||||
|
// For Control instructions we cannot verify the ast_id, since there is no
|
||||||
|
// 1:1 mapping but it corresponds to two simulates for each branch.
|
||||||
|
static const int kPendingMarkerId = 6;
|
||||||
|
|
||||||
int id_;
|
int id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2604,7 +2604,7 @@ void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LCodeGen::DoCmpT(LCmpT* instr) {
|
void LCodeGen::DoCompareGenericAndBranch(LCompareGenericAndBranch* instr) {
|
||||||
Token::Value op = instr->op();
|
Token::Value op = instr->op();
|
||||||
|
|
||||||
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
|
Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
|
||||||
@ -2613,12 +2613,7 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
|
|||||||
Condition condition = TokenToCondition(op, false);
|
Condition condition = TokenToCondition(op, false);
|
||||||
Label true_value, done;
|
Label true_value, done;
|
||||||
__ testq(rax, rax);
|
__ testq(rax, rax);
|
||||||
__ j(condition, &true_value, Label::kNear);
|
EmitBranch(instr, condition);
|
||||||
__ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
|
|
||||||
__ jmp(&done, Label::kNear);
|
|
||||||
__ bind(&true_value);
|
|
||||||
__ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
|
|
||||||
__ bind(&done);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -639,12 +639,18 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
if (hinstr->HasObservableSideEffects()) {
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
||||||
|
// For Control instructions we cannot verify the ast_id, since there is no
|
||||||
|
// 1:1 mapping but it corresponds to two simulates for each branch.
|
||||||
|
if (!hinstr->IsControlInstruction()) {
|
||||||
|
ASSERT(hinstr->next()->IsSimulate());
|
||||||
|
HSimulate* sim = HSimulate::cast(hinstr->next());
|
||||||
|
pending_deoptimization_ast_id_ = sim->ast_id();
|
||||||
|
} else {
|
||||||
|
pending_deoptimization_ast_id_ = BailoutId::PendingMarker();
|
||||||
|
}
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
instruction_pending_deoptimization_environment_ = instr;
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
@ -1600,13 +1606,13 @@ LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
|
LInstruction* LChunkBuilder::DoCompareGenericAndBranch(
|
||||||
|
HCompareGenericAndBranch* instr) {
|
||||||
ASSERT(instr->left()->representation().IsTagged());
|
ASSERT(instr->left()->representation().IsTagged());
|
||||||
ASSERT(instr->right()->representation().IsTagged());
|
ASSERT(instr->right()->representation().IsTagged());
|
||||||
LOperand* left = UseFixed(instr->left(), rdx);
|
LOperand* left = UseFixed(instr->left(), rdx);
|
||||||
LOperand* right = UseFixed(instr->right(), rax);
|
LOperand* right = UseFixed(instr->right(), rax);
|
||||||
LCmpT* result = new(zone()) LCmpT(left, right);
|
return MarkAsCall(new(zone()) LCompareGenericAndBranch(left, right), instr);
|
||||||
return MarkAsCall(DefineFixed(result, rax), instr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2434,7 +2440,9 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
|||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
// If there is an instruction pending deoptimization environment create a
|
||||||
// lazy bailout instruction to capture the environment.
|
// lazy bailout instruction to capture the environment.
|
||||||
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
|
if (!pending_deoptimization_ast_id_.IsNone()) {
|
||||||
|
ASSERT(pending_deoptimization_ast_id_ == instr->ast_id() ||
|
||||||
|
pending_deoptimization_ast_id_.IsPendingMarker());
|
||||||
LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
|
LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
|
||||||
LInstruction* result = AssignEnvironment(lazy_bailout);
|
LInstruction* result = AssignEnvironment(lazy_bailout);
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
// Store the lazy deopt environment with the instruction if needed. Right
|
||||||
|
@ -76,7 +76,7 @@ class LCodeGen;
|
|||||||
V(CmpObjectEqAndBranch) \
|
V(CmpObjectEqAndBranch) \
|
||||||
V(CmpHoleAndBranch) \
|
V(CmpHoleAndBranch) \
|
||||||
V(CmpMapAndBranch) \
|
V(CmpMapAndBranch) \
|
||||||
V(CmpT) \
|
V(CompareGenericAndBranch) \
|
||||||
V(ConstantD) \
|
V(ConstantD) \
|
||||||
V(ConstantE) \
|
V(ConstantE) \
|
||||||
V(ConstantI) \
|
V(ConstantI) \
|
||||||
@ -1019,9 +1019,9 @@ class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 2> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
class LCompareGenericAndBranch V8_FINAL : public LControlInstruction<2, 0> {
|
||||||
public:
|
public:
|
||||||
LCmpT(LOperand* left, LOperand* right) {
|
LCompareGenericAndBranch(LOperand* left, LOperand* right) {
|
||||||
inputs_[0] = left;
|
inputs_[0] = left;
|
||||||
inputs_[1] = right;
|
inputs_[1] = right;
|
||||||
}
|
}
|
||||||
@ -1029,8 +1029,9 @@ class LCmpT V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
|||||||
LOperand* left() { return inputs_[0]; }
|
LOperand* left() { return inputs_[0]; }
|
||||||
LOperand* right() { return inputs_[1]; }
|
LOperand* right() { return inputs_[1]; }
|
||||||
|
|
||||||
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
|
DECLARE_CONCRETE_INSTRUCTION(CompareGenericAndBranch,
|
||||||
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
|
"compare-generic-and-branch")
|
||||||
|
DECLARE_HYDROGEN_ACCESSOR(CompareGenericAndBranch)
|
||||||
|
|
||||||
Token::Value op() const { return hydrogen()->token(); }
|
Token::Value op() const { return hydrogen()->token(); }
|
||||||
};
|
};
|
||||||
|
226
test/mjsunit/compare-generic.js
Normal file
226
test/mjsunit/compare-generic.js
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// Copyright 2013 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax
|
||||||
|
|
||||||
|
// Test the correct placement of the simulates after HCompareGenericAndBranch:
|
||||||
|
function Checker() {
|
||||||
|
this.str = "1";
|
||||||
|
var toStringCalled = 0;
|
||||||
|
var toStringExpected = 0;
|
||||||
|
this.toString = function() {
|
||||||
|
toStringCalled++;
|
||||||
|
return this.str;
|
||||||
|
};
|
||||||
|
this.check = function() {
|
||||||
|
toStringExpected++;
|
||||||
|
assertEquals(toStringExpected, toStringCalled);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var left = new Checker();
|
||||||
|
var right = new Checker();
|
||||||
|
|
||||||
|
// This test compares a < b against x < y where
|
||||||
|
// x/y are objects providing a/b as toString. In the end we
|
||||||
|
// check if the observable side effects match our
|
||||||
|
// expectations, thus we make sure that we deopted to a
|
||||||
|
// simulate after the comparison was done.
|
||||||
|
function test(a,b) {
|
||||||
|
left.str = a;
|
||||||
|
right.str = b;
|
||||||
|
if (left >= right) {
|
||||||
|
assertTrue(a >= b);
|
||||||
|
} else {
|
||||||
|
assertFalse(a >= b);
|
||||||
|
}
|
||||||
|
left.check();
|
||||||
|
right.check();
|
||||||
|
}
|
||||||
|
|
||||||
|
test("ab","abc");
|
||||||
|
test("ab","a");
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test("a","ab");
|
||||||
|
test(1,"a");
|
||||||
|
test("a","ab");
|
||||||
|
%OptimizeFunctionOnNextCall(test);
|
||||||
|
test("a","ab");
|
||||||
|
test("a",1);
|
||||||
|
test("ab","a");
|
||||||
|
|
||||||
|
|
||||||
|
// Use generic compare in value, effect and test contexts
|
||||||
|
|
||||||
|
function Checker2() {
|
||||||
|
var valueOfCalled = 0;
|
||||||
|
this.valueOf = function() {
|
||||||
|
return valueOfCalled++;
|
||||||
|
}
|
||||||
|
this.valueOfCalled = function() {
|
||||||
|
return valueOfCalled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var x = new Checker2();
|
||||||
|
var y = new Checker2();
|
||||||
|
|
||||||
|
if (x < y || y < x || x <= y) {
|
||||||
|
assertEquals(3, x.valueOfCalled());
|
||||||
|
assertEquals(3, y.valueOfCalled());
|
||||||
|
assertEquals(1, (x < y) + (y < x) + (x <= y))
|
||||||
|
assertEquals(6, x.valueOfCalled());
|
||||||
|
assertEquals(6, y.valueOfCalled());
|
||||||
|
x < y;
|
||||||
|
assertEquals(7, x.valueOfCalled());
|
||||||
|
assertEquals(7, y.valueOfCalled());
|
||||||
|
x < y;
|
||||||
|
assertEquals(8, x.valueOfCalled());
|
||||||
|
assertEquals(8, y.valueOfCalled());
|
||||||
|
var res;
|
||||||
|
if (x <= y) {
|
||||||
|
res = 1+(x > {});
|
||||||
|
} else {
|
||||||
|
assertTrue(false);
|
||||||
|
res = y <= {};
|
||||||
|
}
|
||||||
|
assertEquals(10, x.valueOfCalled());
|
||||||
|
assertEquals(9, y.valueOfCalled());
|
||||||
|
assertEquals(1, res);
|
||||||
|
assertFalse(x < y);
|
||||||
|
|
||||||
|
var tb = 0, fb = 0;
|
||||||
|
var val = 0;
|
||||||
|
for (var i = 1; i < 10; i++) {
|
||||||
|
var res = 0;
|
||||||
|
// uses x,y in control context
|
||||||
|
if (x <= y) {
|
||||||
|
res += val;
|
||||||
|
assertTrue(x <= y);
|
||||||
|
// adds 1 + 0, uses x in value context
|
||||||
|
res += 1+(x > {});
|
||||||
|
tb++;
|
||||||
|
assertEquals(fb, tb);
|
||||||
|
} else {
|
||||||
|
res += val;
|
||||||
|
assertFalse(x < y);
|
||||||
|
// adds 1, uses y in value context, increments 2
|
||||||
|
res += (y <= y);
|
||||||
|
// use x in value context, increments x once to make it equal to y again
|
||||||
|
x + 2;
|
||||||
|
assertEquals(fb, tb);
|
||||||
|
fb++;
|
||||||
|
}
|
||||||
|
assertEquals(11+(2*i)+tb+fb, x.valueOfCalled());
|
||||||
|
assertEquals(10+(2*i)+(2*fb), y.valueOfCalled());
|
||||||
|
assertEquals(1 + val, res);
|
||||||
|
// Triggers deopt inside branch.
|
||||||
|
if (i%5 == 0) val += 0.5;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertTrue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function t(a,b) { return (b < a) - (a < b); };
|
||||||
|
function f() {
|
||||||
|
x = new Checker2();
|
||||||
|
y = new Checker2();
|
||||||
|
var tb = 0, fb = 0;
|
||||||
|
var val = 0;
|
||||||
|
for (var i = 1; i < 10; i++) {
|
||||||
|
var res = 0;
|
||||||
|
if ((x < y) + (y < x)) {
|
||||||
|
res += val;
|
||||||
|
res += x<0;
|
||||||
|
fb++;
|
||||||
|
} else {
|
||||||
|
res += val;
|
||||||
|
res += y<0;
|
||||||
|
tb++;
|
||||||
|
}
|
||||||
|
assertEquals(0, res + 1 - res - 1);
|
||||||
|
assertEquals((2*i)+fb, x.valueOfCalled());
|
||||||
|
assertEquals((2*i)+tb, y.valueOfCalled());
|
||||||
|
assertEquals(val, res);
|
||||||
|
if (i%4 == 0) val += 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
f();
|
||||||
|
|
||||||
|
var a = {valueOf: function(){this.conv++; return 1;}};
|
||||||
|
var b = {valueOf: function(){this.conv++; return 2;}};
|
||||||
|
|
||||||
|
a.conv = 0;
|
||||||
|
b.conv = 0;
|
||||||
|
|
||||||
|
function f2(a,b,d1,d2) {
|
||||||
|
var runs = 0;
|
||||||
|
if ((a < b) + (a < b)) {
|
||||||
|
if (d2) { d2 += 0.2; }
|
||||||
|
runs++;
|
||||||
|
} else {
|
||||||
|
assertUnreachable();
|
||||||
|
}
|
||||||
|
assertEquals(1, runs);
|
||||||
|
if (a > b) {
|
||||||
|
assertUnreachable();
|
||||||
|
} else {
|
||||||
|
if (d1) { d1 += 0.2; }
|
||||||
|
runs++;
|
||||||
|
}
|
||||||
|
assertEquals(2, runs);
|
||||||
|
}
|
||||||
|
|
||||||
|
f2(a,b);
|
||||||
|
f2(a,b);
|
||||||
|
|
||||||
|
%OptimizeFunctionOnNextCall(f2);
|
||||||
|
f2(a,b);
|
||||||
|
f2(a,b);
|
||||||
|
|
||||||
|
f2(a,b,true);
|
||||||
|
f2(a,b);
|
||||||
|
|
||||||
|
%OptimizeFunctionOnNextCall(f2);
|
||||||
|
f2(a,b);
|
||||||
|
f2(a,b);
|
||||||
|
|
||||||
|
f2(a,b,false,true);
|
||||||
|
f2(a,b);
|
||||||
|
|
||||||
|
assertEquals(30, a.conv);
|
||||||
|
assertEquals(30, b.conv);
|
||||||
|
|
||||||
|
b.valueOf = function(){ return {}; }
|
||||||
|
try {
|
||||||
|
f2(a,b);
|
||||||
|
} catch(e) {
|
||||||
|
res = e.stack;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user