| Index: src/ia32/codegen-ia32.cc
|
| diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
|
| index 82286829b770d60abf4b5aed0d4c69cffa5170fe..ba32e052b0dc501d3e708dbd09ce113c76974534 100644
|
| --- a/src/ia32/codegen-ia32.cc
|
| +++ b/src/ia32/codegen-ia32.cc
|
| @@ -4717,105 +4717,213 @@ void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
|
| }
|
|
|
|
|
| +void CodeGenerator::EmitSlotAssignment(Assignment* node) {
|
| +#ifdef DEBUG
|
| + int original_height = frame_->height();
|
| +#endif
|
| + Comment cmnt(masm_, "[ Variable Assignment");
|
| + Variable* var = node->target()->AsVariableProxy()->AsVariable();
|
| + ASSERT(var != NULL);
|
| + Slot* slot = var->slot();
|
| + ASSERT(slot != NULL);
|
| +
|
| + // Evaluate the right-hand side.
|
| + if (node->is_compound()) {
|
| + Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
|
| + frame()->Push(&result);
|
| + Load(node->value());
|
| +
|
| + bool overwrite_value =
|
| + (node->value()->AsBinaryOperation() != NULL &&
|
| + node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
|
| + GenericBinaryOperation(node->binary_op(),
|
| + node->type(),
|
| + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
|
| + } else {
|
| + Load(node->value());
|
| + }
|
| +
|
| + // Perform the assignment.
|
| + if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
|
| + CodeForSourcePosition(node->position());
|
| + StoreToSlot(slot,
|
| + node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
|
| + }
|
| + ASSERT(frame_->height() == original_height + 1);
|
| +}
|
| +
|
| +
|
| +void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
|
| +#ifdef DEBUG
|
| + int original_height = frame_->height();
|
| +#endif
|
| + Comment cmnt(masm_, "[ Named Property Assignment");
|
| + Variable* var = node->target()->AsVariableProxy()->AsVariable();
|
| + Property* prop = node->target()->AsProperty();
|
| + ASSERT(var == NULL || prop == NULL);
|
| +
|
| + // Initialize name and evaluate the receiver subexpression.
|
| + Handle<String> name;
|
| + if (var != NULL) {
|
| + name = var->name();
|
| + LoadGlobal();
|
| + } else {
|
| + Literal* lit = prop->key()->AsLiteral();
|
| + ASSERT(lit != NULL);
|
| + name = Handle<String>::cast(lit->handle());
|
| + Load(prop->obj());
|
| + }
|
| +
|
| + if (node->starts_initialization_block()) {
|
| + // Change to slow case in the beginning of an initialization block to
|
| + // avoid the quadratic behavior of repeatedly adding fast properties.
|
| + frame()->Dup();
|
| + Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1);
|
| + }
|
| +
|
| + if (node->ends_initialization_block()) {
|
| + // Add an extra copy of the receiver to the frame, so that it can be
|
| + // converted back to fast case after the assignment.
|
| + frame()->Dup();
|
| + }
|
| +
|
| + // Evaluate the right-hand side.
|
| + if (node->is_compound()) {
|
| + frame()->Dup();
|
| + Result value = EmitNamedLoad(name, var != NULL);
|
| + frame()->Push(&value);
|
| + Load(node->value());
|
| +
|
| + bool overwrite_value =
|
| + (node->value()->AsBinaryOperation() != NULL &&
|
| + node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
|
| + GenericBinaryOperation(node->binary_op(),
|
| + node->type(),
|
| + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
|
| + } else {
|
| + Load(node->value());
|
| + }
|
| +
|
| + // Perform the assignment. It is safe to ignore constants here.
|
| + ASSERT(var == NULL || var->mode() != Variable::CONST);
|
| + ASSERT(node->op() != Token::INIT_CONST);
|
| + CodeForSourcePosition(node->position());
|
| + Result answer = EmitNamedStore(name);
|
| + frame()->Push(&answer);
|
| +
|
| + if (node->ends_initialization_block()) {
|
| + // The argument to the runtime call is the extra copy of the receiver,
|
| + // which is below the value of the assignment. Swap the receiver and
|
| + // the value of the assignment expression.
|
| + Result result = frame()->Pop();
|
| + Result receiver = frame()->Pop();
|
| + frame()->Push(&result);
|
| + frame()->Push(&receiver);
|
| + Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
|
| + }
|
| +
|
| + ASSERT(frame()->height() == original_height + 1);
|
| +}
|
| +
|
| +
|
| +void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
|
| +#ifdef DEBUG
|
| + int original_height = frame_->height();
|
| +#endif
|
| + Comment cmnt(masm_, "[ Named Property Assignment");
|
| + Property* prop = node->target()->AsProperty();
|
| + ASSERT(prop != NULL);
|
| +
|
| + // Evaluate the receiver subexpression.
|
| + Load(prop->obj());
|
| +
|
| + if (node->starts_initialization_block()) {
|
| + // Change to slow case in the beginning of an initialization block to
|
| + // avoid the quadratic behavior of repeatedly adding fast properties.
|
| + frame_->Dup();
|
| + Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
|
| + }
|
| +
|
| + if (node->ends_initialization_block()) {
|
| + // Add an extra copy of the receiver to the frame, so that it can be
|
| + // converted back to fast case after the assignment.
|
| + frame_->Dup();
|
| + }
|
| +
|
| + // Evaluate the key subexpression.
|
| + Load(prop->key());
|
| +
|
| + // Evaluate the right-hand side.
|
| + if (node->is_compound()) {
|
| + // Duplicate receiver and key.
|
| + frame()->PushElementAt(1);
|
| + frame()->PushElementAt(1);
|
| + Result value = EmitKeyedLoad();
|
| + frame()->Push(&value);
|
| + Load(node->value());
|
| +
|
| + bool overwrite_value =
|
| + (node->value()->AsBinaryOperation() != NULL &&
|
| + node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
|
| + GenericBinaryOperation(node->binary_op(),
|
| + node->type(),
|
| + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
|
| + } else {
|
| + Load(node->value());
|
| + }
|
| +
|
| + // Perform the assignment. It is safe to ignore constants here.
|
| + ASSERT(node->op() != Token::INIT_CONST);
|
| + CodeForSourcePosition(node->position());
|
| + Result answer = EmitKeyedStore(prop->key()->type());
|
| + frame()->Push(&answer);
|
| +
|
| + if (node->ends_initialization_block()) {
|
| + // The argument to the runtime call is the extra copy of the receiver,
|
| + // which is below the value of the assignment. Swap the receiver and
|
| + // the value of the assignment expression.
|
| + Result result = frame()->Pop();
|
| + Result receiver = frame()->Pop();
|
| + frame()->Push(&result);
|
| + frame()->Push(&receiver);
|
| + Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
|
| + }
|
| +
|
| + ASSERT(frame()->height() == original_height + 1);
|
| +}
|
| +
|
| +
|
| void CodeGenerator::VisitAssignment(Assignment* node) {
|
| #ifdef DEBUG
|
| int original_height = frame_->height();
|
| #endif
|
| - Comment cmnt(masm_, "[ Assignment");
|
| + Variable* var = node->target()->AsVariableProxy()->AsVariable();
|
| + Property* prop = node->target()->AsProperty();
|
|
|
| - { Reference target(this, node->target(), node->is_compound());
|
| - if (target.is_illegal()) {
|
| - // Fool the virtual frame into thinking that we left the assignment's
|
| - // value on the frame.
|
| - frame_->Push(Smi::FromInt(0));
|
| - return;
|
| - }
|
| - Variable* var = node->target()->AsVariableProxy()->AsVariable();
|
| + if (var != NULL && !var->is_global()) {
|
| + EmitSlotAssignment(node);
|
|
|
| - if (node->starts_initialization_block()) {
|
| - ASSERT(target.type() == Reference::NAMED ||
|
| - target.type() == Reference::KEYED);
|
| - // Change to slow case in the beginning of an initialization
|
| - // block to avoid the quadratic behavior of repeatedly adding
|
| - // fast properties.
|
| + } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
|
| + (var != NULL && var->is_global())) {
|
| + // Properties whose keys are property names and global variables are
|
| + // treated as named property references. We do not need to consider
|
| + // global 'this' because it is not a valid left-hand side.
|
| + EmitNamedPropertyAssignment(node);
|
|
|
| - // The receiver is the argument to the runtime call. It is the
|
| - // first value pushed when the reference was loaded to the
|
| - // frame.
|
| - frame_->PushElementAt(target.size() - 1);
|
| - Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
|
| - }
|
| - if (node->ends_initialization_block()) {
|
| - // Add an extra copy of the receiver to the frame, so that it can be
|
| - // converted back to fast case after the assignment.
|
| - ASSERT(target.type() == Reference::NAMED ||
|
| - target.type() == Reference::KEYED);
|
| - if (target.type() == Reference::NAMED) {
|
| - frame_->Dup();
|
| - // Dup target receiver on stack.
|
| - } else {
|
| - ASSERT(target.type() == Reference::KEYED);
|
| - Result temp = frame_->Pop();
|
| - frame_->Dup();
|
| - frame_->Push(&temp);
|
| - }
|
| - }
|
| - if (node->op() == Token::ASSIGN ||
|
| - node->op() == Token::INIT_VAR ||
|
| - node->op() == Token::INIT_CONST) {
|
| - Load(node->value());
|
| -
|
| - } else { // Assignment is a compound assignment.
|
| - Literal* literal = node->value()->AsLiteral();
|
| - bool overwrite_value =
|
| - (node->value()->AsBinaryOperation() != NULL &&
|
| - node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
|
| - Variable* right_var = node->value()->AsVariableProxy()->AsVariable();
|
| - // There are two cases where the target is not read in the right hand
|
| - // side, that are easy to test for: the right hand side is a literal,
|
| - // or the right hand side is a different variable. TakeValue invalidates
|
| - // the target, with an implicit promise that it will be written to again
|
| - // before it is read.
|
| - if (literal != NULL || (right_var != NULL && right_var != var)) {
|
| - target.TakeValue();
|
| - } else {
|
| - target.GetValue();
|
| - }
|
| - Load(node->value());
|
| - GenericBinaryOperation(node->binary_op(),
|
| - node->type(),
|
| - overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
|
| - }
|
| + } else if (prop != NULL) {
|
| + // Other properties (including rewritten parameters for a function that
|
| + // uses arguments) are keyed property assignments.
|
| + EmitKeyedPropertyAssignment(node);
|
|
|
| - if (var != NULL &&
|
| - var->mode() == Variable::CONST &&
|
| - node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) {
|
| - // Assignment ignored - leave the value on the stack.
|
| - UnloadReference(&target);
|
| - } else {
|
| - CodeForSourcePosition(node->position());
|
| - if (node->op() == Token::INIT_CONST) {
|
| - // Dynamic constant initializations must use the function context
|
| - // and initialize the actual constant declared. Dynamic variable
|
| - // initializations are simply assignments and use SetValue.
|
| - target.SetValue(CONST_INIT);
|
| - } else {
|
| - target.SetValue(NOT_CONST_INIT);
|
| - }
|
| - if (node->ends_initialization_block()) {
|
| - ASSERT(target.type() == Reference::UNLOADED);
|
| - // End of initialization block. Revert to fast case. The
|
| - // argument to the runtime call is the extra copy of the receiver,
|
| - // which is below the value of the assignment.
|
| - // Swap the receiver and the value of the assignment expression.
|
| - Result lhs = frame_->Pop();
|
| - Result receiver = frame_->Pop();
|
| - frame_->Push(&lhs);
|
| - frame_->Push(&receiver);
|
| - Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
|
| - }
|
| - }
|
| + } else {
|
| + // Invalid left-hand side.
|
| + Load(node->target());
|
| + Result result = frame()->CallRuntime(Runtime::kThrowReferenceError, 1);
|
| + // The runtime call doesn't actually return but the code generator will
|
| + // still generate code and expects a certain frame height.
|
| + frame()->Push(&result);
|
| }
|
| +
|
| ASSERT(frame_->height() == original_height + 1);
|
| }
|
|
|
| @@ -5021,9 +5129,9 @@ void CodeGenerator::VisitCall(Call* node) {
|
| LoadGlobalReceiver();
|
| } else {
|
| Load(property->obj());
|
| - frame_->Dup();
|
| + frame()->Dup();
|
| Load(property->key());
|
| - Result function = EmitKeyedLoad(false);
|
| + Result function = EmitKeyedLoad();
|
| Result receiver = frame_->Pop();
|
| frame_->Push(&function);
|
| frame_->Push(&receiver);
|
| @@ -6454,9 +6562,8 @@ class DeferredReferenceGetKeyedValue: public DeferredCode {
|
| public:
|
| explicit DeferredReferenceGetKeyedValue(Register dst,
|
| Register receiver,
|
| - Register key,
|
| - bool is_global)
|
| - : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) {
|
| + Register key)
|
| + : dst_(dst), receiver_(receiver), key_(key) {
|
| set_comment("[ DeferredReferenceGetKeyedValue");
|
| }
|
|
|
| @@ -6469,7 +6576,6 @@ class DeferredReferenceGetKeyedValue: public DeferredCode {
|
| Register dst_;
|
| Register receiver_;
|
| Register key_;
|
| - bool is_global_;
|
| };
|
|
|
|
|
| @@ -6500,10 +6606,7 @@ void DeferredReferenceGetKeyedValue::Generate() {
|
| // This means that we cannot allow test instructions after calls to
|
| // KeyedLoadIC stubs in other places.
|
| Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| - RelocInfo::Mode mode = is_global_
|
| - ? RelocInfo::CODE_TARGET_CONTEXT
|
| - : RelocInfo::CODE_TARGET;
|
| - __ call(ic, mode);
|
| + __ call(ic, RelocInfo::CODE_TARGET);
|
| // The delta from the start of the map-compare instruction to the
|
| // test instruction. We use masm_-> directly here instead of the __
|
| // macro because the macro sometimes uses macro expansion to turn
|
| @@ -6567,12 +6670,91 @@ void DeferredReferenceSetKeyedValue::Generate() {
|
| }
|
|
|
|
|
| -Result CodeGenerator::EmitKeyedLoad(bool is_global) {
|
| - Comment cmnt(masm_, "[ Load from keyed Property");
|
| - // Inline array load code if inside of a loop. We do not know
|
| - // the receiver map yet, so we initially generate the code with
|
| - // a check against an invalid map. In the inline cache code, we
|
| - // patch the map check if appropriate.
|
| +Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
|
| +#ifdef DEBUG
|
| + int original_height = frame()->height();
|
| +#endif
|
| + Result result;
|
| + // Do not inline the inobject property case for loads from the global
|
| + // object. Also do not inline for unoptimized code. This saves time in
|
| + // the code generator. Unoptimized code is toplevel code or code that is
|
| + // not in a loop.
|
| + if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
|
| + Comment cmnt(masm(), "[ Load from named Property");
|
| + frame()->Push(name);
|
| +
|
| + RelocInfo::Mode mode = is_contextual
|
| + ? RelocInfo::CODE_TARGET_CONTEXT
|
| + : RelocInfo::CODE_TARGET;
|
| + result = frame()->CallLoadIC(mode);
|
| + // A test eax instruction following the call signals that the inobject
|
| + // property case was inlined. Ensure that there is not a test eax
|
| + // instruction here.
|
| + __ nop();
|
| + } else {
|
| + // Inline the inobject property case.
|
| + Comment cmnt(masm(), "[ Inlined named property load");
|
| + Result receiver = frame()->Pop();
|
| + receiver.ToRegister();
|
| +
|
| + result = allocator()->Allocate();
|
| + ASSERT(result.is_valid());
|
| + DeferredReferenceGetNamedValue* deferred =
|
| + new DeferredReferenceGetNamedValue(result.reg(), receiver.reg(), name);
|
| +
|
| + // Check that the receiver is a heap object.
|
| + __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(zero);
|
| +
|
| + __ bind(deferred->patch_site());
|
| + // This is the map check instruction that will be patched (so we can't
|
| + // use the double underscore macro that may insert instructions).
|
| + // Initially use an invalid map to force a failure.
|
| + masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
|
| + Immediate(Factory::null_value()));
|
| + // This branch is always a forwards branch so it's always a fixed size
|
| + // which allows the assert below to succeed and patching to work.
|
| + deferred->Branch(not_equal);
|
| +
|
| + // The delta from the patch label to the load offset must be statically
|
| + // known.
|
| + ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
|
| + LoadIC::kOffsetToLoadInstruction);
|
| + // The initial (invalid) offset has to be large enough to force a 32-bit
|
| + // instruction encoding to allow patching with an arbitrary offset. Use
|
| + // kMaxInt (minus kHeapObjectTag).
|
| + int offset = kMaxInt;
|
| + masm()->mov(result.reg(), FieldOperand(receiver.reg(), offset));
|
| +
|
| + __ IncrementCounter(&Counters::named_load_inline, 1);
|
| + deferred->BindExit();
|
| + }
|
| + ASSERT(frame()->height() == original_height - 1);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +Result CodeGenerator::EmitNamedStore(Handle<String> name) {
|
| +#ifdef DEBUG
|
| + int original_height = frame()->height();
|
| +#endif
|
| + frame()->Push(name);
|
| + Result result = frame()->CallStoreIC();
|
| +
|
| + ASSERT(frame()->height() == original_height - 2);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +Result CodeGenerator::EmitKeyedLoad() {
|
| +#ifdef DEBUG
|
| + int original_height = frame()->height();
|
| +#endif
|
| + Result result;
|
| + // Inline array load code if inside of a loop. We do not know the
|
| + // receiver map yet, so we initially generate the code with a check
|
| + // against an invalid map. In the inline cache code, we patch the map
|
| + // check if appropriate.
|
| if (loop_nesting() > 0) {
|
| Comment cmnt(masm_, "[ Inlined load from keyed Property");
|
|
|
| @@ -6588,22 +6770,16 @@ Result CodeGenerator::EmitKeyedLoad(bool is_global) {
|
|
|
| // Use a fresh temporary for the index and later the loaded
|
| // value.
|
| - Result index = allocator()->Allocate();
|
| - ASSERT(index.is_valid());
|
| + result = allocator()->Allocate();
|
| + ASSERT(result.is_valid());
|
|
|
| DeferredReferenceGetKeyedValue* deferred =
|
| - new DeferredReferenceGetKeyedValue(index.reg(),
|
| + new DeferredReferenceGetKeyedValue(result.reg(),
|
| receiver.reg(),
|
| - key.reg(),
|
| - is_global);
|
| + key.reg());
|
|
|
| - // Check that the receiver is not a smi (only needed if this
|
| - // is not a load from the global context) and that it has the
|
| - // expected map.
|
| - if (!is_global) {
|
| - __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| - deferred->Branch(zero);
|
| - }
|
| + __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(zero);
|
|
|
| // Initially, use an invalid map. The map is patched in the IC
|
| // initialization code.
|
| @@ -6628,46 +6804,132 @@ Result CodeGenerator::EmitKeyedLoad(bool is_global) {
|
|
|
| // Shift the key to get the actual index value and check that
|
| // it is within bounds.
|
| - __ mov(index.reg(), key.reg());
|
| - __ SmiUntag(index.reg());
|
| - __ cmp(index.reg(),
|
| + __ mov(result.reg(), key.reg());
|
| + __ SmiUntag(result.reg());
|
| + __ cmp(result.reg(),
|
| FieldOperand(elements.reg(), FixedArray::kLengthOffset));
|
| deferred->Branch(above_equal);
|
|
|
| - // Load and check that the result is not the hole. We could
|
| - // reuse the index or elements register for the value.
|
| - //
|
| - // TODO(206): Consider whether it makes sense to try some
|
| - // heuristic about which register to reuse. For example, if
|
| - // one is eax, the we can reuse that one because the value
|
| - // coming from the deferred code will be in eax.
|
| - Result value = index;
|
| - __ mov(value.reg(), Operand(elements.reg(),
|
| - index.reg(),
|
| - times_4,
|
| - FixedArray::kHeaderSize - kHeapObjectTag));
|
| + // Load and check that the result is not the hole.
|
| + __ mov(result.reg(), Operand(elements.reg(),
|
| + result.reg(),
|
| + times_4,
|
| + FixedArray::kHeaderSize - kHeapObjectTag));
|
| elements.Unuse();
|
| - index.Unuse();
|
| - __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value()));
|
| + __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
|
| deferred->Branch(equal);
|
| __ IncrementCounter(&Counters::keyed_load_inline, 1);
|
|
|
| deferred->BindExit();
|
| - return value;
|
| } else {
|
| Comment cmnt(masm_, "[ Load from keyed Property");
|
| - RelocInfo::Mode mode = is_global
|
| - ? RelocInfo::CODE_TARGET_CONTEXT
|
| - : RelocInfo::CODE_TARGET;
|
| - Result answer = frame_->CallKeyedLoadIC(mode);
|
| + result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET);
|
| // Make sure that we do not have a test instruction after the
|
| // call. A test instruction after the call is used to
|
| // indicate that we have generated an inline version of the
|
| // keyed load. The explicit nop instruction is here because
|
| // the push that follows might be peep-hole optimized away.
|
| __ nop();
|
| - return answer;
|
| }
|
| + ASSERT(frame()->height() == original_height - 2);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
|
| +#ifdef DEBUG
|
| + int original_height = frame()->height();
|
| +#endif
|
| + Result result;
|
| + // Generate inlined version of the keyed store if the code is in a loop
|
| + // and the key is likely to be a smi.
|
| + if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
|
| + Comment cmnt(masm(), "[ Inlined store to keyed Property");
|
| +
|
| + // Get the receiver, key and value into registers.
|
| + result = frame()->Pop();
|
| + Result key = frame()->Pop();
|
| + Result receiver = frame()->Pop();
|
| +
|
| + Result tmp = allocator_->Allocate();
|
| + ASSERT(tmp.is_valid());
|
| +
|
| + // Determine whether the value is a constant before putting it in a
|
| + // register.
|
| + bool value_is_constant = result.is_constant();
|
| +
|
| + // Make sure that value, key and receiver are in registers.
|
| + result.ToRegister();
|
| + key.ToRegister();
|
| + receiver.ToRegister();
|
| +
|
| + DeferredReferenceSetKeyedValue* deferred =
|
| + new DeferredReferenceSetKeyedValue(result.reg(),
|
| + key.reg(),
|
| + receiver.reg());
|
| +
|
| + // Check that the value is a smi if it is not a constant. We can skip
|
| + // the write barrier for smis and constants.
|
| + if (!value_is_constant) {
|
| + __ test(result.reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(not_zero);
|
| + }
|
| +
|
| + // Check that the key is a non-negative smi.
|
| + __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000));
|
| + deferred->Branch(not_zero);
|
| +
|
| + // Check that the receiver is not a smi.
|
| + __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(zero);
|
| +
|
| + // Check that the receiver is a JSArray.
|
| + __ mov(tmp.reg(),
|
| + FieldOperand(receiver.reg(), HeapObject::kMapOffset));
|
| + __ movzx_b(tmp.reg(),
|
| + FieldOperand(tmp.reg(), Map::kInstanceTypeOffset));
|
| + __ cmp(tmp.reg(), JS_ARRAY_TYPE);
|
| + deferred->Branch(not_equal);
|
| +
|
| + // Check that the key is within bounds. Both the key and the length of
|
| + // the JSArray are smis.
|
| + __ cmp(key.reg(),
|
| + FieldOperand(receiver.reg(), JSArray::kLengthOffset));
|
| + deferred->Branch(greater_equal);
|
| +
|
| + // Get the elements array from the receiver and check that it is not a
|
| + // dictionary.
|
| + __ mov(tmp.reg(),
|
| + FieldOperand(receiver.reg(), JSObject::kElementsOffset));
|
| + // Bind the deferred code patch site to be able to locate the fixed
|
| + // array map comparison. When debugging, we patch this comparison to
|
| + // always fail so that we will hit the IC call in the deferred code
|
| + // which will allow the debugger to break for fast case stores.
|
| + __ bind(deferred->patch_site());
|
| + __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
|
| + Immediate(Factory::fixed_array_map()));
|
| + deferred->Branch(not_equal);
|
| +
|
| + // Store the value.
|
| + __ mov(Operand(tmp.reg(),
|
| + key.reg(),
|
| + times_2,
|
| + FixedArray::kHeaderSize - kHeapObjectTag),
|
| + result.reg());
|
| + __ IncrementCounter(&Counters::keyed_store_inline, 1);
|
| +
|
| + deferred->BindExit();
|
| + } else {
|
| + result = frame()->CallKeyedStoreIC();
|
| + // Make sure that we do not have a test instruction after the
|
| + // call. A test instruction after the call is used to
|
| + // indicate that we have generated an inline version of the
|
| + // keyed store.
|
| + __ nop();
|
| + frame()->Drop(2);
|
| + }
|
| + ASSERT(frame()->height() == original_height - 3);
|
| + return result;
|
| }
|
|
|
|
|
| @@ -6687,7 +6949,7 @@ Handle<String> Reference::GetName() {
|
| } else {
|
| Literal* raw_name = property->key()->AsLiteral();
|
| ASSERT(raw_name != NULL);
|
| - return Handle<String>(String::cast(*raw_name->handle()));
|
| + return Handle<String>::cast(raw_name->handle());
|
| }
|
| }
|
|
|
| @@ -6720,73 +6982,10 @@ void Reference::GetValue() {
|
| Variable* var = expression_->AsVariableProxy()->AsVariable();
|
| bool is_global = var != NULL;
|
| ASSERT(!is_global || var->is_global());
|
| -
|
| - if (persist_after_get_) {
|
| - cgen_->frame()->Dup();
|
| - }
|
| - // Do not inline the inobject property case for loads from the global
|
| - // object. Also do not inline for unoptimized code. This saves time
|
| - // in the code generator. Unoptimized code is toplevel code or code
|
| - // that is not in a loop.
|
| - if (is_global ||
|
| - cgen_->scope()->is_global_scope() ||
|
| - cgen_->loop_nesting() == 0) {
|
| - Comment cmnt(masm, "[ Load from named Property");
|
| - cgen_->frame()->Push(GetName());
|
| -
|
| - RelocInfo::Mode mode = is_global
|
| - ? RelocInfo::CODE_TARGET_CONTEXT
|
| - : RelocInfo::CODE_TARGET;
|
| - Result answer = cgen_->frame()->CallLoadIC(mode);
|
| - // A test eax instruction following the call signals that the
|
| - // inobject property case was inlined. Ensure that there is not
|
| - // a test eax instruction here.
|
| - __ nop();
|
| - cgen_->frame()->Push(&answer);
|
| - } else {
|
| - // Inline the inobject property case.
|
| - Comment cmnt(masm, "[ Inlined named property load");
|
| - Result receiver = cgen_->frame()->Pop();
|
| - receiver.ToRegister();
|
| -
|
| - Result value = cgen_->allocator()->Allocate();
|
| - ASSERT(value.is_valid());
|
| - DeferredReferenceGetNamedValue* deferred =
|
| - new DeferredReferenceGetNamedValue(value.reg(),
|
| - receiver.reg(),
|
| - GetName());
|
| -
|
| - // Check that the receiver is a heap object.
|
| - __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| - deferred->Branch(zero);
|
| -
|
| - __ bind(deferred->patch_site());
|
| - // This is the map check instruction that will be patched (so we can't
|
| - // use the double underscore macro that may insert instructions).
|
| - // Initially use an invalid map to force a failure.
|
| - masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
|
| - Immediate(Factory::null_value()));
|
| - // This branch is always a forwards branch so it's always a fixed
|
| - // size which allows the assert below to succeed and patching to work.
|
| - deferred->Branch(not_equal);
|
| -
|
| - // The delta from the patch label to the load offset must be
|
| - // statically known.
|
| - ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
|
| - LoadIC::kOffsetToLoadInstruction);
|
| - // The initial (invalid) offset has to be large enough to force
|
| - // a 32-bit instruction encoding to allow patching with an
|
| - // arbitrary offset. Use kMaxInt (minus kHeapObjectTag).
|
| - int offset = kMaxInt;
|
| - masm->mov(value.reg(), FieldOperand(receiver.reg(), offset));
|
| -
|
| - __ IncrementCounter(&Counters::named_load_inline, 1);
|
| - deferred->BindExit();
|
| - cgen_->frame()->Push(&value);
|
| - }
|
| - if (!persist_after_get_) {
|
| - set_unloaded();
|
| - }
|
| + if (persist_after_get_) cgen_->frame()->Dup();
|
| + Result result = cgen_->EmitNamedLoad(GetName(), is_global);
|
| + if (!persist_after_get_) set_unloaded();
|
| + cgen_->frame()->Push(&result);
|
| break;
|
| }
|
|
|
| @@ -6795,18 +6994,13 @@ void Reference::GetValue() {
|
| cgen_->frame()->PushElementAt(1);
|
| cgen_->frame()->PushElementAt(1);
|
| }
|
| - Variable* var = expression_->AsVariableProxy()->AsVariable();
|
| - bool is_global = var != NULL;
|
| - ASSERT(!is_global || var->is_global());
|
| - Result value = cgen_->EmitKeyedLoad(is_global);
|
| + Result value = cgen_->EmitKeyedLoad();
|
| cgen_->frame()->Push(&value);
|
| - if (!persist_after_get_) {
|
| - set_unloaded();
|
| - }
|
| + if (!persist_after_get_) set_unloaded();
|
| break;
|
| }
|
|
|
| - default:
|
| + default:
|
| UNREACHABLE();
|
| }
|
| }
|
| @@ -6858,14 +7052,13 @@ void Reference::SetValue(InitState init_state) {
|
| Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
|
| ASSERT(slot != NULL);
|
| cgen_->StoreToSlot(slot, init_state);
|
| - cgen_->UnloadReference(this);
|
| + set_unloaded();
|
| break;
|
| }
|
|
|
| case NAMED: {
|
| Comment cmnt(masm, "[ Store to named Property");
|
| - cgen_->frame()->Push(GetName());
|
| - Result answer = cgen_->frame()->CallStoreIC();
|
| + Result answer = cgen_->EmitNamedStore(GetName());
|
| cgen_->frame()->Push(&answer);
|
| set_unloaded();
|
| break;
|
| @@ -6873,108 +7066,16 @@ void Reference::SetValue(InitState init_state) {
|
|
|
| case KEYED: {
|
| Comment cmnt(masm, "[ Store to keyed Property");
|
| -
|
| - // Generate inlined version of the keyed store if the code is in
|
| - // a loop and the key is likely to be a smi.
|
| Property* property = expression()->AsProperty();
|
| ASSERT(property != NULL);
|
| - StaticType* key_smi_analysis = property->key()->type();
|
| -
|
| - if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) {
|
| - Comment cmnt(masm, "[ Inlined store to keyed Property");
|
| -
|
| - // Get the receiver, key and value into registers.
|
| - Result value = cgen_->frame()->Pop();
|
| - Result key = cgen_->frame()->Pop();
|
| - Result receiver = cgen_->frame()->Pop();
|
| -
|
| - Result tmp = cgen_->allocator_->Allocate();
|
| - ASSERT(tmp.is_valid());
|
| -
|
| - // Determine whether the value is a constant before putting it
|
| - // in a register.
|
| - bool value_is_constant = value.is_constant();
|
| -
|
| - // Make sure that value, key and receiver are in registers.
|
| - value.ToRegister();
|
| - key.ToRegister();
|
| - receiver.ToRegister();
|
| -
|
| - DeferredReferenceSetKeyedValue* deferred =
|
| - new DeferredReferenceSetKeyedValue(value.reg(),
|
| - key.reg(),
|
| - receiver.reg());
|
| -
|
| - // Check that the value is a smi if it is not a constant. We
|
| - // can skip the write barrier for smis and constants.
|
| - if (!value_is_constant) {
|
| - __ test(value.reg(), Immediate(kSmiTagMask));
|
| - deferred->Branch(not_zero);
|
| - }
|
| -
|
| - // Check that the key is a non-negative smi.
|
| - __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000));
|
| - deferred->Branch(not_zero);
|
| -
|
| - // Check that the receiver is not a smi.
|
| - __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| - deferred->Branch(zero);
|
| -
|
| - // Check that the receiver is a JSArray.
|
| - __ mov(tmp.reg(),
|
| - FieldOperand(receiver.reg(), HeapObject::kMapOffset));
|
| - __ movzx_b(tmp.reg(),
|
| - FieldOperand(tmp.reg(), Map::kInstanceTypeOffset));
|
| - __ cmp(tmp.reg(), JS_ARRAY_TYPE);
|
| - deferred->Branch(not_equal);
|
| -
|
| - // Check that the key is within bounds. Both the key and the
|
| - // length of the JSArray are smis.
|
| - __ cmp(key.reg(),
|
| - FieldOperand(receiver.reg(), JSArray::kLengthOffset));
|
| - deferred->Branch(greater_equal);
|
| -
|
| - // Get the elements array from the receiver and check that it
|
| - // is not a dictionary.
|
| - __ mov(tmp.reg(),
|
| - FieldOperand(receiver.reg(), JSObject::kElementsOffset));
|
| - // Bind the deferred code patch site to be able to locate the
|
| - // fixed array map comparison. When debugging, we patch this
|
| - // comparison to always fail so that we will hit the IC call
|
| - // in the deferred code which will allow the debugger to
|
| - // break for fast case stores.
|
| - __ bind(deferred->patch_site());
|
| - __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
|
| - Immediate(Factory::fixed_array_map()));
|
| - deferred->Branch(not_equal);
|
| -
|
| - // Store the value.
|
| - __ mov(Operand(tmp.reg(),
|
| - key.reg(),
|
| - times_2,
|
| - FixedArray::kHeaderSize - kHeapObjectTag),
|
| - value.reg());
|
| - __ IncrementCounter(&Counters::keyed_store_inline, 1);
|
| -
|
| - deferred->BindExit();
|
| -
|
| - cgen_->frame()->Push(&receiver);
|
| - cgen_->frame()->Push(&key);
|
| - cgen_->frame()->Push(&value);
|
| - } else {
|
| - Result answer = cgen_->frame()->CallKeyedStoreIC();
|
| - // Make sure that we do not have a test instruction after the
|
| - // call. A test instruction after the call is used to
|
| - // indicate that we have generated an inline version of the
|
| - // keyed store.
|
| - __ nop();
|
| - cgen_->frame()->Push(&answer);
|
| - }
|
| - cgen_->UnloadReference(this);
|
| + Result answer = cgen_->EmitKeyedStore(property->key()->type());
|
| + cgen_->frame()->Push(&answer);
|
| + set_unloaded();
|
| break;
|
| }
|
|
|
| - default:
|
| + case UNLOADED:
|
| + case ILLEGAL:
|
| UNREACHABLE();
|
| }
|
| }
|
|
|