Index: src/codegen-arm.cc |
=================================================================== |
--- src/codegen-arm.cc (revision 408) |
+++ src/codegen-arm.cc (working copy) |
@@ -671,73 +671,6 @@ |
} |
-#undef __ |
-#define __ masm-> |
- |
-MemOperand ArmCodeGenerator::SlotOperand(CodeGenerator* cgen, |
- Slot* slot, |
- Register tmp) { |
- // Currently, this assertion will fail if we try to assign to |
- // a constant variable that is constant because it is read-only |
- // (such as the variable referring to a named function expression). |
- // We need to implement assignments to read-only variables. |
- // Ideally, we should do this during AST generation (by converting |
- // such assignments into expression statements); however, in general |
- // we may not be able to make the decision until past AST generation, |
- // that is when the entire program is known. |
- ASSERT(slot != NULL); |
- int index = slot->index(); |
- switch (slot->type()) { |
- case Slot::PARAMETER: |
- return ParameterOperand(cgen, index); |
- |
- case Slot::LOCAL: { |
- ASSERT(0 <= index && |
- index < cgen->scope()->num_stack_slots() && |
- index >= 0); |
- int local_offset = JavaScriptFrameConstants::kLocal0Offset - |
- index * kPointerSize; |
- return MemOperand(fp, local_offset); |
- } |
- |
- case Slot::CONTEXT: { |
- MacroAssembler* masm = cgen->masm(); |
- // Follow the context chain if necessary. |
- ASSERT(!tmp.is(cp)); // do not overwrite context register |
- Register context = cp; |
- int chain_length = |
- cgen->scope()->ContextChainLength(slot->var()->scope()); |
- for (int i = chain_length; i-- > 0;) { |
- // Load the closure. |
- // (All contexts, even 'with' contexts, have a closure, |
- // and it is the same for all contexts inside a function. |
- // There is no need to go to the function context first.) |
- __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); |
- // Load the function context (which is the incoming, outer context). |
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset)); |
- context = tmp; |
- } |
- // We may have a 'with' context now. Get the function context. |
- // (In fact this mov may never be the needed, since the scope analysis |
- // may not permit a direct context access in this case and thus we are |
- // always at a function context. However it is safe to dereference be- |
- // cause the function context of a function context is itself. Before |
- // deleting this mov we should try to create a counter-example first, |
- // though...) |
- __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); |
- return ContextOperand(tmp, index); |
- } |
- |
- default: |
- UNREACHABLE(); |
- return MemOperand(r0, 0); |
- } |
-} |
- |
- |
-#undef __ |
-#define __ masm_-> |
- |
// Loads a value on the stack. If it is a boolean value, the result may have |
// been (partially) translated into branches, or it may have set the condition |
// code register. If force_cc is set, the value is forced to set the condition |
@@ -902,126 +835,6 @@ |
} |
-#undef __ |
-#define __ masm-> |
- |
-void Property::GenerateStoreCode(CodeGenerator* cgen, |
- Reference* ref, |
- InitState init_state) { |
- MacroAssembler* masm = cgen->masm(); |
- Comment cmnt(masm, "[ Store to Property"); |
- __ RecordPosition(position()); |
- ArmCodeGenerator::SetReferenceProperty(cgen, ref, key()); |
-} |
- |
- |
-void VariableProxy::GenerateStoreCode(CodeGenerator* cgen, |
- Reference* ref, |
- InitState init_state) { |
- MacroAssembler* masm = cgen->masm(); |
- Comment cmnt(masm, "[ Store to VariableProxy"); |
- Variable* node = var(); |
- |
- Expression* expr = node->rewrite(); |
- if (expr != NULL) { |
- expr->GenerateStoreCode(cgen, ref, init_state); |
- } else { |
- ASSERT(node->is_global()); |
- if (node->AsProperty() != NULL) { |
- __ RecordPosition(node->AsProperty()->position()); |
- } |
- Expression* key = new Literal(node->name()); |
- ArmCodeGenerator::SetReferenceProperty(cgen, ref, key); |
- } |
-} |
- |
- |
-void Slot::GenerateStoreCode(CodeGenerator* cgen, |
- Reference* ref, |
- InitState init_state) { |
- MacroAssembler* masm = cgen->masm(); |
- Comment cmnt(masm, "[ Store to Slot"); |
- |
- if (type() == Slot::LOOKUP) { |
- ASSERT(var()->mode() == Variable::DYNAMIC); |
- |
- // For now, just do a runtime call. |
- __ push(cp); |
- __ mov(r0, Operand(var()->name())); |
- __ push(r0); |
- |
- if (init_state == CONST_INIT) { |
- // Same as the case for a normal store, but ignores attribute |
- // (e.g. READ_ONLY) of context slot so that we can initialize const |
- // properties (introduced via eval("const foo = (some expr);")). Also, |
- // uses the current function context instead of the top context. |
- // |
- // Note that we must declare the foo upon entry of eval(), via a |
- // context slot declaration, but we cannot initialize it at the same |
- // time, because the const declaration may be at the end of the eval |
- // code (sigh...) and the const variable may have been used before |
- // (where its value is 'undefined'). Thus, we can only do the |
- // initialization when we actually encounter the expression and when |
- // the expression operands are defined and valid, and thus we need the |
- // split into 2 operations: declaration of the context slot followed |
- // by initialization. |
- __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
- } else { |
- __ CallRuntime(Runtime::kStoreContextSlot, 3); |
- } |
- // Storing a variable must keep the (new) value on the expression |
- // stack. This is necessary for compiling assignment expressions. |
- __ push(r0); |
- |
- } else { |
- ASSERT(var()->mode() != Variable::DYNAMIC); |
- |
- Label exit; |
- if (init_state == CONST_INIT) { |
- ASSERT(var()->mode() == Variable::CONST); |
- // Only the first const initialization must be executed (the slot |
- // still contains 'the hole' value). When the assignment is executed, |
- // the code is identical to a normal store (see below). |
- Comment cmnt(masm, "[ Init const"); |
- __ ldr(r2, ArmCodeGenerator::SlotOperand(cgen, this, r2)); |
- __ cmp(r2, Operand(Factory::the_hole_value())); |
- __ b(ne, &exit); |
- } |
- |
- // We must execute the store. |
- // r2 may be loaded with context; used below in RecordWrite. |
- // Storing a variable must keep the (new) value on the stack. This is |
- // necessary for compiling assignment expressions. |
- // |
- // Note: We will reach here even with var()->mode() == Variable::CONST |
- // because of const declarations which will initialize consts to 'the |
- // hole' value and by doing so, end up calling this code. r2 may be |
- // loaded with context; used below in RecordWrite. |
- __ pop(r0); |
- __ str(r0, ArmCodeGenerator::SlotOperand(cgen, this, r2)); |
- __ push(r0); |
- |
- if (type() == Slot::CONTEXT) { |
- // Skip write barrier if the written value is a smi. |
- __ tst(r0, Operand(kSmiTagMask)); |
- __ b(eq, &exit); |
- // r2 is loaded with context when calling SlotOperand above. |
- int offset = FixedArray::kHeaderSize + index() * kPointerSize; |
- __ mov(r3, Operand(offset)); |
- __ RecordWrite(r2, r3, r1); |
- } |
- // If we definitely did not jump over the assignment, we do not need to |
- // bind the exit label. Doing so can defeat peephole optimization. |
- if (init_state == CONST_INIT || type() == Slot::CONTEXT) { |
- __ bind(&exit); |
- } |
- } |
-} |
- |
- |
-#undef __ |
-#define __ masm_-> |
- |
// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given |
// register to a boolean in the condition code register. The code |
// may jump to 'false_target' in case the register converts to 'false'. |
@@ -1062,9 +875,6 @@ |
} |
-#undef __ |
-#define __ masm-> |
- |
class GetPropertyStub : public CodeStub { |
public: |
GetPropertyStub() { } |
@@ -1078,61 +888,6 @@ |
}; |
-void GetPropertyStub::Generate(MacroAssembler* masm) { |
- // sp[0]: key |
- // sp[1]: receiver |
- Label slow, fast; |
- // Get the key and receiver object from the stack. |
- __ ldm(ia, sp, r0.bit() | r1.bit()); |
- // Check that the key is a smi. |
- __ tst(r0, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- __ mov(r0, Operand(r0, ASR, kSmiTagSize)); |
- // Check that the object isn't a smi. |
- __ tst(r1, Operand(kSmiTagMask)); |
- __ b(eq, &slow); |
- |
- // Check that the object is some kind of JS object EXCEPT JS Value type. |
- // In the case that the object is a value-wrapper object, |
- // we enter the runtime system to make sure that indexing into string |
- // objects work as intended. |
- ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); |
- __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
- __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
- __ cmp(r2, Operand(JS_OBJECT_TYPE)); |
- __ b(lt, &slow); |
- |
- // Get the elements array of the object. |
- __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); |
- // Check that the object is in fast mode (not dictionary). |
- __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); |
- __ cmp(r3, Operand(Factory::hash_table_map())); |
- __ b(eq, &slow); |
- // Check that the key (index) is within bounds. |
- __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset)); |
- __ cmp(r0, Operand(r3)); |
- __ b(lo, &fast); |
- |
- // Slow case: Push extra copies of the arguments (2). |
- __ bind(&slow); |
- __ ldm(ia, sp, r0.bit() | r1.bit()); |
- __ stm(db_w, sp, r0.bit() | r1.bit()); |
- // Do tail-call to runtime routine. |
- __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2); |
- |
- // Fast case: Do the load. |
- __ bind(&fast); |
- __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag)); |
- __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2)); |
- __ cmp(r0, Operand(Factory::the_hole_value())); |
- // In case the loaded value is the_hole we have to consult GetProperty |
- // to ensure the prototype chain is searched. |
- __ b(eq, &slow); |
- |
- masm->StubReturn(1); |
-} |
- |
- |
class SetPropertyStub : public CodeStub { |
public: |
SetPropertyStub() { } |
@@ -1146,116 +901,6 @@ |
}; |
- |
-void SetPropertyStub::Generate(MacroAssembler* masm) { |
- // r0 : value |
- // sp[0] : key |
- // sp[1] : receiver |
- |
- Label slow, fast, array, extra, exit; |
- // Get the key and the object from the stack. |
- __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver |
- // Check that the key is a smi. |
- __ tst(r1, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- // Check that the object isn't a smi. |
- __ tst(r3, Operand(kSmiTagMask)); |
- __ b(eq, &slow); |
- // Get the type of the object from its map. |
- __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); |
- __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
- // Check if the object is a JS array or not. |
- __ cmp(r2, Operand(JS_ARRAY_TYPE)); |
- __ b(eq, &array); |
- // Check that the object is some kind of JS object. |
- __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE)); |
- __ b(lt, &slow); |
- |
- |
- // Object case: Check key against length in the elements array. |
- __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset)); |
- // Check that the object is in fast mode (not dictionary). |
- __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); |
- __ cmp(r2, Operand(Factory::hash_table_map())); |
- __ b(eq, &slow); |
- // Untag the key (for checking against untagged length in the fixed array). |
- __ mov(r1, Operand(r1, ASR, kSmiTagSize)); |
- // Compute address to store into and check array bounds. |
- __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag)); |
- __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2)); |
- __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset)); |
- __ cmp(r1, Operand(ip)); |
- __ b(lo, &fast); |
- |
- |
- // Slow case: Push extra copies of the arguments (3). |
- __ bind(&slow); |
- __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object |
- __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); |
- // Do tail-call to runtime routine. |
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3); |
- |
- |
- // Extra capacity case: Check if there is extra capacity to |
- // perform the store and update the length. Used for adding one |
- // element to the array by writing to array[array.length]. |
- // r0 == value, r1 == key, r2 == elements, r3 == object |
- __ bind(&extra); |
- __ b(ne, &slow); // do not leave holes in the array |
- __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag |
- __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset)); |
- __ cmp(r1, Operand(ip)); |
- __ b(hs, &slow); |
- __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag |
- __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment |
- __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset)); |
- __ mov(r3, Operand(r2)); |
- // NOTE: Computing the address to store into must take the fact |
- // that the key has been incremented into account. |
- int displacement = Array::kHeaderSize - kHeapObjectTag - |
- ((1 << kSmiTagSize) * 2); |
- __ add(r2, r2, Operand(displacement)); |
- __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
- __ b(&fast); |
- |
- |
- // Array case: Get the length and the elements array from the JS |
- // array. Check that the array is in fast mode; if it is the |
- // length is always a smi. |
- // r0 == value, r3 == object |
- __ bind(&array); |
- __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset)); |
- __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); |
- __ cmp(r1, Operand(Factory::hash_table_map())); |
- __ b(eq, &slow); |
- |
- // Check the key against the length in the array, compute the |
- // address to store into and fall through to fast case. |
- __ ldr(r1, MemOperand(sp)); |
- // r0 == value, r1 == key, r2 == elements, r3 == object. |
- __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset)); |
- __ cmp(r1, Operand(ip)); |
- __ b(hs, &extra); |
- __ mov(r3, Operand(r2)); |
- __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag)); |
- __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
- |
- |
- // Fast case: Do the store. |
- // r0 == value, r2 == address to store into, r3 == elements |
- __ bind(&fast); |
- __ str(r0, MemOperand(r2)); |
- // Skip write barrier if the written value is a smi. |
- __ tst(r0, Operand(kSmiTagMask)); |
- __ b(eq, &exit); |
- // Update write barrier for the elements array address. |
- __ sub(r1, r2, Operand(r3)); |
- __ RecordWrite(r3, r1, r2); |
- __ bind(&exit); |
- masm->StubReturn(1); |
-} |
- |
- |
class GenericBinaryOpStub : public CodeStub { |
public: |
explicit GenericBinaryOpStub(Token::Value op) : op_(op) { } |
@@ -1289,242 +934,6 @@ |
}; |
-void GenericBinaryOpStub::Generate(MacroAssembler* masm) { |
- // r1 : x |
- // r0 : y |
- // result : r0 |
- |
- switch (op_) { |
- case Token::ADD: { |
- Label slow, exit; |
- // fast path |
- __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
- __ add(r0, r1, Operand(r0), SetCC); // add y optimistically |
- // go slow-path in case of overflow |
- __ b(vs, &slow); |
- // go slow-path in case of non-smi operands |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ b(eq, &exit); |
- // slow path |
- __ bind(&slow); |
- __ sub(r0, r0, Operand(r1)); // revert optimistic add |
- __ push(r1); |
- __ push(r0); |
- __ mov(r0, Operand(1)); // set number of arguments |
- __ InvokeBuiltin(Builtins::ADD, JUMP_JS); |
- // done |
- __ bind(&exit); |
- break; |
- } |
- |
- case Token::SUB: { |
- Label slow, exit; |
- // fast path |
- __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
- __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically |
- // go slow-path in case of overflow |
- __ b(vs, &slow); |
- // go slow-path in case of non-smi operands |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result |
- __ b(eq, &exit); |
- // slow path |
- __ bind(&slow); |
- __ push(r1); |
- __ push(r0); |
- __ mov(r0, Operand(1)); // set number of arguments |
- __ InvokeBuiltin(Builtins::SUB, JUMP_JS); |
- // done |
- __ bind(&exit); |
- break; |
- } |
- |
- case Token::MUL: { |
- Label slow, exit; |
- // tag check |
- __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- // remove tag from one operand (but keep sign), so that result is smi |
- __ mov(ip, Operand(r0, ASR, kSmiTagSize)); |
- // do multiplication |
- __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1 |
- // go slow on overflows (overflow bit is not set) |
- __ mov(ip, Operand(r3, ASR, 31)); |
- __ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical |
- __ b(ne, &slow); |
- // go slow on zero result to handle -0 |
- __ tst(r3, Operand(r3)); |
- __ mov(r0, Operand(r3), LeaveCC, ne); |
- __ b(ne, &exit); |
- // slow case |
- __ bind(&slow); |
- __ push(r1); |
- __ push(r0); |
- __ mov(r0, Operand(1)); // set number of arguments |
- __ InvokeBuiltin(Builtins::MUL, JUMP_JS); |
- // done |
- __ bind(&exit); |
- break; |
- } |
- |
- case Token::BIT_OR: |
- case Token::BIT_AND: |
- case Token::BIT_XOR: { |
- Label slow, exit; |
- // tag check |
- __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- switch (op_) { |
- case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break; |
- case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break; |
- case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; |
- default: UNREACHABLE(); |
- } |
- __ b(&exit); |
- __ bind(&slow); |
- __ push(r1); // restore stack |
- __ push(r0); |
- __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
- switch (op_) { |
- case Token::BIT_OR: |
- __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); |
- break; |
- case Token::BIT_AND: |
- __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); |
- break; |
- case Token::BIT_XOR: |
- __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
- __ bind(&exit); |
- break; |
- } |
- |
- case Token::SHL: |
- case Token::SHR: |
- case Token::SAR: { |
- Label slow, exit; |
- // tag check |
- __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- // remove tags from operands (but keep sign) |
- __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x |
- __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
- // use only the 5 least significant bits of the shift count |
- __ and_(r2, r2, Operand(0x1f)); |
- // perform operation |
- switch (op_) { |
- case Token::SAR: |
- __ mov(r3, Operand(r3, ASR, r2)); |
- // no checks of result necessary |
- break; |
- |
- case Token::SHR: |
- __ mov(r3, Operand(r3, LSR, r2)); |
- // check that the *unsigned* result fits in a smi |
- // neither of the two high-order bits can be set: |
- // - 0x80000000: high bit would be lost when smi tagging |
- // - 0x40000000: this number would convert to negative when |
- // smi tagging these two cases can only happen with shifts |
- // by 0 or 1 when handed a valid smi |
- __ and_(r2, r3, Operand(0xc0000000), SetCC); |
- __ b(ne, &slow); |
- break; |
- |
- case Token::SHL: |
- __ mov(r3, Operand(r3, LSL, r2)); |
- // check that the *signed* result fits in a smi |
- __ add(r2, r3, Operand(0x40000000), SetCC); |
- __ b(mi, &slow); |
- break; |
- |
- default: UNREACHABLE(); |
- } |
- // tag result and store it in r0 |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ mov(r0, Operand(r3, LSL, kSmiTagSize)); |
- __ b(&exit); |
- // slow case |
- __ bind(&slow); |
- __ push(r1); // restore stack |
- __ push(r0); |
- __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
- switch (op_) { |
- case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break; |
- case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break; |
- case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; |
- default: UNREACHABLE(); |
- } |
- __ bind(&exit); |
- break; |
- } |
- |
- default: UNREACHABLE(); |
- } |
- __ Ret(); |
-} |
- |
- |
-void StackCheckStub::Generate(MacroAssembler* masm) { |
- Label within_limit; |
- __ mov(ip, Operand(ExternalReference::address_of_stack_guard_limit())); |
- __ ldr(ip, MemOperand(ip)); |
- __ cmp(sp, Operand(ip)); |
- __ b(hs, &within_limit); |
- // Do tail-call to runtime routine. |
- __ push(r0); |
- __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1); |
- __ bind(&within_limit); |
- |
- masm->StubReturn(1); |
-} |
- |
- |
-void UnarySubStub::Generate(MacroAssembler* masm) { |
- Label undo; |
- Label slow; |
- Label done; |
- |
- // Enter runtime system if the value is not a smi. |
- __ tst(r0, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- |
- // Enter runtime system if the value of the expression is zero |
- // to make sure that we switch between 0 and -0. |
- __ cmp(r0, Operand(0)); |
- __ b(eq, &slow); |
- |
- // The value of the expression is a smi that is not zero. Try |
- // optimistic subtraction '0 - value'. |
- __ rsb(r1, r0, Operand(0), SetCC); |
- __ b(vs, &slow); |
- |
- // If result is a smi we are done. |
- __ tst(r1, Operand(kSmiTagMask)); |
- __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result |
- __ b(eq, &done); |
- |
- // Enter runtime system. |
- __ bind(&slow); |
- __ push(r0); |
- __ mov(r0, Operand(0)); // set number of arguments |
- __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS); |
- |
- __ bind(&done); |
- masm->StubReturn(1); |
-} |
- |
- |
class InvokeBuiltinStub : public CodeStub { |
public: |
enum Kind { Inc, Dec, ToNumber }; |
@@ -1550,355 +959,6 @@ |
}; |
-void InvokeBuiltinStub::Generate(MacroAssembler* masm) { |
- __ push(r0); |
- __ mov(r0, Operand(0)); // set number of arguments |
- switch (kind_) { |
- case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break; |
- case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break; |
- case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break; |
- default: UNREACHABLE(); |
- } |
- masm->StubReturn(argc_); |
-} |
- |
- |
-void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { |
- // r0 holds exception |
- ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code |
- __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); |
- __ ldr(sp, MemOperand(r3)); |
- __ pop(r2); // pop next in chain |
- __ str(r2, MemOperand(r3)); |
- // restore parameter- and frame-pointer and pop state. |
- __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); |
- // Before returning we restore the context from the frame pointer if not NULL. |
- // The frame pointer is NULL in the exception handler of a JS entry frame. |
- __ cmp(fp, Operand(0)); |
- // Set cp to NULL if fp is NULL. |
- __ mov(cp, Operand(0), LeaveCC, eq); |
- // Restore cp otherwise. |
- __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); |
- if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); |
- __ pop(pc); |
-} |
- |
- |
-void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { |
- // Fetch top stack handler. |
- __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); |
- __ ldr(r3, MemOperand(r3)); |
- |
- // Unwind the handlers until the ENTRY handler is found. |
- Label loop, done; |
- __ bind(&loop); |
- // Load the type of the current stack handler. |
- const int kStateOffset = StackHandlerConstants::kAddressDisplacement + |
- StackHandlerConstants::kStateOffset; |
- __ ldr(r2, MemOperand(r3, kStateOffset)); |
- __ cmp(r2, Operand(StackHandler::ENTRY)); |
- __ b(eq, &done); |
- // Fetch the next handler in the list. |
- const int kNextOffset = StackHandlerConstants::kAddressDisplacement + |
- StackHandlerConstants::kNextOffset; |
- __ ldr(r3, MemOperand(r3, kNextOffset)); |
- __ jmp(&loop); |
- __ bind(&done); |
- |
- // Set the top handler address to next handler past the current ENTRY handler. |
- __ ldr(r0, MemOperand(r3, kNextOffset)); |
- __ mov(r2, Operand(ExternalReference(Top::k_handler_address))); |
- __ str(r0, MemOperand(r2)); |
- |
- // Set external caught exception to false. |
- __ mov(r0, Operand(false)); |
- ExternalReference external_caught(Top::k_external_caught_exception_address); |
- __ mov(r2, Operand(external_caught)); |
- __ str(r0, MemOperand(r2)); |
- |
- // Set pending exception and r0 to out of memory exception. |
- Failure* out_of_memory = Failure::OutOfMemoryException(); |
- __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); |
- __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); |
- __ str(r0, MemOperand(r2)); |
- |
- // Restore the stack to the address of the ENTRY handler |
- __ mov(sp, Operand(r3)); |
- |
- // restore parameter- and frame-pointer and pop state. |
- __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); |
- // Before returning we restore the context from the frame pointer if not NULL. |
- // The frame pointer is NULL in the exception handler of a JS entry frame. |
- __ cmp(fp, Operand(0)); |
- // Set cp to NULL if fp is NULL. |
- __ mov(cp, Operand(0), LeaveCC, eq); |
- // Restore cp otherwise. |
- __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); |
- if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); |
- __ pop(pc); |
-} |
- |
- |
-void CEntryStub::GenerateCore(MacroAssembler* masm, |
- Label* throw_normal_exception, |
- Label* throw_out_of_memory_exception, |
- StackFrame::Type frame_type, |
- bool do_gc) { |
- // r0: result parameter for PerformGC, if any |
- // r4: number of arguments including receiver (C callee-saved) |
- // r5: pointer to builtin function (C callee-saved) |
- // r6: pointer to the first argument (C callee-saved) |
- |
- if (do_gc) { |
- // Passing r0. |
- __ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY); |
- } |
- |
- // Call C built-in. |
- // r0 = argc, r1 = argv |
- __ mov(r0, Operand(r4)); |
- __ mov(r1, Operand(r6)); |
- |
- // TODO(1242173): To let the GC traverse the return address of the exit |
- // frames, we need to know where the return address is. Right now, |
- // we push it on the stack to be able to find it again, but we never |
- // restore from it in case of changes, which makes it impossible to |
- // support moving the C entry code stub. This should be fixed, but currently |
- // this is OK because the CEntryStub gets generated so early in the V8 boot |
- // sequence that it is not moving ever. |
- __ add(lr, pc, Operand(4)); // compute return address: (pc + 8) + 4 |
- __ push(lr); |
-#if !defined(__arm__) |
- // Notify the simulator of the transition to C code. |
- __ swi(assembler::arm::call_rt_r5); |
-#else /* !defined(__arm__) */ |
- __ mov(pc, Operand(r5)); |
-#endif /* !defined(__arm__) */ |
- // result is in r0 or r0:r1 - do not destroy these registers! |
- |
- // check for failure result |
- Label failure_returned; |
- ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0); |
- // Lower 2 bits of r2 are 0 iff r0 has failure tag. |
- __ add(r2, r0, Operand(1)); |
- __ tst(r2, Operand(kFailureTagMask)); |
- __ b(eq, &failure_returned); |
- |
- // Exit C frame and return. |
- // r0:r1: result |
- // sp: stack pointer |
- // fp: frame pointer |
- // pp: caller's parameter pointer pp (restored as C callee-saved) |
- __ LeaveExitFrame(frame_type); |
- |
- // check if we should retry or throw exception |
- Label retry; |
- __ bind(&failure_returned); |
- ASSERT(Failure::RETRY_AFTER_GC == 0); |
- __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); |
- __ b(eq, &retry); |
- |
- Label continue_exception; |
- // If the returned failure is EXCEPTION then promote Top::pending_exception(). |
- __ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); |
- __ b(ne, &continue_exception); |
- |
- // Retrieve the pending exception and clear the variable. |
- __ mov(ip, Operand(Factory::the_hole_value().location())); |
- __ ldr(r3, MemOperand(ip)); |
- __ mov(ip, Operand(Top::pending_exception_address())); |
- __ ldr(r0, MemOperand(ip)); |
- __ str(r3, MemOperand(ip)); |
- |
- __ bind(&continue_exception); |
- // Special handling of out of memory exception. |
- Failure* out_of_memory = Failure::OutOfMemoryException(); |
- __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); |
- __ b(eq, throw_out_of_memory_exception); |
- |
- // Handle normal exception. |
- __ jmp(throw_normal_exception); |
- |
- __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying |
-} |
- |
- |
-void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { |
- // Called from JavaScript; parameters are on stack as if calling JS function |
- // r0: number of arguments including receiver |
- // r1: pointer to builtin function |
- // fp: frame pointer (restored after C call) |
- // sp: stack pointer (restored as callee's pp after C call) |
- // cp: current context (C callee-saved) |
- // pp: caller's parameter pointer pp (C callee-saved) |
- |
- // NOTE: Invocations of builtins may return failure objects |
- // instead of a proper result. The builtin entry handles |
- // this by performing a garbage collection and retrying the |
- // builtin once. |
- |
- StackFrame::Type frame_type = is_debug_break |
- ? StackFrame::EXIT_DEBUG |
- : StackFrame::EXIT; |
- |
- // Enter the exit frame that transitions from JavaScript to C++. |
- __ EnterExitFrame(frame_type); |
- |
- // r4: number of arguments (C callee-saved) |
- // r5: pointer to builtin function (C callee-saved) |
- // r6: pointer to first argument (C callee-saved) |
- |
- Label throw_out_of_memory_exception; |
- Label throw_normal_exception; |
- |
-#ifdef DEBUG |
- if (FLAG_gc_greedy) { |
- Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE); |
- __ mov(r0, Operand(reinterpret_cast<intptr_t>(failure))); |
- } |
- GenerateCore(masm, |
- &throw_normal_exception, |
- &throw_out_of_memory_exception, |
- frame_type, |
- FLAG_gc_greedy); |
-#else |
- GenerateCore(masm, |
- &throw_normal_exception, |
- &throw_out_of_memory_exception, |
- frame_type, |
- false); |
-#endif |
- GenerateCore(masm, |
- &throw_normal_exception, |
- &throw_out_of_memory_exception, |
- frame_type, |
- true); |
- |
- __ bind(&throw_out_of_memory_exception); |
- GenerateThrowOutOfMemory(masm); |
- // control flow for generated will not return. |
- |
- __ bind(&throw_normal_exception); |
- GenerateThrowTOS(masm); |
-} |
- |
- |
-void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { |
- // r0: code entry |
- // r1: function |
- // r2: receiver |
- // r3: argc |
- // [sp+0]: argv |
- |
- Label invoke, exit; |
- |
- // Called from C, so do not pop argc and args on exit (preserve sp) |
- // No need to save register-passed args |
- // Save callee-saved registers (incl. cp, pp, and fp), sp, and lr |
- __ stm(db_w, sp, kCalleeSaved | lr.bit()); |
- |
- // Get address of argv, see stm above. |
- // r0: code entry |
- // r1: function |
- // r2: receiver |
- // r3: argc |
- __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize)); |
- __ ldr(r4, MemOperand(r4)); // argv |
- |
- // Push a frame with special values setup to mark it as an entry frame. |
- // r0: code entry |
- // r1: function |
- // r2: receiver |
- // r3: argc |
- // r4: argv |
- int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; |
- __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used. |
- __ mov(r7, Operand(~ArgumentsAdaptorFrame::SENTINEL)); |
- __ mov(r6, Operand(Smi::FromInt(marker))); |
- __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address))); |
- __ ldr(r5, MemOperand(r5)); |
- __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit()); |
- |
- // Setup frame pointer for the frame to be pushed. |
- __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); |
- |
- // Call a faked try-block that does the invoke. |
- __ bl(&invoke); |
- |
- // Caught exception: Store result (exception) in the pending |
- // exception field in the JSEnv and return a failure sentinel. |
- // Coming in here the fp will be invalid because the PushTryHandler below |
- // sets it to 0 to signal the existence of the JSEntry frame. |
- __ mov(ip, Operand(Top::pending_exception_address())); |
- __ str(r0, MemOperand(ip)); |
- __ mov(r0, Operand(Handle<Failure>(Failure::Exception()))); |
- __ b(&exit); |
- |
- // Invoke: Link this frame into the handler chain. |
- __ bind(&invoke); |
- // Must preserve r0-r4, r5-r7 are available. |
- __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); |
- // If an exception not caught by another handler occurs, this handler returns |
- // control to the code after the bl(&invoke) above, which restores all |
- // kCalleeSaved registers (including cp, pp and fp) to their saved values |
- // before returning a failure to C. |
- |
- // Clear any pending exceptions. |
- __ mov(ip, Operand(ExternalReference::the_hole_value_location())); |
- __ ldr(r5, MemOperand(ip)); |
- __ mov(ip, Operand(Top::pending_exception_address())); |
- __ str(r5, MemOperand(ip)); |
- |
- // Invoke the function by calling through JS entry trampoline builtin. |
- // Notice that we cannot store a reference to the trampoline code directly in |
- // this stub, because runtime stubs are not traversed when doing GC. |
- |
- // Expected registers by Builtins::JSEntryTrampoline |
- // r0: code entry |
- // r1: function |
- // r2: receiver |
- // r3: argc |
- // r4: argv |
- if (is_construct) { |
- ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline); |
- __ mov(ip, Operand(construct_entry)); |
- } else { |
- ExternalReference entry(Builtins::JSEntryTrampoline); |
- __ mov(ip, Operand(entry)); |
- } |
- __ ldr(ip, MemOperand(ip)); // deref address |
- |
- // Branch and link to JSEntryTrampoline |
- __ mov(lr, Operand(pc)); |
- __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); |
- |
- // Unlink this frame from the handler chain. When reading the |
- // address of the next handler, there is no need to use the address |
- // displacement since the current stack pointer (sp) points directly |
- // to the stack handler. |
- __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset)); |
- __ mov(ip, Operand(ExternalReference(Top::k_handler_address))); |
- __ str(r3, MemOperand(ip)); |
- // No need to restore registers |
- __ add(sp, sp, Operand(StackHandlerConstants::kSize)); |
- |
- __ bind(&exit); // r0 holds result |
- // Restore the top frame descriptors from the stack. |
- __ pop(r3); |
- __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); |
- __ str(r3, MemOperand(ip)); |
- |
- // Reset the stack to the callee saved registers. |
- __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); |
- |
- // Restore callee-saved registers and return. |
-#ifdef DEBUG |
- if (FLAG_debug_code) __ mov(lr, Operand(pc)); |
-#endif |
- __ ldm(ia_w, sp, kCalleeSaved | pc.bit()); |
-} |
- |
- |
class ArgumentsAccessStub: public CodeStub { |
public: |
explicit ArgumentsAccessStub(bool is_length) : is_length_(is_length) { } |
@@ -1921,91 +981,6 @@ |
}; |
-void ArgumentsAccessStub::Generate(MacroAssembler* masm) { |
- // ----------- S t a t e ------------- |
- // -- r0: formal number of parameters for the calling function |
- // -- r1: key (if value access) |
- // -- lr: return address |
- // ----------------------------------- |
- |
- // Check that the key is a smi for non-length accesses. |
- Label slow; |
- if (!is_length_) { |
- __ tst(r1, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- } |
- |
- // Check if the calling frame is an arguments adaptor frame. |
- // r0: formal number of parameters |
- // r1: key (if access) |
- Label adaptor; |
- __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
- __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); |
- __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL)); |
- __ b(eq, &adaptor); |
- |
- static const int kParamDisplacement = |
- StandardFrameConstants::kCallerSPOffset - kPointerSize; |
- |
- if (is_length_) { |
- // Nothing to do: the formal length of parameters has been passed in r0 |
- // by the calling function. |
- } else { |
- // Check index against formal parameter count. Use unsigned comparison to |
- // get the negative check for free. |
- // r0: formal number of parameters |
- // r1: index |
- __ cmp(r1, r0); |
- __ b(cs, &slow); |
- |
- // Read the argument from the current frame. |
- __ sub(r3, r0, r1); |
- __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); |
- __ ldr(r0, MemOperand(r3, kParamDisplacement)); |
- } |
- |
- // Return to the calling function. |
- __ mov(pc, lr); |
- |
- // An arguments adaptor frame is present. Find the length or the actual |
- // argument in the calling frame. |
- // r0: formal number of parameters |
- // r1: key |
- // r2: adaptor frame pointer |
- __ bind(&adaptor); |
- // Read the arguments length from the adaptor frame. This is the result if |
- // only accessing the length, otherwise it is used in accessing the value |
- __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
- |
- if (!is_length_) { |
- // Check index against actual arguments count. Use unsigned comparison to |
- // get the negative check for free. |
- // r0: actual number of parameter |
- // r1: index |
- // r2: adaptor frame point |
- __ cmp(r1, r0); |
- __ b(cs, &slow); |
- |
- // Read the argument from the adaptor frame. |
- __ sub(r3, r0, r1); |
- __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); |
- __ ldr(r0, MemOperand(r3, kParamDisplacement)); |
- } |
- |
- // Return to the calling function. |
- __ mov(pc, lr); |
- |
- if (!is_length_) { |
- __ bind(&slow); |
- __ push(r1); |
- __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); |
- } |
-} |
- |
- |
-#undef __ |
-#define __ masm_-> |
- |
void ArmCodeGenerator::GetReferenceProperty(Expression* key) { |
ASSERT(!ref()->is_illegal()); |
Reference::Type type = ref()->type(); |
@@ -2044,42 +1019,6 @@ |
} |
-#undef __ |
-#define __ masm-> |
- |
-void ArmCodeGenerator::SetReferenceProperty(CodeGenerator* cgen, |
- Reference* ref, |
- Expression* key) { |
- ASSERT(!ref->is_illegal()); |
- MacroAssembler* masm = cgen->masm(); |
- |
- if (ref->type() == Reference::NAMED) { |
- // Compute the name of the property. |
- Literal* literal = key->AsLiteral(); |
- Handle<String> name(String::cast(*literal->handle())); |
- |
- // Call the appropriate IC code. |
- masm->pop(r0); // value |
- // Setup the name register. |
- masm->mov(r2, Operand(name)); |
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
- masm->Call(ic, RelocInfo::CODE_TARGET); |
- |
- } else { |
- // Access keyed property. |
- ASSERT(ref->type() == Reference::KEYED); |
- |
- masm->pop(r0); // value |
- SetPropertyStub stub; |
- masm->CallStub(&stub); |
- } |
- masm->push(r0); |
-} |
- |
- |
-#undef __ |
-#define __ masm_-> |
- |
void ArmCodeGenerator::GenericBinaryOperation(Token::Value op) { |
// sp[0] : y |
// sp[1] : x |
@@ -2422,34 +1361,6 @@ |
}; |
-void CallFunctionStub::Generate(MacroAssembler* masm) { |
- Label slow; |
- // Get the function to call from the stack. |
- // function, receiver [, arguments] |
- masm->ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize)); |
- |
- // Check that the function is really a JavaScript function. |
- // r1: pushed function (to be verified) |
- masm->tst(r1, Operand(kSmiTagMask)); |
- masm->b(eq, &slow); |
- // Get the map of the function object. |
- masm->ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
- masm->ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
- masm->cmp(r2, Operand(JS_FUNCTION_TYPE)); |
- masm->b(ne, &slow); |
- |
- // Fast-case: Invoke the function now. |
- // r1: pushed function |
- ParameterCount actual(argc_); |
- masm->InvokeFunction(r1, actual, JUMP_FUNCTION); |
- |
- // Slow-case: Non-function called. |
- masm->bind(&slow); |
- masm->mov(r0, Operand(argc_)); // Setup the number of arguments. |
- masm->InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS); |
-} |
- |
- |
// Call the function on the stack with the given arguments. |
void ArmCodeGenerator::CallWithArguments(ZoneList<Expression*>* args, |
int position) { |
@@ -4532,8 +3443,1074 @@ |
#undef __ |
+#define __ masm-> |
+MemOperand ArmCodeGenerator::SlotOperand(CodeGenerator* cgen, |
+ Slot* slot, |
+ Register tmp) { |
+ // Currently, this assertion will fail if we try to assign to |
+ // a constant variable that is constant because it is read-only |
+ // (such as the variable referring to a named function expression). |
+ // We need to implement assignments to read-only variables. |
+ // Ideally, we should do this during AST generation (by converting |
+ // such assignments into expression statements); however, in general |
+ // we may not be able to make the decision until past AST generation, |
+ // that is when the entire program is known. |
+ ASSERT(slot != NULL); |
+ int index = slot->index(); |
+ switch (slot->type()) { |
+ case Slot::PARAMETER: |
+ return ParameterOperand(cgen, index); |
+ case Slot::LOCAL: { |
+ ASSERT(0 <= index && |
+ index < cgen->scope()->num_stack_slots() && |
+ index >= 0); |
+ int local_offset = JavaScriptFrameConstants::kLocal0Offset - |
+ index * kPointerSize; |
+ return MemOperand(fp, local_offset); |
+ } |
+ |
+ case Slot::CONTEXT: { |
+ MacroAssembler* masm = cgen->masm(); |
+ // Follow the context chain if necessary. |
+ ASSERT(!tmp.is(cp)); // do not overwrite context register |
+ Register context = cp; |
+ int chain_length = |
+ cgen->scope()->ContextChainLength(slot->var()->scope()); |
+ for (int i = chain_length; i-- > 0;) { |
+ // Load the closure. |
+ // (All contexts, even 'with' contexts, have a closure, |
+ // and it is the same for all contexts inside a function. |
+ // There is no need to go to the function context first.) |
+ __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); |
+ // Load the function context (which is the incoming, outer context). |
+ __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset)); |
+ context = tmp; |
+ } |
+ // We may have a 'with' context now. Get the function context. |
+ // (In fact this mov may never be the needed, since the scope analysis |
+ // may not permit a direct context access in this case and thus we are |
+ // always at a function context. However it is safe to dereference be- |
+ // cause the function context of a function context is itself. Before |
+ // deleting this mov we should try to create a counter-example first, |
+ // though...) |
+ __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); |
+ return ContextOperand(tmp, index); |
+ } |
+ |
+ default: |
+ UNREACHABLE(); |
+ return MemOperand(r0, 0); |
+ } |
+} |
+ |
+ |
+void Property::GenerateStoreCode(CodeGenerator* cgen, |
+ Reference* ref, |
+ InitState init_state) { |
+ MacroAssembler* masm = cgen->masm(); |
+ Comment cmnt(masm, "[ Store to Property"); |
+ __ RecordPosition(position()); |
+ ArmCodeGenerator::SetReferenceProperty(cgen, ref, key()); |
+} |
+ |
+ |
+void VariableProxy::GenerateStoreCode(CodeGenerator* cgen, |
+ Reference* ref, |
+ InitState init_state) { |
+ MacroAssembler* masm = cgen->masm(); |
+ Comment cmnt(masm, "[ Store to VariableProxy"); |
+ Variable* node = var(); |
+ |
+ Expression* expr = node->rewrite(); |
+ if (expr != NULL) { |
+ expr->GenerateStoreCode(cgen, ref, init_state); |
+ } else { |
+ ASSERT(node->is_global()); |
+ if (node->AsProperty() != NULL) { |
+ __ RecordPosition(node->AsProperty()->position()); |
+ } |
+ Expression* key = new Literal(node->name()); |
+ ArmCodeGenerator::SetReferenceProperty(cgen, ref, key); |
+ } |
+} |
+ |
+ |
+void Slot::GenerateStoreCode(CodeGenerator* cgen, |
+ Reference* ref, |
+ InitState init_state) { |
+ MacroAssembler* masm = cgen->masm(); |
+ Comment cmnt(masm, "[ Store to Slot"); |
+ |
+ if (type() == Slot::LOOKUP) { |
+ ASSERT(var()->mode() == Variable::DYNAMIC); |
+ |
+ // For now, just do a runtime call. |
+ __ push(cp); |
+ __ mov(r0, Operand(var()->name())); |
+ __ push(r0); |
+ |
+ if (init_state == CONST_INIT) { |
+ // Same as the case for a normal store, but ignores attribute |
+ // (e.g. READ_ONLY) of context slot so that we can initialize const |
+ // properties (introduced via eval("const foo = (some expr);")). Also, |
+ // uses the current function context instead of the top context. |
+ // |
+ // Note that we must declare the foo upon entry of eval(), via a |
+ // context slot declaration, but we cannot initialize it at the same |
+ // time, because the const declaration may be at the end of the eval |
+ // code (sigh...) and the const variable may have been used before |
+ // (where its value is 'undefined'). Thus, we can only do the |
+ // initialization when we actually encounter the expression and when |
+ // the expression operands are defined and valid, and thus we need the |
+ // split into 2 operations: declaration of the context slot followed |
+ // by initialization. |
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
+ } else { |
+ __ CallRuntime(Runtime::kStoreContextSlot, 3); |
+ } |
+ // Storing a variable must keep the (new) value on the expression |
+ // stack. This is necessary for compiling assignment expressions. |
+ __ push(r0); |
+ |
+ } else { |
+ ASSERT(var()->mode() != Variable::DYNAMIC); |
+ |
+ Label exit; |
+ if (init_state == CONST_INIT) { |
+ ASSERT(var()->mode() == Variable::CONST); |
+ // Only the first const initialization must be executed (the slot |
+ // still contains 'the hole' value). When the assignment is executed, |
+ // the code is identical to a normal store (see below). |
+ Comment cmnt(masm, "[ Init const"); |
+ __ ldr(r2, ArmCodeGenerator::SlotOperand(cgen, this, r2)); |
+ __ cmp(r2, Operand(Factory::the_hole_value())); |
+ __ b(ne, &exit); |
+ } |
+ |
+ // We must execute the store. |
+ // r2 may be loaded with context; used below in RecordWrite. |
+ // Storing a variable must keep the (new) value on the stack. This is |
+ // necessary for compiling assignment expressions. |
+ // |
+ // Note: We will reach here even with var()->mode() == Variable::CONST |
+ // because of const declarations which will initialize consts to 'the |
+ // hole' value and by doing so, end up calling this code. r2 may be |
+ // loaded with context; used below in RecordWrite. |
+ __ pop(r0); |
+ __ str(r0, ArmCodeGenerator::SlotOperand(cgen, this, r2)); |
+ __ push(r0); |
+ |
+ if (type() == Slot::CONTEXT) { |
+ // Skip write barrier if the written value is a smi. |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(eq, &exit); |
+ // r2 is loaded with context when calling SlotOperand above. |
+ int offset = FixedArray::kHeaderSize + index() * kPointerSize; |
+ __ mov(r3, Operand(offset)); |
+ __ RecordWrite(r2, r3, r1); |
+ } |
+ // If we definitely did not jump over the assignment, we do not need to |
+ // bind the exit label. Doing so can defeat peephole optimization. |
+ if (init_state == CONST_INIT || type() == Slot::CONTEXT) { |
+ __ bind(&exit); |
+ } |
+ } |
+} |
+ |
+ |
+void GetPropertyStub::Generate(MacroAssembler* masm) { |
+ // sp[0]: key |
+ // sp[1]: receiver |
+ Label slow, fast; |
+ // Get the key and receiver object from the stack. |
+ __ ldm(ia, sp, r0.bit() | r1.bit()); |
+ // Check that the key is a smi. |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ __ mov(r0, Operand(r0, ASR, kSmiTagSize)); |
+ // Check that the object isn't a smi. |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(eq, &slow); |
+ |
+ // Check that the object is some kind of JS object EXCEPT JS Value type. |
+ // In the case that the object is a value-wrapper object, |
+ // we enter the runtime system to make sure that indexing into string |
+ // objects work as intended. |
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); |
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
+ __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
+ __ cmp(r2, Operand(JS_OBJECT_TYPE)); |
+ __ b(lt, &slow); |
+ |
+ // Get the elements array of the object. |
+ __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); |
+ // Check that the object is in fast mode (not dictionary). |
+ __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); |
+ __ cmp(r3, Operand(Factory::hash_table_map())); |
+ __ b(eq, &slow); |
+ // Check that the key (index) is within bounds. |
+ __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset)); |
+ __ cmp(r0, Operand(r3)); |
+ __ b(lo, &fast); |
+ |
+ // Slow case: Push extra copies of the arguments (2). |
+ __ bind(&slow); |
+ __ ldm(ia, sp, r0.bit() | r1.bit()); |
+ __ stm(db_w, sp, r0.bit() | r1.bit()); |
+ // Do tail-call to runtime routine. |
+ __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2); |
+ |
+ // Fast case: Do the load. |
+ __ bind(&fast); |
+ __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag)); |
+ __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2)); |
+ __ cmp(r0, Operand(Factory::the_hole_value())); |
+ // In case the loaded value is the_hole we have to consult GetProperty |
+ // to ensure the prototype chain is searched. |
+ __ b(eq, &slow); |
+ |
+ __ StubReturn(1); |
+} |
+ |
+ |
+void SetPropertyStub::Generate(MacroAssembler* masm) { |
+ // r0 : value |
+ // sp[0] : key |
+ // sp[1] : receiver |
+ |
+ Label slow, fast, array, extra, exit; |
+ // Get the key and the object from the stack. |
+ __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver |
+ // Check that the key is a smi. |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ // Check that the object isn't a smi. |
+ __ tst(r3, Operand(kSmiTagMask)); |
+ __ b(eq, &slow); |
+ // Get the type of the object from its map. |
+ __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); |
+ __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
+ // Check if the object is a JS array or not. |
+ __ cmp(r2, Operand(JS_ARRAY_TYPE)); |
+ __ b(eq, &array); |
+ // Check that the object is some kind of JS object. |
+ __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE)); |
+ __ b(lt, &slow); |
+ |
+ |
+ // Object case: Check key against length in the elements array. |
+ __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset)); |
+ // Check that the object is in fast mode (not dictionary). |
+ __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); |
+ __ cmp(r2, Operand(Factory::hash_table_map())); |
+ __ b(eq, &slow); |
+ // Untag the key (for checking against untagged length in the fixed array). |
+ __ mov(r1, Operand(r1, ASR, kSmiTagSize)); |
+ // Compute address to store into and check array bounds. |
+ __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag)); |
+ __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2)); |
+ __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset)); |
+ __ cmp(r1, Operand(ip)); |
+ __ b(lo, &fast); |
+ |
+ |
+ // Slow case: Push extra copies of the arguments (3). |
+ __ bind(&slow); |
+ __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object |
+ __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); |
+ // Do tail-call to runtime routine. |
+ __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3); |
+ |
+ |
+ // Extra capacity case: Check if there is extra capacity to |
+ // perform the store and update the length. Used for adding one |
+ // element to the array by writing to array[array.length]. |
+ // r0 == value, r1 == key, r2 == elements, r3 == object |
+ __ bind(&extra); |
+ __ b(ne, &slow); // do not leave holes in the array |
+ __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag |
+ __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset)); |
+ __ cmp(r1, Operand(ip)); |
+ __ b(hs, &slow); |
+ __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag |
+ __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment |
+ __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset)); |
+ __ mov(r3, Operand(r2)); |
+ // NOTE: Computing the address to store into must take the fact |
+ // that the key has been incremented into account. |
+ int displacement = Array::kHeaderSize - kHeapObjectTag - |
+ ((1 << kSmiTagSize) * 2); |
+ __ add(r2, r2, Operand(displacement)); |
+ __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
+ __ b(&fast); |
+ |
+ |
+ // Array case: Get the length and the elements array from the JS |
+ // array. Check that the array is in fast mode; if it is the |
+ // length is always a smi. |
+ // r0 == value, r3 == object |
+ __ bind(&array); |
+ __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset)); |
+ __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); |
+ __ cmp(r1, Operand(Factory::hash_table_map())); |
+ __ b(eq, &slow); |
+ |
+ // Check the key against the length in the array, compute the |
+ // address to store into and fall through to fast case. |
+ __ ldr(r1, MemOperand(sp)); |
+ // r0 == value, r1 == key, r2 == elements, r3 == object. |
+ __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset)); |
+ __ cmp(r1, Operand(ip)); |
+ __ b(hs, &extra); |
+ __ mov(r3, Operand(r2)); |
+ __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag)); |
+ __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
+ |
+ |
+ // Fast case: Do the store. |
+ // r0 == value, r2 == address to store into, r3 == elements |
+ __ bind(&fast); |
+ __ str(r0, MemOperand(r2)); |
+ // Skip write barrier if the written value is a smi. |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(eq, &exit); |
+ // Update write barrier for the elements array address. |
+ __ sub(r1, r2, Operand(r3)); |
+ __ RecordWrite(r3, r1, r2); |
+ __ bind(&exit); |
+ __ StubReturn(1); |
+} |
+ |
+ |
+void GenericBinaryOpStub::Generate(MacroAssembler* masm) { |
+ // r1 : x |
+ // r0 : y |
+ // result : r0 |
+ |
+ switch (op_) { |
+ case Token::ADD: { |
+ Label slow, exit; |
+ // fast path |
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
+ __ add(r0, r1, Operand(r0), SetCC); // add y optimistically |
+ // go slow-path in case of overflow |
+ __ b(vs, &slow); |
+ // go slow-path in case of non-smi operands |
+ ASSERT(kSmiTag == 0); // adjust code below |
+ __ tst(r2, Operand(kSmiTagMask)); |
+ __ b(eq, &exit); |
+ // slow path |
+ __ bind(&slow); |
+ __ sub(r0, r0, Operand(r1)); // revert optimistic add |
+ __ push(r1); |
+ __ push(r0); |
+ __ mov(r0, Operand(1)); // set number of arguments |
+ __ InvokeBuiltin(Builtins::ADD, JUMP_JS); |
+ // done |
+ __ bind(&exit); |
+ break; |
+ } |
+ |
+ case Token::SUB: { |
+ Label slow, exit; |
+ // fast path |
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
+ __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically |
+ // go slow-path in case of overflow |
+ __ b(vs, &slow); |
+ // go slow-path in case of non-smi operands |
+ ASSERT(kSmiTag == 0); // adjust code below |
+ __ tst(r2, Operand(kSmiTagMask)); |
+ __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result |
+ __ b(eq, &exit); |
+ // slow path |
+ __ bind(&slow); |
+ __ push(r1); |
+ __ push(r0); |
+ __ mov(r0, Operand(1)); // set number of arguments |
+ __ InvokeBuiltin(Builtins::SUB, JUMP_JS); |
+ // done |
+ __ bind(&exit); |
+ break; |
+ } |
+ |
+ case Token::MUL: { |
+ Label slow, exit; |
+ // tag check |
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
+ ASSERT(kSmiTag == 0); // adjust code below |
+ __ tst(r2, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ // remove tag from one operand (but keep sign), so that result is smi |
+ __ mov(ip, Operand(r0, ASR, kSmiTagSize)); |
+ // do multiplication |
+ __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1 |
+ // go slow on overflows (overflow bit is not set) |
+ __ mov(ip, Operand(r3, ASR, 31)); |
+ __ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical |
+ __ b(ne, &slow); |
+ // go slow on zero result to handle -0 |
+ __ tst(r3, Operand(r3)); |
+ __ mov(r0, Operand(r3), LeaveCC, ne); |
+ __ b(ne, &exit); |
+ // slow case |
+ __ bind(&slow); |
+ __ push(r1); |
+ __ push(r0); |
+ __ mov(r0, Operand(1)); // set number of arguments |
+ __ InvokeBuiltin(Builtins::MUL, JUMP_JS); |
+ // done |
+ __ bind(&exit); |
+ break; |
+ } |
+ |
+ case Token::BIT_OR: |
+ case Token::BIT_AND: |
+ case Token::BIT_XOR: { |
+ Label slow, exit; |
+ // tag check |
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
+ ASSERT(kSmiTag == 0); // adjust code below |
+ __ tst(r2, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ switch (op_) { |
+ case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break; |
+ case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break; |
+ case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; |
+ default: UNREACHABLE(); |
+ } |
+ __ b(&exit); |
+ __ bind(&slow); |
+ __ push(r1); // restore stack |
+ __ push(r0); |
+ __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
+ switch (op_) { |
+ case Token::BIT_OR: |
+ __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); |
+ break; |
+ case Token::BIT_AND: |
+ __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); |
+ break; |
+ case Token::BIT_XOR: |
+ __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ __ bind(&exit); |
+ break; |
+ } |
+ |
+ case Token::SHL: |
+ case Token::SHR: |
+ case Token::SAR: { |
+ Label slow, exit; |
+ // tag check |
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
+ ASSERT(kSmiTag == 0); // adjust code below |
+ __ tst(r2, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ // remove tags from operands (but keep sign) |
+ __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x |
+ __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
+ // use only the 5 least significant bits of the shift count |
+ __ and_(r2, r2, Operand(0x1f)); |
+ // perform operation |
+ switch (op_) { |
+ case Token::SAR: |
+ __ mov(r3, Operand(r3, ASR, r2)); |
+ // no checks of result necessary |
+ break; |
+ |
+ case Token::SHR: |
+ __ mov(r3, Operand(r3, LSR, r2)); |
+ // check that the *unsigned* result fits in a smi |
+ // neither of the two high-order bits can be set: |
+ // - 0x80000000: high bit would be lost when smi tagging |
+ // - 0x40000000: this number would convert to negative when |
+ // smi tagging these two cases can only happen with shifts |
+ // by 0 or 1 when handed a valid smi |
+ __ and_(r2, r3, Operand(0xc0000000), SetCC); |
+ __ b(ne, &slow); |
+ break; |
+ |
+ case Token::SHL: |
+ __ mov(r3, Operand(r3, LSL, r2)); |
+ // check that the *signed* result fits in a smi |
+ __ add(r2, r3, Operand(0x40000000), SetCC); |
+ __ b(mi, &slow); |
+ break; |
+ |
+ default: UNREACHABLE(); |
+ } |
+ // tag result and store it in r0 |
+ ASSERT(kSmiTag == 0); // adjust code below |
+ __ mov(r0, Operand(r3, LSL, kSmiTagSize)); |
+ __ b(&exit); |
+ // slow case |
+ __ bind(&slow); |
+ __ push(r1); // restore stack |
+ __ push(r0); |
+ __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
+ switch (op_) { |
+ case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break; |
+ case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break; |
+ case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; |
+ default: UNREACHABLE(); |
+ } |
+ __ bind(&exit); |
+ break; |
+ } |
+ |
+ default: UNREACHABLE(); |
+ } |
+ __ Ret(); |
+} |
+ |
+ |
+void StackCheckStub::Generate(MacroAssembler* masm) { |
+ Label within_limit; |
+ __ mov(ip, Operand(ExternalReference::address_of_stack_guard_limit())); |
+ __ ldr(ip, MemOperand(ip)); |
+ __ cmp(sp, Operand(ip)); |
+ __ b(hs, &within_limit); |
+ // Do tail-call to runtime routine. |
+ __ push(r0); |
+ __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1); |
+ __ bind(&within_limit); |
+ |
+ __ StubReturn(1); |
+} |
+ |
+ |
+void UnarySubStub::Generate(MacroAssembler* masm) { |
+ Label undo; |
+ Label slow; |
+ Label done; |
+ |
+ // Enter runtime system if the value is not a smi. |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ |
+ // Enter runtime system if the value of the expression is zero |
+ // to make sure that we switch between 0 and -0. |
+ __ cmp(r0, Operand(0)); |
+ __ b(eq, &slow); |
+ |
+ // The value of the expression is a smi that is not zero. Try |
+ // optimistic subtraction '0 - value'. |
+ __ rsb(r1, r0, Operand(0), SetCC); |
+ __ b(vs, &slow); |
+ |
+ // If result is a smi we are done. |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result |
+ __ b(eq, &done); |
+ |
+ // Enter runtime system. |
+ __ bind(&slow); |
+ __ push(r0); |
+ __ mov(r0, Operand(0)); // set number of arguments |
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS); |
+ |
+ __ bind(&done); |
+ __ StubReturn(1); |
+} |
+ |
+ |
+void InvokeBuiltinStub::Generate(MacroAssembler* masm) { |
+ __ push(r0); |
+ __ mov(r0, Operand(0)); // set number of arguments |
+ switch (kind_) { |
+ case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break; |
+ case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break; |
+ case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break; |
+ default: UNREACHABLE(); |
+ } |
+ __ StubReturn(argc_); |
+} |
+ |
+ |
+void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { |
+ // r0 holds exception |
+ ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code |
+ __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); |
+ __ ldr(sp, MemOperand(r3)); |
+ __ pop(r2); // pop next in chain |
+ __ str(r2, MemOperand(r3)); |
+ // restore parameter- and frame-pointer and pop state. |
+ __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); |
+ // Before returning we restore the context from the frame pointer if not NULL. |
+ // The frame pointer is NULL in the exception handler of a JS entry frame. |
+ __ cmp(fp, Operand(0)); |
+ // Set cp to NULL if fp is NULL. |
+ __ mov(cp, Operand(0), LeaveCC, eq); |
+ // Restore cp otherwise. |
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); |
+ if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); |
+ __ pop(pc); |
+} |
+ |
+ |
+void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { |
+ // Fetch top stack handler. |
+ __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); |
+ __ ldr(r3, MemOperand(r3)); |
+ |
+ // Unwind the handlers until the ENTRY handler is found. |
+ Label loop, done; |
+ __ bind(&loop); |
+ // Load the type of the current stack handler. |
+ const int kStateOffset = StackHandlerConstants::kAddressDisplacement + |
+ StackHandlerConstants::kStateOffset; |
+ __ ldr(r2, MemOperand(r3, kStateOffset)); |
+ __ cmp(r2, Operand(StackHandler::ENTRY)); |
+ __ b(eq, &done); |
+ // Fetch the next handler in the list. |
+ const int kNextOffset = StackHandlerConstants::kAddressDisplacement + |
+ StackHandlerConstants::kNextOffset; |
+ __ ldr(r3, MemOperand(r3, kNextOffset)); |
+ __ jmp(&loop); |
+ __ bind(&done); |
+ |
+ // Set the top handler address to next handler past the current ENTRY handler. |
+ __ ldr(r0, MemOperand(r3, kNextOffset)); |
+ __ mov(r2, Operand(ExternalReference(Top::k_handler_address))); |
+ __ str(r0, MemOperand(r2)); |
+ |
+ // Set external caught exception to false. |
+ __ mov(r0, Operand(false)); |
+ ExternalReference external_caught(Top::k_external_caught_exception_address); |
+ __ mov(r2, Operand(external_caught)); |
+ __ str(r0, MemOperand(r2)); |
+ |
+ // Set pending exception and r0 to out of memory exception. |
+ Failure* out_of_memory = Failure::OutOfMemoryException(); |
+ __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); |
+ __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); |
+ __ str(r0, MemOperand(r2)); |
+ |
+ // Restore the stack to the address of the ENTRY handler |
+ __ mov(sp, Operand(r3)); |
+ |
+ // restore parameter- and frame-pointer and pop state. |
+ __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); |
+ // Before returning we restore the context from the frame pointer if not NULL. |
+ // The frame pointer is NULL in the exception handler of a JS entry frame. |
+ __ cmp(fp, Operand(0)); |
+ // Set cp to NULL if fp is NULL. |
+ __ mov(cp, Operand(0), LeaveCC, eq); |
+ // Restore cp otherwise. |
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); |
+ if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); |
+ __ pop(pc); |
+} |
+ |
+ |
+void CEntryStub::GenerateCore(MacroAssembler* masm, |
+ Label* throw_normal_exception, |
+ Label* throw_out_of_memory_exception, |
+ StackFrame::Type frame_type, |
+ bool do_gc) { |
+ // r0: result parameter for PerformGC, if any |
+ // r4: number of arguments including receiver (C callee-saved) |
+ // r5: pointer to builtin function (C callee-saved) |
+ // r6: pointer to the first argument (C callee-saved) |
+ |
+ if (do_gc) { |
+ // Passing r0. |
+ __ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY); |
+ } |
+ |
+ // Call C built-in. |
+ // r0 = argc, r1 = argv |
+ __ mov(r0, Operand(r4)); |
+ __ mov(r1, Operand(r6)); |
+ |
+ // TODO(1242173): To let the GC traverse the return address of the exit |
+ // frames, we need to know where the return address is. Right now, |
+ // we push it on the stack to be able to find it again, but we never |
+ // restore from it in case of changes, which makes it impossible to |
+ // support moving the C entry code stub. This should be fixed, but currently |
+ // this is OK because the CEntryStub gets generated so early in the V8 boot |
+ // sequence that it is not moving ever. |
+ __ add(lr, pc, Operand(4)); // compute return address: (pc + 8) + 4 |
+ __ push(lr); |
+#if !defined(__arm__) |
+ // Notify the simulator of the transition to C code. |
+ __ swi(assembler::arm::call_rt_r5); |
+#else /* !defined(__arm__) */ |
+ __ mov(pc, Operand(r5)); |
+#endif /* !defined(__arm__) */ |
+ // result is in r0 or r0:r1 - do not destroy these registers! |
+ |
+ // check for failure result |
+ Label failure_returned; |
+ ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0); |
+ // Lower 2 bits of r2 are 0 iff r0 has failure tag. |
+ __ add(r2, r0, Operand(1)); |
+ __ tst(r2, Operand(kFailureTagMask)); |
+ __ b(eq, &failure_returned); |
+ |
+ // Exit C frame and return. |
+ // r0:r1: result |
+ // sp: stack pointer |
+ // fp: frame pointer |
+ // pp: caller's parameter pointer pp (restored as C callee-saved) |
+ __ LeaveExitFrame(frame_type); |
+ |
+ // check if we should retry or throw exception |
+ Label retry; |
+ __ bind(&failure_returned); |
+ ASSERT(Failure::RETRY_AFTER_GC == 0); |
+ __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); |
+ __ b(eq, &retry); |
+ |
+ Label continue_exception; |
+ // If the returned failure is EXCEPTION then promote Top::pending_exception(). |
+ __ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); |
+ __ b(ne, &continue_exception); |
+ |
+ // Retrieve the pending exception and clear the variable. |
+ __ mov(ip, Operand(Factory::the_hole_value().location())); |
+ __ ldr(r3, MemOperand(ip)); |
+ __ mov(ip, Operand(Top::pending_exception_address())); |
+ __ ldr(r0, MemOperand(ip)); |
+ __ str(r3, MemOperand(ip)); |
+ |
+ __ bind(&continue_exception); |
+ // Special handling of out of memory exception. |
+ Failure* out_of_memory = Failure::OutOfMemoryException(); |
+ __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); |
+ __ b(eq, throw_out_of_memory_exception); |
+ |
+ // Handle normal exception. |
+ __ jmp(throw_normal_exception); |
+ |
+ __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying |
+} |
+ |
+ |
+void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { |
+ // Called from JavaScript; parameters are on stack as if calling JS function |
+ // r0: number of arguments including receiver |
+ // r1: pointer to builtin function |
+ // fp: frame pointer (restored after C call) |
+ // sp: stack pointer (restored as callee's pp after C call) |
+ // cp: current context (C callee-saved) |
+ // pp: caller's parameter pointer pp (C callee-saved) |
+ |
+ // NOTE: Invocations of builtins may return failure objects |
+ // instead of a proper result. The builtin entry handles |
+ // this by performing a garbage collection and retrying the |
+ // builtin once. |
+ |
+ StackFrame::Type frame_type = is_debug_break |
+ ? StackFrame::EXIT_DEBUG |
+ : StackFrame::EXIT; |
+ |
+ // Enter the exit frame that transitions from JavaScript to C++. |
+ __ EnterExitFrame(frame_type); |
+ |
+ // r4: number of arguments (C callee-saved) |
+ // r5: pointer to builtin function (C callee-saved) |
+ // r6: pointer to first argument (C callee-saved) |
+ |
+ Label throw_out_of_memory_exception; |
+ Label throw_normal_exception; |
+ |
+#ifdef DEBUG |
+ if (FLAG_gc_greedy) { |
+ Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE); |
+ __ mov(r0, Operand(reinterpret_cast<intptr_t>(failure))); |
+ } |
+ GenerateCore(masm, |
+ &throw_normal_exception, |
+ &throw_out_of_memory_exception, |
+ frame_type, |
+ FLAG_gc_greedy); |
+#else |
+ GenerateCore(masm, |
+ &throw_normal_exception, |
+ &throw_out_of_memory_exception, |
+ frame_type, |
+ false); |
+#endif |
+ GenerateCore(masm, |
+ &throw_normal_exception, |
+ &throw_out_of_memory_exception, |
+ frame_type, |
+ true); |
+ |
+ __ bind(&throw_out_of_memory_exception); |
+ GenerateThrowOutOfMemory(masm); |
+ // control flow for generated will not return. |
+ |
+ __ bind(&throw_normal_exception); |
+ GenerateThrowTOS(masm); |
+} |
+ |
+ |
+void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { |
+ // r0: code entry |
+ // r1: function |
+ // r2: receiver |
+ // r3: argc |
+ // [sp+0]: argv |
+ |
+ Label invoke, exit; |
+ |
+ // Called from C, so do not pop argc and args on exit (preserve sp) |
+ // No need to save register-passed args |
+ // Save callee-saved registers (incl. cp, pp, and fp), sp, and lr |
+ __ stm(db_w, sp, kCalleeSaved | lr.bit()); |
+ |
+ // Get address of argv, see stm above. |
+ // r0: code entry |
+ // r1: function |
+ // r2: receiver |
+ // r3: argc |
+ __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize)); |
+ __ ldr(r4, MemOperand(r4)); // argv |
+ |
+ // Push a frame with special values setup to mark it as an entry frame. |
+ // r0: code entry |
+ // r1: function |
+ // r2: receiver |
+ // r3: argc |
+ // r4: argv |
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; |
+ __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used. |
+ __ mov(r7, Operand(~ArgumentsAdaptorFrame::SENTINEL)); |
+ __ mov(r6, Operand(Smi::FromInt(marker))); |
+ __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address))); |
+ __ ldr(r5, MemOperand(r5)); |
+ __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit()); |
+ |
+ // Setup frame pointer for the frame to be pushed. |
+ __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); |
+ |
+ // Call a faked try-block that does the invoke. |
+ __ bl(&invoke); |
+ |
+ // Caught exception: Store result (exception) in the pending |
+ // exception field in the JSEnv and return a failure sentinel. |
+ // Coming in here the fp will be invalid because the PushTryHandler below |
+ // sets it to 0 to signal the existence of the JSEntry frame. |
+ __ mov(ip, Operand(Top::pending_exception_address())); |
+ __ str(r0, MemOperand(ip)); |
+ __ mov(r0, Operand(Handle<Failure>(Failure::Exception()))); |
+ __ b(&exit); |
+ |
+ // Invoke: Link this frame into the handler chain. |
+ __ bind(&invoke); |
+ // Must preserve r0-r4, r5-r7 are available. |
+ __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); |
+ // If an exception not caught by another handler occurs, this handler returns |
+ // control to the code after the bl(&invoke) above, which restores all |
+ // kCalleeSaved registers (including cp, pp and fp) to their saved values |
+ // before returning a failure to C. |
+ |
+ // Clear any pending exceptions. |
+ __ mov(ip, Operand(ExternalReference::the_hole_value_location())); |
+ __ ldr(r5, MemOperand(ip)); |
+ __ mov(ip, Operand(Top::pending_exception_address())); |
+ __ str(r5, MemOperand(ip)); |
+ |
+ // Invoke the function by calling through JS entry trampoline builtin. |
+ // Notice that we cannot store a reference to the trampoline code directly in |
+ // this stub, because runtime stubs are not traversed when doing GC. |
+ |
+ // Expected registers by Builtins::JSEntryTrampoline |
+ // r0: code entry |
+ // r1: function |
+ // r2: receiver |
+ // r3: argc |
+ // r4: argv |
+ if (is_construct) { |
+ ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline); |
+ __ mov(ip, Operand(construct_entry)); |
+ } else { |
+ ExternalReference entry(Builtins::JSEntryTrampoline); |
+ __ mov(ip, Operand(entry)); |
+ } |
+ __ ldr(ip, MemOperand(ip)); // deref address |
+ |
+ // Branch and link to JSEntryTrampoline |
+ __ mov(lr, Operand(pc)); |
+ __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); |
+ |
+ // Unlink this frame from the handler chain. When reading the |
+ // address of the next handler, there is no need to use the address |
+ // displacement since the current stack pointer (sp) points directly |
+ // to the stack handler. |
+ __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset)); |
+ __ mov(ip, Operand(ExternalReference(Top::k_handler_address))); |
+ __ str(r3, MemOperand(ip)); |
+ // No need to restore registers |
+ __ add(sp, sp, Operand(StackHandlerConstants::kSize)); |
+ |
+ __ bind(&exit); // r0 holds result |
+ // Restore the top frame descriptors from the stack. |
+ __ pop(r3); |
+ __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); |
+ __ str(r3, MemOperand(ip)); |
+ |
+ // Reset the stack to the callee saved registers. |
+ __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); |
+ |
+ // Restore callee-saved registers and return. |
+#ifdef DEBUG |
+ if (FLAG_debug_code) __ mov(lr, Operand(pc)); |
+#endif |
+ __ ldm(ia_w, sp, kCalleeSaved | pc.bit()); |
+} |
+ |
+ |
+void ArgumentsAccessStub::Generate(MacroAssembler* masm) { |
+ // ----------- S t a t e ------------- |
+ // -- r0: formal number of parameters for the calling function |
+ // -- r1: key (if value access) |
+ // -- lr: return address |
+ // ----------------------------------- |
+ |
+ // Check that the key is a smi for non-length accesses. |
+ Label slow; |
+ if (!is_length_) { |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(ne, &slow); |
+ } |
+ |
+ // Check if the calling frame is an arguments adaptor frame. |
+ // r0: formal number of parameters |
+ // r1: key (if access) |
+ Label adaptor; |
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
+ __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); |
+ __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL)); |
+ __ b(eq, &adaptor); |
+ |
+ static const int kParamDisplacement = |
+ StandardFrameConstants::kCallerSPOffset - kPointerSize; |
+ |
+ if (is_length_) { |
+ // Nothing to do: the formal length of parameters has been passed in r0 |
+ // by the calling function. |
+ } else { |
+ // Check index against formal parameter count. Use unsigned comparison to |
+ // get the negative check for free. |
+ // r0: formal number of parameters |
+ // r1: index |
+ __ cmp(r1, r0); |
+ __ b(cs, &slow); |
+ |
+ // Read the argument from the current frame. |
+ __ sub(r3, r0, r1); |
+ __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); |
+ __ ldr(r0, MemOperand(r3, kParamDisplacement)); |
+ } |
+ |
+ // Return to the calling function. |
+ __ mov(pc, lr); |
+ |
+ // An arguments adaptor frame is present. Find the length or the actual |
+ // argument in the calling frame. |
+ // r0: formal number of parameters |
+ // r1: key |
+ // r2: adaptor frame pointer |
+ __ bind(&adaptor); |
+ // Read the arguments length from the adaptor frame. This is the result if |
+ // only accessing the length, otherwise it is used in accessing the value |
+ __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
+ |
+ if (!is_length_) { |
+ // Check index against actual arguments count. Use unsigned comparison to |
+ // get the negative check for free. |
+ // r0: actual number of parameter |
+ // r1: index |
+ // r2: adaptor frame point |
+ __ cmp(r1, r0); |
+ __ b(cs, &slow); |
+ |
+ // Read the argument from the adaptor frame. |
+ __ sub(r3, r0, r1); |
+ __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); |
+ __ ldr(r0, MemOperand(r3, kParamDisplacement)); |
+ } |
+ |
+ // Return to the calling function. |
+ __ mov(pc, lr); |
+ |
+ if (!is_length_) { |
+ __ bind(&slow); |
+ __ push(r1); |
+ __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); |
+ } |
+} |
+ |
+ |
+void ArmCodeGenerator::SetReferenceProperty(CodeGenerator* cgen, |
+ Reference* ref, |
+ Expression* key) { |
+ ASSERT(!ref->is_illegal()); |
+ MacroAssembler* masm = cgen->masm(); |
+ |
+ if (ref->type() == Reference::NAMED) { |
+ // Compute the name of the property. |
+ Literal* literal = key->AsLiteral(); |
+ Handle<String> name(String::cast(*literal->handle())); |
+ |
+ // Call the appropriate IC code. |
+ __ pop(r0); // value |
+ // Setup the name register. |
+ __ mov(r2, Operand(name)); |
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
+ __ Call(ic, RelocInfo::CODE_TARGET); |
+ |
+ } else { |
+ // Access keyed property. |
+ ASSERT(ref->type() == Reference::KEYED); |
+ |
+ __ pop(r0); // value |
+ SetPropertyStub stub; |
+ __ CallStub(&stub); |
+ } |
+ __ push(r0); |
+} |
+ |
+ |
+void CallFunctionStub::Generate(MacroAssembler* masm) { |
+ Label slow; |
+ // Get the function to call from the stack. |
+ // function, receiver [, arguments] |
+ __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize)); |
+ |
+ // Check that the function is really a JavaScript function. |
+ // r1: pushed function (to be verified) |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(eq, &slow); |
+ // Get the map of the function object. |
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
+ __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
+ __ cmp(r2, Operand(JS_FUNCTION_TYPE)); |
+ __ b(ne, &slow); |
+ |
+ // Fast-case: Invoke the function now. |
+ // r1: pushed function |
+ ParameterCount actual(argc_); |
+ __ InvokeFunction(r1, actual, JUMP_FUNCTION); |
+ |
+ // Slow-case: Non-function called. |
+ __ bind(&slow); |
+ __ mov(r0, Operand(argc_)); // Setup the number of arguments. |
+ __ InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS); |
+} |
+ |
+ |
+#undef __ |
+ |
// ----------------------------------------------------------------------------- |
// CodeGenerator interface |