| Index: src/hydrogen.cc
|
| ===================================================================
|
| --- src/hydrogen.cc (revision 7948)
|
| +++ src/hydrogen.cc (working copy)
|
| @@ -115,12 +115,13 @@
|
| }
|
|
|
|
|
| -HDeoptimize* HBasicBlock::CreateDeoptimize() {
|
| +HDeoptimize* HBasicBlock::CreateDeoptimize(
|
| + HDeoptimize::UseEnvironment has_uses) {
|
| ASSERT(HasEnvironment());
|
| + if (has_uses == HDeoptimize::kNoUses) return new(zone()) HDeoptimize(0);
|
| +
|
| HEnvironment* environment = last_environment();
|
| -
|
| HDeoptimize* instr = new(zone()) HDeoptimize(environment->length());
|
| -
|
| for (int i = 0; i < environment->length(); i++) {
|
| HValue* val = environment->values()->at(i);
|
| instr->AddEnvironmentValue(val);
|
| @@ -1020,13 +1021,13 @@
|
| }
|
|
|
|
|
| -HValueMap::HValueMap(const HValueMap* other)
|
| +HValueMap::HValueMap(Zone* zone, const HValueMap* other)
|
| : array_size_(other->array_size_),
|
| lists_size_(other->lists_size_),
|
| count_(other->count_),
|
| present_flags_(other->present_flags_),
|
| - array_(ZONE->NewArray<HValueMapListElement>(other->array_size_)),
|
| - lists_(ZONE->NewArray<HValueMapListElement>(other->lists_size_)),
|
| + array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
|
| + lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
|
| free_list_head_(other->free_list_head_) {
|
| memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
|
| memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
|
| @@ -1239,13 +1240,49 @@
|
| }
|
|
|
|
|
| +// Simple sparse set with O(1) add, contains, and clear.
|
| +class SparseSet {
|
| + public:
|
| + SparseSet(Zone* zone, int capacity)
|
| + : capacity_(capacity),
|
| + length_(0),
|
| + dense_(zone->NewArray<int>(capacity)),
|
| + sparse_(zone->NewArray<int>(capacity)) {}
|
| +
|
| + bool Contains(int n) const {
|
| + ASSERT(0 <= n && n < capacity_);
|
| + int d = sparse_[n];
|
| + return 0 <= d && d < length_ && dense_[d] == n;
|
| + }
|
| +
|
| + bool Add(int n) {
|
| + if (Contains(n)) return false;
|
| + dense_[length_] = n;
|
| + sparse_[n] = length_;
|
| + ++length_;
|
| + return true;
|
| + }
|
| +
|
| + void Clear() { length_ = 0; }
|
| +
|
| + private:
|
| + int capacity_;
|
| + int length_;
|
| + int* dense_;
|
| + int* sparse_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SparseSet);
|
| +};
|
| +
|
| +
|
| class HGlobalValueNumberer BASE_EMBEDDED {
|
| public:
|
| explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
|
| : graph_(graph),
|
| info_(info),
|
| - block_side_effects_(graph_->blocks()->length()),
|
| - loop_side_effects_(graph_->blocks()->length()) {
|
| + block_side_effects_(graph->blocks()->length()),
|
| + loop_side_effects_(graph->blocks()->length()),
|
| + visited_on_paths_(graph->zone(), graph->blocks()->length()) {
|
| ASSERT(info->isolate()->heap()->allow_allocation(false));
|
| block_side_effects_.AddBlock(0, graph_->blocks()->length());
|
| loop_side_effects_.AddBlock(0, graph_->blocks()->length());
|
| @@ -1257,6 +1294,8 @@
|
| void Analyze();
|
|
|
| private:
|
| + int CollectSideEffectsOnPathsToDominatedBlock(HBasicBlock* dominator,
|
| + HBasicBlock* dominated);
|
| void AnalyzeBlock(HBasicBlock* block, HValueMap* map);
|
| void ComputeBlockSideEffects();
|
| void LoopInvariantCodeMotion();
|
| @@ -1278,6 +1317,10 @@
|
|
|
| // A map of loop header block IDs to their loop's side effects.
|
| ZoneList<int> loop_side_effects_;
|
| +
|
| + // Used when collecting side effects on paths from dominator to
|
| + // dominated.
|
| + SparseSet visited_on_paths_;
|
| };
|
|
|
|
|
| @@ -1413,8 +1456,27 @@
|
| }
|
|
|
|
|
| +int HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
|
| + HBasicBlock* dominator, HBasicBlock* dominated) {
|
| + int side_effects = 0;
|
| + for (int i = 0; i < dominated->predecessors()->length(); ++i) {
|
| + HBasicBlock* block = dominated->predecessors()->at(i);
|
| + if (dominator->block_id() < block->block_id() &&
|
| + block->block_id() < dominated->block_id() &&
|
| + visited_on_paths_.Add(block->block_id())) {
|
| + side_effects |= block_side_effects_[block->block_id()];
|
| + side_effects |= CollectSideEffectsOnPathsToDominatedBlock(
|
| + dominator, block);
|
| + }
|
| + }
|
| + return side_effects;
|
| +}
|
| +
|
| +
|
| void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
|
| - TraceGVN("Analyzing block B%d\n", block->block_id());
|
| + TraceGVN("Analyzing block B%d%s\n",
|
| + block->block_id(),
|
| + block->IsLoopHeader() ? " (loop header)" : "");
|
|
|
| // If this is a loop header kill everything killed by the loop.
|
| if (block->IsLoopHeader()) {
|
| @@ -1455,23 +1517,18 @@
|
| // No need to copy the map for the last child in the dominator tree.
|
| HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone());
|
|
|
| - // If the dominated block is not a successor to this block we have to
|
| - // kill everything killed on any path between this block and the
|
| - // dominated block. Note we rely on the block ordering.
|
| - bool is_successor = false;
|
| - int predecessor_count = dominated->predecessors()->length();
|
| - for (int j = 0; !is_successor && j < predecessor_count; ++j) {
|
| - is_successor = (dominated->predecessors()->at(j) == block);
|
| + // Kill everything killed on any path between this block and the
|
| + // dominated block.
|
| + // We don't have to traverse these paths if the value map is
|
| + // already empty.
|
| + // If the range of block ids (block_id, dominated_id) is empty
|
| + // there are no such paths.
|
| + if (!successor_map->IsEmpty() &&
|
| + block->block_id() + 1 < dominated->block_id()) {
|
| + visited_on_paths_.Clear();
|
| + successor_map->Kill(CollectSideEffectsOnPathsToDominatedBlock(block,
|
| + dominated));
|
| }
|
| -
|
| - if (!is_successor) {
|
| - int side_effects = 0;
|
| - for (int j = block->block_id() + 1; j < dominated->block_id(); ++j) {
|
| - side_effects |= block_side_effects_[j];
|
| - }
|
| - successor_map->Kill(side_effects);
|
| - }
|
| -
|
| AnalyzeBlock(dominated, successor_map);
|
| }
|
| }
|
| @@ -1802,6 +1859,12 @@
|
| ASSERT(value->IsConstant());
|
| value->DeleteAndReplaceWith(NULL);
|
| }
|
| +
|
| + // The only purpose of a HForceRepresentation is to represent the value
|
| + // after the (possible) HChange instruction. We make it disappear.
|
| + if (value->IsForceRepresentation()) {
|
| + value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
|
| + }
|
| }
|
|
|
|
|
| @@ -2229,14 +2292,21 @@
|
|
|
| // Set the initial values of parameters including "this". "This" has
|
| // parameter index 0.
|
| - int count = scope->num_parameters() + 1;
|
| - for (int i = 0; i < count; ++i) {
|
| + ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
|
| +
|
| + for (int i = 0; i < environment()->parameter_count(); ++i) {
|
| HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
|
| environment()->Bind(i, parameter);
|
| }
|
|
|
| - // Set the initial values of stack-allocated locals.
|
| - for (int i = count; i < environment()->length(); ++i) {
|
| + // First special is HContext.
|
| + HInstruction* context = AddInstruction(new(zone()) HContext);
|
| + environment()->BindContext(context);
|
| +
|
| + // Initialize specials and locals to undefined.
|
| + for (int i = environment()->parameter_count() + 1;
|
| + i < environment()->length();
|
| + ++i) {
|
| environment()->Bind(i, undefined_constant);
|
| }
|
|
|
| @@ -2483,7 +2553,9 @@
|
| // Unconditionally deoptimize on the first non-smi compare.
|
| clause->RecordTypeFeedback(oracle());
|
| if (!clause->IsSmiCompare()) {
|
| - current_block()->FinishExitWithDeoptimization();
|
| + // Finish with deoptimize and add uses of enviroment values to
|
| + // account for invisible uses.
|
| + current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
|
| set_current_block(NULL);
|
| break;
|
| }
|
| @@ -2519,9 +2591,11 @@
|
| // Identify the block where normal (non-fall-through) control flow
|
| // goes to.
|
| HBasicBlock* normal_block = NULL;
|
| - if (clause->is_default() && last_block != NULL) {
|
| - normal_block = last_block;
|
| - last_block = NULL; // Cleared to indicate we've handled it.
|
| + if (clause->is_default()) {
|
| + if (last_block != NULL) {
|
| + normal_block = last_block;
|
| + last_block = NULL; // Cleared to indicate we've handled it.
|
| + }
|
| } else if (!curr_test_block->end()->IsDeoptimize()) {
|
| normal_block = curr_test_block->end()->FirstSuccessor();
|
| curr_test_block = curr_test_block->end()->SecondSuccessor();
|
| @@ -2593,17 +2667,18 @@
|
| int osr_entry_id = statement->OsrEntryId();
|
| // We want the correct environment at the OsrEntry instruction. Build
|
| // it explicitly. The expression stack should be empty.
|
| - int count = environment()->length();
|
| - ASSERT(count ==
|
| - (environment()->parameter_count() + environment()->local_count()));
|
| - for (int i = 0; i < count; ++i) {
|
| - HUnknownOSRValue* unknown = new(zone()) HUnknownOSRValue;
|
| - AddInstruction(unknown);
|
| - environment()->Bind(i, unknown);
|
| + ASSERT(environment()->ExpressionStackIsEmpty());
|
| + for (int i = 0; i < environment()->length(); ++i) {
|
| + HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
|
| + AddInstruction(osr_value);
|
| + environment()->Bind(i, osr_value);
|
| }
|
|
|
| AddSimulate(osr_entry_id);
|
| AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
|
| + HContext* context = new(zone()) HContext;
|
| + AddInstruction(context);
|
| + environment()->BindContext(context);
|
| current_block()->Goto(loop_predecessor);
|
| loop_predecessor->SetJoinId(statement->EntryId());
|
| set_current_block(loop_predecessor);
|
| @@ -2886,12 +2961,12 @@
|
|
|
| HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
|
| ASSERT(var->IsContextSlot());
|
| - HInstruction* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| int length = info()->scope()->ContextChainLength(var->scope());
|
| while (length-- > 0) {
|
| - context = new(zone()) HOuterContext(context);
|
| - AddInstruction(context);
|
| + HInstruction* context_instruction = new(zone()) HOuterContext(context);
|
| + AddInstruction(context_instruction);
|
| + context = context_instruction;
|
| }
|
| return context;
|
| }
|
| @@ -2930,8 +3005,7 @@
|
| HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole);
|
| ast_context()->ReturnInstruction(instr, expr->id());
|
| } else {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HGlobalObject* global_object = new(zone()) HGlobalObject(context);
|
| AddInstruction(global_object);
|
| HLoadGlobalGeneric* instr =
|
| @@ -2974,8 +3048,7 @@
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HObjectLiteral* literal =
|
| new(zone()) HObjectLiteral(context,
|
| expr->constant_properties(),
|
| @@ -3157,8 +3230,7 @@
|
| HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
|
| Handle<String> name,
|
| HValue* value) {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| return new(zone()) HStoreNamedGeneric(
|
| context,
|
| object,
|
| @@ -3232,7 +3304,7 @@
|
| // know about and do not want to handle ones we've never seen. Otherwise
|
| // use a generic IC.
|
| if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
|
| - current_block()->FinishExitWithDeoptimization();
|
| + current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
|
| } else {
|
| HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
|
| instr->set_position(expr->position());
|
| @@ -3334,8 +3406,7 @@
|
| AddInstruction(instr);
|
| if (instr->HasSideEffects()) AddSimulate(ast_id);
|
| } else {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HGlobalObject* global_object = new(zone()) HGlobalObject(context);
|
| AddInstruction(global_object);
|
| HStoreGlobalGeneric* instr =
|
| @@ -3560,8 +3631,7 @@
|
| Property* expr) {
|
| ASSERT(expr->key()->IsPropertyName());
|
| Handle<Object> name = expr->key()->AsLiteral()->handle();
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| return new(zone()) HLoadNamedGeneric(context, obj, name);
|
| }
|
|
|
| @@ -3591,8 +3661,7 @@
|
|
|
| HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
|
| HValue* key) {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| return new(zone()) HLoadKeyedGeneric(context, object, key);
|
| }
|
|
|
| @@ -3667,8 +3736,7 @@
|
| HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
|
| HValue* key,
|
| HValue* value) {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| return new(zone()) HStoreKeyedGeneric(
|
| context,
|
| object,
|
| @@ -3721,6 +3789,11 @@
|
| HLoadExternalArrayPointer* external_elements =
|
| new(zone()) HLoadExternalArrayPointer(elements);
|
| AddInstruction(external_elements);
|
| + if (expr->external_array_type() == kExternalPixelArray) {
|
| + HClampToUint8* clamp = new(zone()) HClampToUint8(val);
|
| + AddInstruction(clamp);
|
| + val = clamp;
|
| + }
|
| return new(zone()) HStoreKeyedSpecializedArrayElement(
|
| external_elements,
|
| key,
|
| @@ -3796,17 +3869,13 @@
|
| if (expr->IsArrayLength()) {
|
| HValue* array = Pop();
|
| AddInstruction(new(zone()) HCheckNonSmi(array));
|
| - AddInstruction(new(zone()) HCheckInstanceType(array,
|
| - JS_ARRAY_TYPE,
|
| - JS_ARRAY_TYPE));
|
| + AddInstruction(HCheckInstanceType::NewIsJSArray(array));
|
| instr = new(zone()) HJSArrayLength(array);
|
|
|
| } else if (expr->IsStringLength()) {
|
| HValue* string = Pop();
|
| AddInstruction(new(zone()) HCheckNonSmi(string));
|
| - AddInstruction(new(zone()) HCheckInstanceType(string,
|
| - FIRST_STRING_TYPE,
|
| - LAST_STRING_TYPE));
|
| + AddInstruction(HCheckInstanceType::NewIsString(string));
|
| instr = new(zone()) HStringLength(string);
|
| } else if (expr->IsStringAccess()) {
|
| CHECK_ALIVE(VisitForValue(expr->key()));
|
| @@ -3919,10 +3988,9 @@
|
| // know about and do not want to handle ones we've never seen. Otherwise
|
| // use a generic IC.
|
| if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
|
| - current_block()->FinishExitWithDeoptimization();
|
| + current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
|
| } else {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
|
| call->set_position(expr->position());
|
| PreProcessCall(call);
|
| @@ -4349,8 +4417,7 @@
|
|
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| call = PreProcessCall(
|
| new(zone()) HCallKeyed(context, key, argument_count));
|
| call->set_position(expr->position());
|
| @@ -4389,8 +4456,7 @@
|
| // When the target has a custom call IC generator, use the IC,
|
| // because it is likely to generate better code. Also use the IC
|
| // when a primitive receiver check is required.
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| call = PreProcessCall(
|
| new(zone()) HCallNamed(context, name, argument_count));
|
| } else {
|
| @@ -4407,8 +4473,7 @@
|
| return;
|
|
|
| } else {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| call = PreProcessCall(
|
| new(zone()) HCallNamed(context, name, argument_count));
|
| }
|
| @@ -4437,9 +4502,8 @@
|
| if (known_global_function) {
|
| // Push the global object instead of the global receiver because
|
| // code generated by the full code generator expects it.
|
| - HContext* context = new(zone()) HContext;
|
| + HValue* context = environment()->LookupContext();
|
| HGlobalObject* global_object = new(zone()) HGlobalObject(context);
|
| - AddInstruction(context);
|
| PushAndAdd(global_object);
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| @@ -4461,8 +4525,7 @@
|
| call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
|
| argument_count));
|
| } else {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| PushAndAdd(new(zone()) HGlobalObject(context));
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| @@ -4472,9 +4535,8 @@
|
| }
|
|
|
| } else {
|
| - HContext* context = new(zone()) HContext;
|
| + HValue* context = environment()->LookupContext();
|
| HGlobalObject* global_object = new(zone()) HGlobalObject(context);
|
| - AddInstruction(context);
|
| AddInstruction(global_object);
|
| PushAndAdd(new(zone()) HGlobalReceiver(global_object));
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| @@ -4497,8 +4559,7 @@
|
| CHECK_ALIVE(VisitForValue(expr->expression()));
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
|
|
| // The constructor is both an operand to the instruction and an argument
|
| // to the construct call.
|
| @@ -4568,121 +4629,169 @@
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - Token::Value op = expr->op();
|
| - if (op == Token::VOID) {
|
| + switch (expr->op()) {
|
| + case Token::DELETE: return VisitDelete(expr);
|
| + case Token::VOID: return VisitVoid(expr);
|
| + case Token::TYPEOF: return VisitTypeof(expr);
|
| + case Token::ADD: return VisitAdd(expr);
|
| + case Token::SUB: return VisitSub(expr);
|
| + case Token::BIT_NOT: return VisitBitNot(expr);
|
| + case Token::NOT: return VisitNot(expr);
|
| + default: UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
|
| + Property* prop = expr->expression()->AsProperty();
|
| + Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
| + if (prop == NULL && var == NULL) {
|
| + // Result of deleting non-property, non-variable reference is true.
|
| + // Evaluate the subexpression for side effects.
|
| CHECK_ALIVE(VisitForEffect(expr->expression()));
|
| - ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| - } else if (op == Token::DELETE) {
|
| - Property* prop = expr->expression()->AsProperty();
|
| - Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
| - if (prop == NULL && var == NULL) {
|
| - // Result of deleting non-property, non-variable reference is true.
|
| - // Evaluate the subexpression for side effects.
|
| - CHECK_ALIVE(VisitForEffect(expr->expression()));
|
| - ast_context()->ReturnValue(graph()->GetConstantTrue());
|
| - } else if (var != NULL &&
|
| - !var->is_global() &&
|
| - var->AsSlot() != NULL &&
|
| - var->AsSlot()->type() != Slot::LOOKUP) {
|
| - // Result of deleting non-global, non-dynamic variables is false.
|
| - // The subexpression does not have side effects.
|
| + ast_context()->ReturnValue(graph()->GetConstantTrue());
|
| + } else if (var != NULL &&
|
| + !var->is_global() &&
|
| + var->AsSlot() != NULL &&
|
| + var->AsSlot()->type() != Slot::LOOKUP) {
|
| + // Result of deleting non-global, non-dynamic variables is false.
|
| + // The subexpression does not have side effects.
|
| + ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| + } else if (prop != NULL) {
|
| + if (prop->is_synthetic()) {
|
| + // Result of deleting parameters is false, even when they rewrite
|
| + // to accesses on the arguments object.
|
| ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| - } else if (prop != NULL) {
|
| - if (prop->is_synthetic()) {
|
| - // Result of deleting parameters is false, even when they rewrite
|
| - // to accesses on the arguments object.
|
| - ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| - } else {
|
| - CHECK_ALIVE(VisitForValue(prop->obj()));
|
| - CHECK_ALIVE(VisitForValue(prop->key()));
|
| - HValue* key = Pop();
|
| - HValue* obj = Pop();
|
| - HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| - }
|
| - } else if (var->is_global()) {
|
| - return Bailout("delete with global variable");
|
| } else {
|
| - return Bailout("delete with non-global variable");
|
| + CHECK_ALIVE(VisitForValue(prop->obj()));
|
| + CHECK_ALIVE(VisitForValue(prop->key()));
|
| + HValue* key = Pop();
|
| + HValue* obj = Pop();
|
| + HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
|
| + ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
| - } else if (op == Token::NOT) {
|
| - if (ast_context()->IsTest()) {
|
| - TestContext* context = TestContext::cast(ast_context());
|
| - VisitForControl(expr->expression(),
|
| - context->if_false(),
|
| - context->if_true());
|
| - } else if (ast_context()->IsValue()) {
|
| - HBasicBlock* materialize_false = graph()->CreateBasicBlock();
|
| - HBasicBlock* materialize_true = graph()->CreateBasicBlock();
|
| - CHECK_BAILOUT(VisitForControl(expr->expression(),
|
| - materialize_false,
|
| - materialize_true));
|
| + } else if (var->is_global()) {
|
| + Bailout("delete with global variable");
|
| + } else {
|
| + Bailout("delete with non-global variable");
|
| + }
|
| +}
|
|
|
| - if (materialize_false->HasPredecessor()) {
|
| - materialize_false->SetJoinId(expr->expression()->id());
|
| - set_current_block(materialize_false);
|
| - Push(graph()->GetConstantFalse());
|
| - } else {
|
| - materialize_false = NULL;
|
| - }
|
|
|
| - if (materialize_true->HasPredecessor()) {
|
| - materialize_true->SetJoinId(expr->expression()->id());
|
| - set_current_block(materialize_true);
|
| - Push(graph()->GetConstantTrue());
|
| - } else {
|
| - materialize_true = NULL;
|
| - }
|
| +void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForEffect(expr->expression()));
|
| + ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| +}
|
|
|
| - HBasicBlock* join =
|
| - CreateJoin(materialize_false, materialize_true, expr->id());
|
| - set_current_block(join);
|
| - if (join != NULL) ast_context()->ReturnValue(Pop());
|
| - } else {
|
| - ASSERT(ast_context()->IsEffect());
|
| - VisitForEffect(expr->expression());
|
| - }
|
|
|
| - } else if (op == Token::TYPEOF) {
|
| - CHECK_ALIVE(VisitForTypeOf(expr->expression()));
|
| - HValue* value = Pop();
|
| - ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
|
| +void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForTypeOf(expr->expression()));
|
| + HValue* value = Pop();
|
| + ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
|
| +}
|
|
|
| +
|
| +void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* value = Pop();
|
| + HInstruction* instr = new(zone()) HMul(value, graph_->GetConstant1());
|
| + ast_context()->ReturnInstruction(instr, expr->id());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitSub(UnaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* value = Pop();
|
| + HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
|
| + TypeInfo info = oracle()->UnaryType(expr);
|
| + Representation rep = ToRepresentation(info);
|
| + TraceRepresentation(expr->op(), info, instr, rep);
|
| + instr->AssumeRepresentation(rep);
|
| + ast_context()->ReturnInstruction(instr, expr->id());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* value = Pop();
|
| + HInstruction* instr = new(zone()) HBitNot(value);
|
| + ast_context()->ReturnInstruction(instr, expr->id());
|
| +}
|
| +
|
| +
|
| +void HGraphBuilder::VisitNot(UnaryOperation* expr) {
|
| + // TODO(svenpanne) Perhaps a switch/virtual function is nicer here.
|
| + if (ast_context()->IsTest()) {
|
| + TestContext* context = TestContext::cast(ast_context());
|
| + VisitForControl(expr->expression(),
|
| + context->if_false(),
|
| + context->if_true());
|
| + return;
|
| + }
|
| +
|
| + if (ast_context()->IsEffect()) {
|
| + VisitForEffect(expr->expression());
|
| + return;
|
| + }
|
| +
|
| + ASSERT(ast_context()->IsValue());
|
| + HBasicBlock* materialize_false = graph()->CreateBasicBlock();
|
| + HBasicBlock* materialize_true = graph()->CreateBasicBlock();
|
| + CHECK_BAILOUT(VisitForControl(expr->expression(),
|
| + materialize_false,
|
| + materialize_true));
|
| +
|
| + if (materialize_false->HasPredecessor()) {
|
| + materialize_false->SetJoinId(expr->expression()->id());
|
| + set_current_block(materialize_false);
|
| + Push(graph()->GetConstantFalse());
|
| } else {
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| - HValue* value = Pop();
|
| - HInstruction* instr = NULL;
|
| - switch (op) {
|
| - case Token::BIT_NOT:
|
| - instr = new(zone()) HBitNot(value);
|
| - break;
|
| - case Token::SUB:
|
| - instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
|
| - break;
|
| - case Token::ADD:
|
| - instr = new(zone()) HMul(value, graph_->GetConstant1());
|
| - break;
|
| - default:
|
| - return Bailout("Value: unsupported unary operation");
|
| - break;
|
| - }
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + materialize_false = NULL;
|
| }
|
| +
|
| + if (materialize_true->HasPredecessor()) {
|
| + materialize_true->SetJoinId(expr->expression()->id());
|
| + set_current_block(materialize_true);
|
| + Push(graph()->GetConstantTrue());
|
| + } else {
|
| + materialize_true = NULL;
|
| + }
|
| +
|
| + HBasicBlock* join =
|
| + CreateJoin(materialize_false, materialize_true, expr->id());
|
| + set_current_block(join);
|
| + if (join != NULL) ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildIncrement(HValue* value,
|
| - bool increment,
|
| +HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
|
| CountOperation* expr) {
|
| - HConstant* delta = increment
|
| - ? graph_->GetConstant1()
|
| - : graph_->GetConstantMinus1();
|
| - HInstruction* instr = new(zone()) HAdd(value, delta);
|
| - Representation rep = ToRepresentation(oracle()->IncrementType(expr));
|
| + // The input to the count operation is on top of the expression stack.
|
| + TypeInfo info = oracle()->IncrementType(expr);
|
| + Representation rep = ToRepresentation(info);
|
| if (rep.IsTagged()) {
|
| rep = Representation::Integer32();
|
| }
|
| - AssumeRepresentation(instr, rep);
|
| +
|
| + if (returns_original_input) {
|
| + // 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
|
| + // as the return value.
|
| + HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
|
| + AddInstruction(number_input);
|
| + Push(number_input);
|
| + }
|
| +
|
| + // The addition has no side effects, so we do not need
|
| + // to simulate the expression stack after this instruction.
|
| + // Any later failures deopt to the load of the input or earlier.
|
| + HConstant* delta = (expr->op() == Token::INC)
|
| + ? graph_->GetConstant1()
|
| + : graph_->GetConstantMinus1();
|
| + HInstruction* instr = new(zone()) HAdd(Top(), delta);
|
| + TraceRepresentation(expr->op(), info, instr, rep);
|
| + instr->AssumeRepresentation(rep);
|
| + AddInstruction(instr);
|
| return instr;
|
| }
|
|
|
| @@ -4695,18 +4804,25 @@
|
| VariableProxy* proxy = target->AsVariableProxy();
|
| Variable* var = proxy->AsVariable();
|
| Property* prop = target->AsProperty();
|
| - ASSERT(var == NULL || prop == NULL);
|
| - bool inc = expr->op() == Token::INC;
|
| + if (var == NULL && prop == NULL) {
|
| + return Bailout("invalid lhs in count operation");
|
| + }
|
|
|
| + // Match the full code generator stack by simulating an extra stack
|
| + // element for postfix operations in a non-effect context. The return
|
| + // value is ToNumber(input).
|
| + bool returns_original_input =
|
| + expr->is_postfix() && !ast_context()->IsEffect();
|
| + HValue* input = NULL; // ToNumber(original_input).
|
| + HValue* after = NULL; // The result after incrementing or decrementing.
|
| +
|
| if (var != NULL) {
|
| + // Argument of the count operation is a variable, not a property.
|
| + ASSERT(prop == NULL);
|
| CHECK_ALIVE(VisitForValue(target));
|
|
|
| - // Match the full code generator stack by simulating an extra stack
|
| - // element for postfix operations in a non-effect context.
|
| - bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
|
| - HValue* before = has_extra ? Top() : Pop();
|
| - HInstruction* after = BuildIncrement(before, inc, expr);
|
| - AddInstruction(after);
|
| + after = BuildIncrement(returns_original_input, expr);
|
| + input = returns_original_input ? Top() : Pop();
|
| Push(after);
|
|
|
| if (var->is_global()) {
|
| @@ -4726,20 +4842,16 @@
|
| } else {
|
| return Bailout("lookup variable in count operation");
|
| }
|
| - Drop(has_extra ? 2 : 1);
|
| - ast_context()->ReturnValue(expr->is_postfix() ? before : after);
|
|
|
| - } else if (prop != NULL) {
|
| + } else {
|
| + // Argument of the count operation is a property.
|
| + ASSERT(prop != NULL);
|
| prop->RecordTypeFeedback(oracle());
|
|
|
| if (prop->key()->IsPropertyName()) {
|
| // Named property.
|
| + if (returns_original_input) Push(graph_->GetConstantUndefined());
|
|
|
| - // Match the full code generator stack by simulating an extra stack
|
| - // element for postfix operations in a non-effect context.
|
| - bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
|
| - if (has_extra) Push(graph_->GetConstantUndefined());
|
| -
|
| CHECK_ALIVE(VisitForValue(prop->obj()));
|
| HValue* obj = Top();
|
|
|
| @@ -4754,11 +4866,8 @@
|
| PushAndAdd(load);
|
| if (load->HasSideEffects()) AddSimulate(expr->CountId());
|
|
|
| - HValue* before = Pop();
|
| - // There is no deoptimization to after the increment, so we don't need
|
| - // to simulate the expression stack after this instruction.
|
| - HInstruction* after = BuildIncrement(before, inc, expr);
|
| - AddInstruction(after);
|
| + after = BuildIncrement(returns_original_input, expr);
|
| + input = Pop();
|
|
|
| HInstruction* store = BuildStoreNamed(obj, after, prop);
|
| AddInstruction(store);
|
| @@ -4767,20 +4876,13 @@
|
| // of the operation, and the placeholder with the original value if
|
| // necessary.
|
| environment()->SetExpressionStackAt(0, after);
|
| - if (has_extra) environment()->SetExpressionStackAt(1, before);
|
| + if (returns_original_input) environment()->SetExpressionStackAt(1, input);
|
| if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| - Drop(has_extra ? 2 : 1);
|
|
|
| - ast_context()->ReturnValue(expr->is_postfix() ? before : after);
|
| -
|
| } else {
|
| // Keyed property.
|
| + if (returns_original_input) Push(graph_->GetConstantUndefined());
|
|
|
| - // Match the full code generator stack by simulate an extra stack element
|
| - // for postfix operations in a non-effect context.
|
| - bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
|
| - if (has_extra) Push(graph_->GetConstantUndefined());
|
| -
|
| CHECK_ALIVE(VisitForValue(prop->obj()));
|
| CHECK_ALIVE(VisitForValue(prop->key()));
|
| HValue* obj = environment()->ExpressionStackAt(1);
|
| @@ -4790,11 +4892,8 @@
|
| PushAndAdd(load);
|
| if (load->HasSideEffects()) AddSimulate(expr->CountId());
|
|
|
| - HValue* before = Pop();
|
| - // There is no deoptimization to after the increment, so we don't need
|
| - // to simulate the expression stack after this instruction.
|
| - HInstruction* after = BuildIncrement(before, inc, expr);
|
| - AddInstruction(after);
|
| + after = BuildIncrement(returns_original_input, expr);
|
| + input = Pop();
|
|
|
| expr->RecordTypeFeedback(oracle());
|
| HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
|
| @@ -4805,24 +4904,32 @@
|
| // original value if necessary.
|
| Drop(1);
|
| environment()->SetExpressionStackAt(0, after);
|
| - if (has_extra) environment()->SetExpressionStackAt(1, before);
|
| + if (returns_original_input) environment()->SetExpressionStackAt(1, input);
|
| if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
|
| - Drop(has_extra ? 2 : 1);
|
| -
|
| - ast_context()->ReturnValue(expr->is_postfix() ? before : after);
|
| }
|
| + }
|
|
|
| - } else {
|
| - return Bailout("invalid lhs in count operation");
|
| - }
|
| + Drop(returns_original_input ? 2 : 1);
|
| + ast_context()->ReturnValue(expr->is_postfix() ? input : after);
|
| }
|
|
|
|
|
| +HCompareSymbolEq* HGraphBuilder::BuildSymbolCompare(HValue* left,
|
| + HValue* right,
|
| + Token::Value op) {
|
| + ASSERT(op == Token::EQ || op == Token::EQ_STRICT);
|
| + AddInstruction(new(zone()) HCheckNonSmi(left));
|
| + AddInstruction(HCheckInstanceType::NewIsSymbol(left));
|
| + AddInstruction(new(zone()) HCheckNonSmi(right));
|
| + AddInstruction(HCheckInstanceType::NewIsSymbol(right));
|
| + return new(zone()) HCompareSymbolEq(left, right, op);
|
| +}
|
| +
|
| +
|
| HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
|
| HValue* index) {
|
| AddInstruction(new(zone()) HCheckNonSmi(string));
|
| - AddInstruction(new(zone()) HCheckInstanceType(
|
| - string, FIRST_STRING_TYPE, LAST_STRING_TYPE));
|
| + AddInstruction(HCheckInstanceType::NewIsString(string));
|
| HStringLength* length = new(zone()) HStringLength(string);
|
| AddInstruction(length);
|
| AddInstruction(new(zone()) HBoundsCheck(index, length));
|
| @@ -4834,54 +4941,7 @@
|
| HValue* left,
|
| HValue* right) {
|
| TypeInfo info = oracle()->BinaryType(expr);
|
| - HInstruction* instr = NULL;
|
| - switch (expr->op()) {
|
| - case Token::ADD:
|
| - if (info.IsString()) {
|
| - AddInstruction(new(zone()) HCheckNonSmi(left));
|
| - AddInstruction(new(zone()) HCheckInstanceType(
|
| - left, FIRST_STRING_TYPE, LAST_STRING_TYPE));
|
| - AddInstruction(new(zone()) HCheckNonSmi(right));
|
| - AddInstruction(new(zone()) HCheckInstanceType(
|
| - right, FIRST_STRING_TYPE, LAST_STRING_TYPE));
|
| - instr = new(zone()) HStringAdd(left, right);
|
| - } else {
|
| - instr = new(zone()) HAdd(left, right);
|
| - }
|
| - break;
|
| - case Token::SUB:
|
| - instr = new(zone()) HSub(left, right);
|
| - break;
|
| - case Token::MUL:
|
| - instr = new(zone()) HMul(left, right);
|
| - break;
|
| - case Token::MOD:
|
| - instr = new(zone()) HMod(left, right);
|
| - break;
|
| - case Token::DIV:
|
| - instr = new(zone()) HDiv(left, right);
|
| - break;
|
| - case Token::BIT_XOR:
|
| - instr = new(zone()) HBitXor(left, right);
|
| - break;
|
| - case Token::BIT_AND:
|
| - instr = new(zone()) HBitAnd(left, right);
|
| - break;
|
| - case Token::BIT_OR:
|
| - instr = new(zone()) HBitOr(left, right);
|
| - break;
|
| - case Token::SAR:
|
| - instr = new(zone()) HSar(left, right);
|
| - break;
|
| - case Token::SHR:
|
| - instr = new(zone()) HShr(left, right);
|
| - break;
|
| - case Token::SHL:
|
| - instr = new(zone()) HShl(left, right);
|
| - break;
|
| - default:
|
| - UNREACHABLE();
|
| - }
|
| + HInstruction* instr = BuildBinaryOperation(expr->op(), left, right, info);
|
| // If we hit an uninitialized binary op stub we will get type info
|
| // for a smi operation. If one of the operands is a constant string
|
| // do not generate code assuming it is a smi operation.
|
| @@ -4890,19 +4950,47 @@
|
| (right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
|
| return instr;
|
| }
|
| - if (FLAG_trace_representation) {
|
| - PrintF("Info: %s/%s\n", info.ToString(), ToRepresentation(info).Mnemonic());
|
| - }
|
| Representation rep = ToRepresentation(info);
|
| // We only generate either int32 or generic tagged bitwise operations.
|
| if (instr->IsBitwiseBinaryOperation() && rep.IsDouble()) {
|
| rep = Representation::Integer32();
|
| }
|
| - AssumeRepresentation(instr, rep);
|
| + TraceRepresentation(expr->op(), info, instr, rep);
|
| + instr->AssumeRepresentation(rep);
|
| return instr;
|
| }
|
|
|
|
|
| +HInstruction* HGraphBuilder::BuildBinaryOperation(
|
| + Token::Value op, HValue* left, HValue* right, TypeInfo info) {
|
| + switch (op) {
|
| + case Token::ADD:
|
| + if (info.IsString()) {
|
| + AddInstruction(new(zone()) HCheckNonSmi(left));
|
| + AddInstruction(HCheckInstanceType::NewIsString(left));
|
| + AddInstruction(new(zone()) HCheckNonSmi(right));
|
| + AddInstruction(HCheckInstanceType::NewIsString(right));
|
| + return new(zone()) HStringAdd(left, right);
|
| + } else {
|
| + return new(zone()) HAdd(left, right);
|
| + }
|
| + case Token::SUB: return new(zone()) HSub(left, right);
|
| + case Token::MUL: return new(zone()) HMul(left, right);
|
| + case Token::MOD: return new(zone()) HMod(left, right);
|
| + case Token::DIV: return new(zone()) HDiv(left, right);
|
| + case Token::BIT_XOR: return new(zone()) HBitXor(left, right);
|
| + case Token::BIT_AND: return new(zone()) HBitAnd(left, right);
|
| + case Token::BIT_OR: return new(zone()) HBitOr(left, right);
|
| + case Token::SAR: return new(zone()) HSar(left, right);
|
| + case Token::SHR: return new(zone()) HShr(left, right);
|
| + case Token::SHL: return new(zone()) HShl(left, right);
|
| + default:
|
| + UNREACHABLE();
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +
|
| // Check for the form (%_ClassOf(foo) === 'BarClass').
|
| static bool IsClassOfTest(CompareOperation* expr) {
|
| if (expr->op() != Token::EQ_STRICT) return false;
|
| @@ -4921,128 +5009,141 @@
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - if (expr->op() == Token::COMMA) {
|
| - CHECK_ALIVE(VisitForEffect(expr->left()));
|
| - // Visit the right subexpression in the same AST context as the entire
|
| - // expression.
|
| - Visit(expr->right());
|
| + switch (expr->op()) {
|
| + case Token::COMMA: return VisitComma(expr);
|
| + case Token::OR: return VisitAndOr(expr, false);
|
| + case Token::AND: return VisitAndOr(expr, true);
|
| + default: return VisitCommon(expr);
|
| + }
|
| +}
|
|
|
| - } else if (expr->op() == Token::AND || expr->op() == Token::OR) {
|
| - bool is_logical_and = (expr->op() == Token::AND);
|
| - if (ast_context()->IsTest()) {
|
| - TestContext* context = TestContext::cast(ast_context());
|
| - // Translate left subexpression.
|
| - HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| - if (is_logical_and) {
|
| - CHECK_BAILOUT(VisitForControl(expr->left(),
|
| - eval_right,
|
| - context->if_false()));
|
| - } else {
|
| - CHECK_BAILOUT(VisitForControl(expr->left(),
|
| - context->if_true(),
|
| - eval_right));
|
| - }
|
|
|
| - // Translate right subexpression by visiting it in the same AST
|
| - // context as the entire expression.
|
| - if (eval_right->HasPredecessor()) {
|
| - eval_right->SetJoinId(expr->RightId());
|
| - set_current_block(eval_right);
|
| - Visit(expr->right());
|
| - }
|
| +void HGraphBuilder::VisitComma(BinaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForEffect(expr->left()));
|
| + // Visit the right subexpression in the same AST context as the entire
|
| + // expression.
|
| + Visit(expr->right());
|
| +}
|
|
|
| - } else if (ast_context()->IsValue()) {
|
| - CHECK_ALIVE(VisitForValue(expr->left()));
|
| - ASSERT(current_block() != NULL);
|
|
|
| - // We need an extra block to maintain edge-split form.
|
| - HBasicBlock* empty_block = graph()->CreateBasicBlock();
|
| - HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| - HTest* test = is_logical_and
|
| - ? new(zone()) HTest(Top(), eval_right, empty_block)
|
| - : new(zone()) HTest(Top(), empty_block, eval_right);
|
| - current_block()->Finish(test);
|
| +void HGraphBuilder::VisitAndOr(BinaryOperation* expr, bool is_logical_and) {
|
| + if (ast_context()->IsTest()) {
|
| + TestContext* context = TestContext::cast(ast_context());
|
| + // Translate left subexpression.
|
| + HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| + if (is_logical_and) {
|
| + CHECK_BAILOUT(VisitForControl(expr->left(),
|
| + eval_right,
|
| + context->if_false()));
|
| + } else {
|
| + CHECK_BAILOUT(VisitForControl(expr->left(),
|
| + context->if_true(),
|
| + eval_right));
|
| + }
|
|
|
| + // Translate right subexpression by visiting it in the same AST
|
| + // context as the entire expression.
|
| + if (eval_right->HasPredecessor()) {
|
| + eval_right->SetJoinId(expr->RightId());
|
| set_current_block(eval_right);
|
| - Drop(1); // Value of the left subexpression.
|
| - CHECK_BAILOUT(VisitForValue(expr->right()));
|
| + Visit(expr->right());
|
| + }
|
|
|
| - HBasicBlock* join_block =
|
| - CreateJoin(empty_block, current_block(), expr->id());
|
| - set_current_block(join_block);
|
| - ast_context()->ReturnValue(Pop());
|
| + } else if (ast_context()->IsValue()) {
|
| + CHECK_ALIVE(VisitForValue(expr->left()));
|
| + ASSERT(current_block() != NULL);
|
|
|
| + // We need an extra block to maintain edge-split form.
|
| + HBasicBlock* empty_block = graph()->CreateBasicBlock();
|
| + HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| + HTest* test = is_logical_and
|
| + ? new(zone()) HTest(Top(), eval_right, empty_block)
|
| + : new(zone()) HTest(Top(), empty_block, eval_right);
|
| + current_block()->Finish(test);
|
| +
|
| + set_current_block(eval_right);
|
| + Drop(1); // Value of the left subexpression.
|
| + CHECK_BAILOUT(VisitForValue(expr->right()));
|
| +
|
| + HBasicBlock* join_block =
|
| + CreateJoin(empty_block, current_block(), expr->id());
|
| + set_current_block(join_block);
|
| + ast_context()->ReturnValue(Pop());
|
| +
|
| + } else {
|
| + ASSERT(ast_context()->IsEffect());
|
| + // In an effect context, we don't need the value of the left subexpression,
|
| + // only its control flow and side effects. We need an extra block to
|
| + // maintain edge-split form.
|
| + HBasicBlock* empty_block = graph()->CreateBasicBlock();
|
| + HBasicBlock* right_block = graph()->CreateBasicBlock();
|
| + if (is_logical_and) {
|
| + CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
|
| } else {
|
| - ASSERT(ast_context()->IsEffect());
|
| - // In an effect context, we don't need the value of the left
|
| - // subexpression, only its control flow and side effects. We need an
|
| - // extra block to maintain edge-split form.
|
| - HBasicBlock* empty_block = graph()->CreateBasicBlock();
|
| - HBasicBlock* right_block = graph()->CreateBasicBlock();
|
| - if (is_logical_and) {
|
| - CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
|
| - } else {
|
| - CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
|
| - }
|
| + CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
|
| + }
|
|
|
| - // TODO(kmillikin): Find a way to fix this. It's ugly that there are
|
| - // actually two empty blocks (one here and one inserted by
|
| - // TestContext::BuildBranch, and that they both have an HSimulate
|
| - // though the second one is not a merge node, and that we really have
|
| - // no good AST ID to put on that first HSimulate.
|
| - if (empty_block->HasPredecessor()) {
|
| - empty_block->SetJoinId(expr->id());
|
| - } else {
|
| - empty_block = NULL;
|
| - }
|
| + // TODO(kmillikin): Find a way to fix this. It's ugly that there are
|
| + // actually two empty blocks (one here and one inserted by
|
| + // TestContext::BuildBranch, and that they both have an HSimulate though the
|
| + // second one is not a merge node, and that we really have no good AST ID to
|
| + // put on that first HSimulate.
|
|
|
| - if (right_block->HasPredecessor()) {
|
| - right_block->SetJoinId(expr->RightId());
|
| - set_current_block(right_block);
|
| - CHECK_BAILOUT(VisitForEffect(expr->right()));
|
| - right_block = current_block();
|
| - } else {
|
| - right_block = NULL;
|
| - }
|
| + if (empty_block->HasPredecessor()) {
|
| + empty_block->SetJoinId(expr->id());
|
| + } else {
|
| + empty_block = NULL;
|
| + }
|
|
|
| - HBasicBlock* join_block =
|
| - CreateJoin(empty_block, right_block, expr->id());
|
| - set_current_block(join_block);
|
| - // We did not materialize any value in the predecessor environments,
|
| - // so there is no need to handle it here.
|
| + if (right_block->HasPredecessor()) {
|
| + right_block->SetJoinId(expr->RightId());
|
| + set_current_block(right_block);
|
| + CHECK_BAILOUT(VisitForEffect(expr->right()));
|
| + right_block = current_block();
|
| + } else {
|
| + right_block = NULL;
|
| }
|
|
|
| - } else {
|
| - CHECK_ALIVE(VisitForValue(expr->left()));
|
| - CHECK_ALIVE(VisitForValue(expr->right()));
|
| -
|
| - HValue* right = Pop();
|
| - HValue* left = Pop();
|
| - HInstruction* instr = BuildBinaryOperation(expr, left, right);
|
| - instr->set_position(expr->position());
|
| - ast_context()->ReturnInstruction(instr, expr->id());
|
| + HBasicBlock* join_block =
|
| + CreateJoin(empty_block, right_block, expr->id());
|
| + set_current_block(join_block);
|
| + // We did not materialize any value in the predecessor environments,
|
| + // so there is no need to handle it here.
|
| }
|
| }
|
|
|
|
|
| -void HGraphBuilder::AssumeRepresentation(HValue* value, Representation r) {
|
| - if (value->CheckFlag(HValue::kFlexibleRepresentation)) {
|
| - if (FLAG_trace_representation) {
|
| - PrintF("Assume representation for %s to be %s (%d)\n",
|
| - value->Mnemonic(),
|
| - r.Mnemonic(),
|
| - graph_->GetMaximumValueID());
|
| - }
|
| - value->ChangeRepresentation(r);
|
| - // The representation of the value is dictated by type feedback and
|
| - // will not be changed later.
|
| - value->ClearFlag(HValue::kFlexibleRepresentation);
|
| - } else if (FLAG_trace_representation) {
|
| - PrintF("No representation assumed\n");
|
| - }
|
| +void HGraphBuilder::VisitCommon(BinaryOperation* expr) {
|
| + CHECK_ALIVE(VisitForValue(expr->left()));
|
| + CHECK_ALIVE(VisitForValue(expr->right()));
|
| + HValue* right = Pop();
|
| + HValue* left = Pop();
|
| + HInstruction* instr = BuildBinaryOperation(expr, left, right);
|
| + instr->set_position(expr->position());
|
| + ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
|
|
|
|
| +void HGraphBuilder::TraceRepresentation(Token::Value op,
|
| + TypeInfo info,
|
| + HValue* value,
|
| + Representation rep) {
|
| + if (!FLAG_trace_representation) return;
|
| + // TODO(svenpanne) Under which circumstances are we actually not flexible?
|
| + // At first glance, this looks a bit weird...
|
| + bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
|
| + PrintF("Operation %s has type info %s, %schange representation assumption "
|
| + "for %s (ID %d) from %s to %s\n",
|
| + Token::Name(op),
|
| + info.ToString(),
|
| + flexible ? "" : " DO NOT ",
|
| + value->Mnemonic(),
|
| + graph_->GetMaximumValueID(),
|
| + value->representation().Mnemonic(),
|
| + rep.Mnemonic());
|
| +}
|
| +
|
| +
|
| Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
|
| if (info.IsSmi()) return Representation::Integer32();
|
| if (info.IsInteger32()) return Representation::Integer32();
|
| @@ -5121,8 +5222,7 @@
|
| // If the target is not null we have found a known global function that is
|
| // assumed to stay the same for this instanceof.
|
| if (target.is_null()) {
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| instr = new(zone()) HInstanceOf(context, left, right);
|
| } else {
|
| AddInstruction(new(zone()) HCheckFunction(right, target));
|
| @@ -5145,6 +5245,9 @@
|
| return Bailout("Unsupported non-primitive compare");
|
| break;
|
| }
|
| + } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
|
| + (op == Token::EQ || op == Token::EQ_STRICT)) {
|
| + instr = BuildSymbolCompare(left, right, op);
|
| } else {
|
| HCompare* compare = new(zone()) HCompare(left, right, op);
|
| Representation r = ToRepresentation(type_info);
|
| @@ -5266,7 +5369,11 @@
|
|
|
|
|
| void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
|
| - return Bailout("inlined runtime function: IsUndetectableObject");
|
| + ASSERT(call->arguments()->length() == 1);
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + HValue* value = Pop();
|
| + ast_context()->ReturnInstruction(new(zone()) HIsUndetectable(value),
|
| + call->id());
|
| }
|
|
|
|
|
| @@ -5398,8 +5505,7 @@
|
| void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
|
| ASSERT_EQ(2, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
|
| Drop(2);
|
| ast_context()->ReturnInstruction(result, call->id());
|
| @@ -5410,8 +5516,7 @@
|
| void HGraphBuilder::GenerateSubString(CallRuntime* call) {
|
| ASSERT_EQ(3, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
|
| Drop(3);
|
| ast_context()->ReturnInstruction(result, call->id());
|
| @@ -5422,8 +5527,7 @@
|
| void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
|
| ASSERT_EQ(2, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
|
| Drop(2);
|
| @@ -5435,8 +5539,7 @@
|
| void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
|
| ASSERT_EQ(4, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
|
| Drop(4);
|
| ast_context()->ReturnInstruction(result, call->id());
|
| @@ -5447,8 +5550,7 @@
|
| void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
|
| ASSERT_EQ(3, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
|
| Drop(3);
|
| @@ -5466,8 +5568,7 @@
|
| void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
|
| ASSERT_EQ(1, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
|
| Drop(1);
|
| @@ -5494,8 +5595,7 @@
|
| }
|
| CHECK_ALIVE(VisitForValue(call->arguments()->last()));
|
| HValue* function = Pop();
|
| - HContext* context = new HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HInvokeFunction* result =
|
| new(zone()) HInvokeFunction(context, function, arg_count);
|
| Drop(arg_count);
|
| @@ -5518,8 +5618,7 @@
|
| void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
|
| ASSERT_EQ(1, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
|
| result->set_transcendental_type(TranscendentalCache::SIN);
|
| @@ -5531,8 +5630,7 @@
|
| void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
|
| ASSERT_EQ(1, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
|
| result->set_transcendental_type(TranscendentalCache::COS);
|
| @@ -5544,8 +5642,7 @@
|
| void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
|
| ASSERT_EQ(1, call->arguments()->length());
|
| CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HContext* context = new(zone()) HContext;
|
| - AddInstruction(context);
|
| + HValue* context = environment()->LookupContext();
|
| HCallStub* result =
|
| new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
|
| result->set_transcendental_type(TranscendentalCache::LOG);
|
| @@ -5590,6 +5687,7 @@
|
| values_(0),
|
| assigned_variables_(4),
|
| parameter_count_(0),
|
| + specials_count_(1),
|
| local_count_(0),
|
| outer_(outer),
|
| pop_count_(0),
|
| @@ -5603,6 +5701,7 @@
|
| : values_(0),
|
| assigned_variables_(0),
|
| parameter_count_(0),
|
| + specials_count_(1),
|
| local_count_(0),
|
| outer_(NULL),
|
| pop_count_(0),
|
| @@ -5619,7 +5718,7 @@
|
| local_count_ = local_count;
|
|
|
| // Avoid reallocating the temporaries' backing store on the first Push.
|
| - int total = parameter_count + local_count + stack_height;
|
| + int total = parameter_count + specials_count_ + local_count + stack_height;
|
| values_.Initialize(total + 4);
|
| for (int i = 0; i < total; ++i) values_.Add(NULL);
|
| }
|
| @@ -5678,12 +5777,12 @@
|
|
|
|
|
| bool HEnvironment::HasExpressionAt(int index) const {
|
| - return index >= parameter_count_ + local_count_;
|
| + return index >= parameter_count_ + specials_count_ + local_count_;
|
| }
|
|
|
|
|
| bool HEnvironment::ExpressionStackIsEmpty() const {
|
| - int first_expression = parameter_count() + local_count();
|
| + int first_expression = parameter_count() + specials_count() + local_count();
|
| ASSERT(length() >= first_expression);
|
| return length() == first_expression;
|
| }
|
| @@ -5761,12 +5860,9 @@
|
| inner->SetValueAt(i, push);
|
| }
|
| }
|
| -
|
| - // Initialize the stack-allocated locals to undefined.
|
| - int local_base = arity + 1;
|
| - int local_count = function->scope()->num_stack_slots();
|
| - for (int i = 0; i < local_count; ++i) {
|
| - inner->SetValueAt(local_base + i, undefined);
|
| + inner->SetValueAt(arity + 1, outer->LookupContext());
|
| + for (int i = arity + 2; i < inner->length(); ++i) {
|
| + inner->SetValueAt(i, undefined);
|
| }
|
|
|
| inner->set_ast_id(AstNode::kFunctionEntryId);
|
| @@ -5777,8 +5873,11 @@
|
| void HEnvironment::PrintTo(StringStream* stream) {
|
| for (int i = 0; i < length(); i++) {
|
| if (i == 0) stream->Add("parameters\n");
|
| - if (i == parameter_count()) stream->Add("locals\n");
|
| - if (i == parameter_count() + local_count()) stream->Add("expressions");
|
| + if (i == parameter_count()) stream->Add("specials\n");
|
| + if (i == parameter_count() + specials_count()) stream->Add("locals\n");
|
| + if (i == parameter_count() + specials_count() + local_count()) {
|
| + stream->Add("expressions");
|
| + }
|
| HValue* val = values_.at(i);
|
| stream->Add("%d: ", i);
|
| if (val != NULL) {
|
| @@ -5873,10 +5972,11 @@
|
| Tag states_tag(this, "states");
|
| Tag locals_tag(this, "locals");
|
| int total = current->phis()->length();
|
| - trace_.Add("size %d\n", total);
|
| - trace_.Add("method \"None\"");
|
| + PrintIntProperty("size", current->phis()->length());
|
| + PrintStringProperty("method", "None");
|
| for (int j = 0; j < total; ++j) {
|
| HPhi* phi = current->phis()->at(j);
|
| + PrintIndent();
|
| trace_.Add("%d ", phi->merged_index());
|
| phi->PrintNameTo(&trace_);
|
| trace_.Add(" ");
|
| @@ -5891,6 +5991,7 @@
|
| while (instruction != NULL) {
|
| int bci = 0;
|
| int uses = instruction->UseCount();
|
| + PrintIndent();
|
| trace_.Add("%d %d ", bci, uses);
|
| instruction->PrintNameTo(&trace_);
|
| trace_.Add(" ");
|
| @@ -5910,6 +6011,7 @@
|
| for (int i = first_index; i <= last_index; ++i) {
|
| LInstruction* linstr = instructions->at(i);
|
| if (linstr != NULL) {
|
| + PrintIndent();
|
| trace_.Add("%d ",
|
| LifetimePosition::FromInstructionIndex(i).Value());
|
| linstr->PrintTo(&trace_);
|
| @@ -5945,6 +6047,7 @@
|
|
|
| void HTracer::TraceLiveRange(LiveRange* range, const char* type) {
|
| if (range != NULL && !range->IsEmpty()) {
|
| + PrintIndent();
|
| trace_.Add("%d %s", range->id(), type);
|
| if (range->HasRegisterAssigned()) {
|
| LOperand* op = range->CreateAssignedOperand();
|
|
|