blob: 800723b8265f909c51f2292adc0809fec0565618 [file] [log] [blame]
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001// Copyright 2010 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000030#if defined(V8_TARGET_ARCH_ARM)
31
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000032#include "bootstrapper.h"
33#include "codegen-inl.h"
ager@chromium.orgc4c92722009-11-18 14:12:51 +000034#include "compiler.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035#include "debug.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000036#include "ic-inl.h"
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000037#include "jsregexp.h"
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000038#include "jump-target-light-inl.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000039#include "parser.h"
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000040#include "regexp-macro-assembler.h"
41#include "regexp-stack.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000042#include "register-allocator-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043#include "runtime.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000044#include "scopes.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000045#include "virtual-frame-inl.h"
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000046#include "virtual-frame-arm-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000047
kasperl@chromium.org71affb52009-05-26 05:44:31 +000048namespace v8 {
49namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +000051
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000052static void EmitIdenticalObjectComparison(MacroAssembler* masm,
53 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +000054 Condition cc,
55 bool never_nan_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000056static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000057 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000058 Label* slow,
59 bool strict);
60static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc);
61static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +000062static void MultiplyByKnownInt(MacroAssembler* masm,
63 Register source,
64 Register destination,
65 int known_int);
66static bool IsEasyToMultiplyBy(int x);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000067
68
ricow@chromium.org30ce4112010-05-31 10:38:25 +000069#define __ ACCESS_MASM(masm_)
kasperl@chromium.org2abc4502009-07-02 07:00:29 +000070
ager@chromium.orge2902be2009-06-08 12:21:35 +000071// -------------------------------------------------------------------------
72// Platform-specific DeferredCode functions.
73
74void DeferredCode::SaveRegisters() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000075 // On ARM you either have a completely spilled frame or you
76 // handle it yourself, but at the moment there's no automation
77 // of registers and deferred code.
ager@chromium.orge2902be2009-06-08 12:21:35 +000078}
79
80
81void DeferredCode::RestoreRegisters() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +000082}
83
84
85// -------------------------------------------------------------------------
86// Platform-specific RuntimeCallHelper functions.
87
88void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
89 frame_state_->frame()->AssertIsSpilled();
90}
91
92
93void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
94}
95
96
97void ICRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
98 masm->EnterInternalFrame();
99}
100
101
102void ICRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
103 masm->LeaveInternalFrame();
ager@chromium.orge2902be2009-06-08 12:21:35 +0000104}
105
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000106
107// -------------------------------------------------------------------------
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000108// CodeGenState implementation.
109
ager@chromium.org7c537e22008-10-16 08:43:32 +0000110CodeGenState::CodeGenState(CodeGenerator* owner)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000111 : owner_(owner),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000112 true_target_(NULL),
113 false_target_(NULL),
114 previous_(NULL) {
115 owner_->set_state(this);
116}
117
118
ager@chromium.org7c537e22008-10-16 08:43:32 +0000119CodeGenState::CodeGenState(CodeGenerator* owner,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000120 JumpTarget* true_target,
121 JumpTarget* false_target)
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000122 : owner_(owner),
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000123 true_target_(true_target),
124 false_target_(false_target),
125 previous_(owner->state()) {
126 owner_->set_state(this);
127}
128
129
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000130CodeGenState::~CodeGenState() {
131 ASSERT(owner_->state() == this);
132 owner_->set_state(previous_);
133}
134
135
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000136// -------------------------------------------------------------------------
ager@chromium.org7c537e22008-10-16 08:43:32 +0000137// CodeGenerator implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000138
ager@chromium.org5c838252010-02-19 08:53:10 +0000139CodeGenerator::CodeGenerator(MacroAssembler* masm)
140 : deferred_(8),
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000141 masm_(masm),
ager@chromium.org5c838252010-02-19 08:53:10 +0000142 info_(NULL),
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000143 frame_(NULL),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000144 allocator_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000145 cc_reg_(al),
146 state_(NULL),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000147 loop_nesting_(0),
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000148 function_return_is_shadowed_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000149}
150
151
152// Calling conventions:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000153// fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000154// sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000155// r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000156// cp: callee's context
157
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000158void CodeGenerator::Generate(CompilationInfo* info) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000159 // Record the position for debugging purposes.
ager@chromium.org5c838252010-02-19 08:53:10 +0000160 CodeForFunctionPosition(info->function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000161 Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000162
163 // Initialize state.
ager@chromium.org5c838252010-02-19 08:53:10 +0000164 info_ = info;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000165 ASSERT(allocator_ == NULL);
166 RegisterAllocator register_allocator(this);
167 allocator_ = &register_allocator;
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000168 ASSERT(frame_ == NULL);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000169 frame_ = new VirtualFrame();
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000170 cc_reg_ = al;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000171
172 // Adjust for function-level loop nesting.
173 ASSERT_EQ(0, loop_nesting_);
174 loop_nesting_ = info->loop_nesting();
175
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000176 {
177 CodeGenState state(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000178
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000179 // Entry:
180 // Stack: receiver, arguments
181 // lr: return address
182 // fp: caller's frame pointer
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000183 // sp: stack pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000184 // r1: called JS function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000185 // cp: callee's context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000186 allocator_->Initialize();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000187
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000188#ifdef DEBUG
189 if (strlen(FLAG_stop_at) > 0 &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000190 info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000191 frame_->SpillAll();
kasper.lund7276f142008-07-30 08:49:36 +0000192 __ stop("stop-at");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000193 }
194#endif
195
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000196 if (info->mode() == CompilationInfo::PRIMARY) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000197 frame_->Enter();
198 // tos: code slot
199
200 // Allocate space for locals and initialize them. This also checks
201 // for stack overflow.
202 frame_->AllocateStackSlots();
203
ager@chromium.org357bf652010-04-12 11:30:10 +0000204 VirtualFrame::SpilledScope spilled_scope(frame_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000205 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000206 if (heap_slots > 0) {
207 // Allocate local context.
208 // Get outer context and create a new context based on it.
209 __ ldr(r0, frame_->Function());
210 frame_->EmitPush(r0);
211 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
212 FastNewContextStub stub(heap_slots);
213 frame_->CallStub(&stub, 1);
214 } else {
215 frame_->CallRuntime(Runtime::kNewContext, 1);
216 }
217
218#ifdef DEBUG
219 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000220 __ cmp(r0, cp);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000221 verified_true.Branch(eq);
222 __ stop("NewContext: r0 is expected to be the same as cp");
223 verified_true.Bind();
224#endif
225 // Update context local.
226 __ str(cp, frame_->Context());
227 }
228
229 // TODO(1241774): Improve this code:
230 // 1) only needed if we have a context
231 // 2) no need to recompute context ptr every single time
232 // 3) don't copy parameter operand code from SlotOperand!
233 {
234 Comment cmnt2(masm_, "[ copy context parameters into .context");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000235 // Note that iteration order is relevant here! If we have the same
236 // parameter twice (e.g., function (x, y, x)), and that parameter
237 // needs to be copied into the context, it must be the last argument
238 // passed to the parameter that needs to be copied. This is a rare
239 // case so we don't check for it, instead we rely on the copying
240 // order: such a parameter is copied repeatedly into the same
241 // context location and thus the last value is what is seen inside
242 // the function.
ager@chromium.org5c838252010-02-19 08:53:10 +0000243 for (int i = 0; i < scope()->num_parameters(); i++) {
244 Variable* par = scope()->parameter(i);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000245 Slot* slot = par->slot();
246 if (slot != NULL && slot->type() == Slot::CONTEXT) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000247 ASSERT(!scope()->is_global_scope()); // No params in global scope.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000248 __ ldr(r1, frame_->ParameterAt(i));
249 // Loads r2 with context; used below in RecordWrite.
250 __ str(r1, SlotOperand(slot, r2));
251 // Load the offset into r3.
252 int slot_offset =
253 FixedArray::kHeaderSize + slot->index() * kPointerSize;
254 __ mov(r3, Operand(slot_offset));
255 __ RecordWrite(r2, r3, r1);
256 }
257 }
258 }
259
260 // Store the arguments object. This must happen after context
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000261 // initialization because the arguments object may be stored in
262 // the context.
263 if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
264 StoreArgumentsObject(true);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000265 }
266
267 // Initialize ThisFunction reference if present.
ager@chromium.org5c838252010-02-19 08:53:10 +0000268 if (scope()->is_function_scope() && scope()->function() != NULL) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000269 __ mov(ip, Operand(Factory::the_hole_value()));
270 frame_->EmitPush(ip);
ager@chromium.org5c838252010-02-19 08:53:10 +0000271 StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000272 }
273 } else {
274 // When used as the secondary compiler for splitting, r1, cp,
275 // fp, and lr have been pushed on the stack. Adjust the virtual
276 // frame to match this state.
277 frame_->Adjust(4);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +0000278
279 // Bind all the bailout labels to the beginning of the function.
280 List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
281 for (int i = 0; i < bailouts->length(); i++) {
282 __ bind(bailouts->at(i)->label());
283 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000284 }
285
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000286 // Initialize the function return target after the locals are set
287 // up, because it needs the expected frame height from the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +0000288 function_return_.SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000289 function_return_is_shadowed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000291 // Generate code to 'execute' declarations and initialize functions
292 // (source elements). In case of an illegal redeclaration we need to
293 // handle that instead of processing the declarations.
ager@chromium.org5c838252010-02-19 08:53:10 +0000294 if (scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 Comment cmnt(masm_, "[ illegal redeclarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000296 scope()->VisitIllegalRedeclaration(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 } else {
298 Comment cmnt(masm_, "[ declarations");
ager@chromium.org5c838252010-02-19 08:53:10 +0000299 ProcessDeclarations(scope()->declarations());
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000300 // Bail out if a stack-overflow exception occurred when processing
301 // declarations.
kasper.lund212ac232008-07-16 07:07:30 +0000302 if (HasStackOverflow()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000303 }
304
mads.s.ager31e71382008-08-13 09:32:07 +0000305 if (FLAG_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000306 frame_->CallRuntime(Runtime::kTraceEnter, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000307 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000308 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309
310 // Compile the body of the function in a vanilla state. Don't
311 // bother compiling all the code if the scope has an illegal
312 // redeclaration.
ager@chromium.org5c838252010-02-19 08:53:10 +0000313 if (!scope()->HasIllegalRedeclaration()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 Comment cmnt(masm_, "[ function body");
315#ifdef DEBUG
316 bool is_builtin = Bootstrapper::IsActive();
317 bool should_trace =
318 is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
mads.s.ager31e71382008-08-13 09:32:07 +0000319 if (should_trace) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000320 frame_->CallRuntime(Runtime::kDebugTrace, 0);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000321 // Ignore the return value.
mads.s.ager31e71382008-08-13 09:32:07 +0000322 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000323#endif
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000324 VisitStatements(info->function()->body());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000325 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000326 }
327
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000328 // Generate the return sequence if necessary.
ager@chromium.org4af710e2009-09-15 12:20:11 +0000329 if (has_valid_frame() || function_return_.is_linked()) {
330 if (!function_return_.is_linked()) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000331 CodeForReturnPosition(info->function());
ager@chromium.org4af710e2009-09-15 12:20:11 +0000332 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000333 // exit
334 // r0: result
335 // sp: stack pointer
336 // fp: frame pointer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000337 // cp: callee's context
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000338 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +0000339
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000340 function_return_.Bind();
341 if (FLAG_trace) {
342 // Push the return value on the stack as the parameter.
343 // Runtime::TraceExit returns the parameter as it is.
344 frame_->EmitPush(r0);
345 frame_->CallRuntime(Runtime::kTraceExit, 1);
346 }
347
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000348#ifdef DEBUG
ager@chromium.org4af710e2009-09-15 12:20:11 +0000349 // Add a label for checking the size of the code used for returning.
350 Label check_exit_codesize;
351 masm_->bind(&check_exit_codesize);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000352#endif
353 // Make sure that the constant pool is not emitted inside of the return
354 // sequence.
355 { Assembler::BlockConstPoolScope block_const_pool(masm_);
356 // Tear down the frame which will restore the caller's frame pointer and
357 // the link register.
358 frame_->Exit();
ager@chromium.org4af710e2009-09-15 12:20:11 +0000359
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000360 // Here we use masm_-> instead of the __ macro to avoid the code coverage
361 // tool from instrumenting as we rely on the code size here.
362 int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
363 masm_->add(sp, sp, Operand(sp_delta));
364 masm_->Jump(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000365
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000366#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000367 // Check that the size of the code used for returning matches what is
368 // expected by the debugger. If the sp_delts above cannot be encoded in
369 // the add instruction the add will generate two instructions.
370 int return_sequence_length =
371 masm_->InstructionsGeneratedSince(&check_exit_codesize);
372 CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength ||
373 return_sequence_length == Assembler::kJSReturnSequenceLength + 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000374#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000375 }
mads.s.ager31e71382008-08-13 09:32:07 +0000376 }
377
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000378 // Adjust for function-level loop nesting.
379 ASSERT(loop_nesting_ == info->loop_nesting());
380 loop_nesting_ = 0;
381
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000382 // Code generation state must be reset.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000383 ASSERT(!has_cc());
384 ASSERT(state_ == NULL);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000385 ASSERT(loop_nesting() == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000386 ASSERT(!function_return_is_shadowed_);
387 function_return_.Unuse();
388 DeleteFrame();
389
390 // Process any deferred code using the register allocator.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000391 if (!HasStackOverflow()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000392 ProcessDeferred();
393 }
394
395 allocator_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396}
397
398
ager@chromium.org7c537e22008-10-16 08:43:32 +0000399MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
400 // Currently, this assertion will fail if we try to assign to
401 // a constant variable that is constant because it is read-only
402 // (such as the variable referring to a named function expression).
403 // We need to implement assignments to read-only variables.
404 // Ideally, we should do this during AST generation (by converting
405 // such assignments into expression statements); however, in general
406 // we may not be able to make the decision until past AST generation,
407 // that is when the entire program is known.
408 ASSERT(slot != NULL);
409 int index = slot->index();
410 switch (slot->type()) {
411 case Slot::PARAMETER:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000412 return frame_->ParameterAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000413
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000414 case Slot::LOCAL:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000415 return frame_->LocalAt(index);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000416
417 case Slot::CONTEXT: {
418 // Follow the context chain if necessary.
419 ASSERT(!tmp.is(cp)); // do not overwrite context register
420 Register context = cp;
421 int chain_length = scope()->ContextChainLength(slot->var()->scope());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000422 for (int i = 0; i < chain_length; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000423 // Load the closure.
424 // (All contexts, even 'with' contexts, have a closure,
425 // and it is the same for all contexts inside a function.
426 // There is no need to go to the function context first.)
427 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
428 // Load the function context (which is the incoming, outer context).
429 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
430 context = tmp;
431 }
432 // We may have a 'with' context now. Get the function context.
433 // (In fact this mov may never be the needed, since the scope analysis
434 // may not permit a direct context access in this case and thus we are
435 // always at a function context. However it is safe to dereference be-
436 // cause the function context of a function context is itself. Before
437 // deleting this mov we should try to create a counter-example first,
438 // though...)
439 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
440 return ContextOperand(tmp, index);
441 }
442
443 default:
444 UNREACHABLE();
445 return MemOperand(r0, 0);
446 }
447}
448
449
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000450MemOperand CodeGenerator::ContextSlotOperandCheckExtensions(
451 Slot* slot,
452 Register tmp,
453 Register tmp2,
454 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +0000455 ASSERT(slot->type() == Slot::CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000456 Register context = cp;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000457
ager@chromium.org381abbb2009-02-25 13:23:22 +0000458 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
459 if (s->num_heap_slots() > 0) {
460 if (s->calls_eval()) {
461 // Check that extension is NULL.
462 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
463 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000464 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000465 }
466 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
467 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
468 context = tmp;
469 }
470 }
471 // Check that last extension is NULL.
472 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
473 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000474 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +0000475 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000476 return ContextOperand(tmp, slot->index());
ager@chromium.org381abbb2009-02-25 13:23:22 +0000477}
478
479
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000480// Loads a value on TOS. If it is a boolean value, the result may have been
481// (partially) translated into branches, or it may have set the condition
482// code register. If force_cc is set, the value is forced to set the
483// condition code register and no value is pushed. If the condition code
484// register was set, has_cc() is true and cc_reg_ contains the condition to
485// test for 'true'.
ager@chromium.org7c537e22008-10-16 08:43:32 +0000486void CodeGenerator::LoadCondition(Expression* x,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000487 JumpTarget* true_target,
488 JumpTarget* false_target,
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000489 bool force_cc) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000490 ASSERT(!has_cc());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000491 int original_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000492
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000493 { CodeGenState new_state(this, true_target, false_target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000494 Visit(x);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000495
496 // If we hit a stack overflow, we may not have actually visited
497 // the expression. In that case, we ensure that we have a
498 // valid-looking frame state because we will continue to generate
499 // code as we unwind the C++ stack.
500 //
501 // It's possible to have both a stack overflow and a valid frame
502 // state (eg, a subexpression overflowed, visiting it returned
503 // with a dummied frame state, and visiting this expression
504 // returned with a normal-looking state).
505 if (HasStackOverflow() &&
506 has_valid_frame() &&
507 !has_cc() &&
508 frame_->height() == original_height) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000509 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000510 true_target->Jump();
511 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000512 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000513 if (force_cc && frame_ != NULL && !has_cc()) {
mads.s.ager31e71382008-08-13 09:32:07 +0000514 // Convert the TOS value to a boolean in the condition code register.
515 ToBoolean(true_target, false_target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000517 ASSERT(!force_cc || !has_valid_frame() || has_cc());
518 ASSERT(!has_valid_frame() ||
519 (has_cc() && frame_->height() == original_height) ||
520 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000521}
522
523
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000524void CodeGenerator::Load(Expression* expr) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000525#ifdef DEBUG
526 int original_height = frame_->height();
527#endif
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000528 JumpTarget true_target;
529 JumpTarget false_target;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000530 LoadCondition(expr, &true_target, &false_target, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000531
532 if (has_cc()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000533 // Convert cc_reg_ into a boolean value.
ager@chromium.org357bf652010-04-12 11:30:10 +0000534 VirtualFrame::SpilledScope scope(frame_);
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000535 JumpTarget loaded;
536 JumpTarget materialize_true;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000537 materialize_true.Branch(cc_reg_);
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000538 __ LoadRoot(r0, Heap::kFalseValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000539 frame_->EmitPush(r0);
540 loaded.Jump();
541 materialize_true.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000542 __ LoadRoot(r0, Heap::kTrueValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000543 frame_->EmitPush(r0);
544 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000545 cc_reg_ = al;
546 }
547
548 if (true_target.is_linked() || false_target.is_linked()) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000549 VirtualFrame::SpilledScope scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000550 // We have at least one condition value that has been "translated"
551 // into a branch, thus it needs to be loaded explicitly.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000552 JumpTarget loaded;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000553 if (frame_ != NULL) {
554 loaded.Jump(); // Don't lose the current TOS.
555 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000556 bool both = true_target.is_linked() && false_target.is_linked();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000557 // Load "true" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 if (true_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000559 true_target.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000560 __ LoadRoot(r0, Heap::kTrueValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000561 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000563 // If both "true" and "false" need to be loaded jump across the code for
564 // "false".
565 if (both) {
566 loaded.Jump();
567 }
568 // Load "false" if necessary.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000569 if (false_target.is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000570 false_target.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000571 __ LoadRoot(r0, Heap::kFalseValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000572 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000573 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000574 // A value is loaded on all paths reaching this point.
575 loaded.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000576 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000577 ASSERT(has_valid_frame());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 ASSERT(!has_cc());
ager@chromium.orgac091b72010-05-05 07:34:42 +0000579 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000580}
581
582
ager@chromium.org7c537e22008-10-16 08:43:32 +0000583void CodeGenerator::LoadGlobal() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000584 Register reg = frame_->GetTOSRegister();
585 __ ldr(reg, GlobalObject());
586 frame_->EmitPush(reg);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000587}
588
589
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000590void CodeGenerator::LoadGlobalReceiver(Register scratch) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000591 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000592 __ ldr(scratch, ContextOperand(cp, Context::GLOBAL_INDEX));
593 __ ldr(scratch,
594 FieldMemOperand(scratch, GlobalObject::kGlobalReceiverOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000595 frame_->EmitPush(scratch);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000596}
597
598
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000599ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
600 if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
601 ASSERT(scope()->arguments_shadow() != NULL);
602 // We don't want to do lazy arguments allocation for functions that
603 // have heap-allocated contexts, because it interfers with the
604 // uninitialized const tracking in the context objects.
605 return (scope()->num_heap_slots() > 0)
606 ? EAGER_ARGUMENTS_ALLOCATION
607 : LAZY_ARGUMENTS_ALLOCATION;
608}
609
610
611void CodeGenerator::StoreArgumentsObject(bool initial) {
612 VirtualFrame::SpilledScope spilled_scope(frame_);
613
614 ArgumentsAllocationMode mode = ArgumentsMode();
615 ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
616
617 Comment cmnt(masm_, "[ store arguments object");
618 if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
619 // When using lazy arguments allocation, we store the hole value
620 // as a sentinel indicating that the arguments object hasn't been
621 // allocated yet.
622 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
623 frame_->EmitPush(ip);
624 } else {
625 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
626 __ ldr(r2, frame_->Function());
627 // The receiver is below the arguments, the return address, and the
628 // frame pointer on the stack.
629 const int kReceiverDisplacement = 2 + scope()->num_parameters();
630 __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
631 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
632 frame_->Adjust(3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000633 __ Push(r2, r1, r0);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000634 frame_->CallStub(&stub, 3);
635 frame_->EmitPush(r0);
636 }
637
638 Variable* arguments = scope()->arguments()->var();
639 Variable* shadow = scope()->arguments_shadow()->var();
640 ASSERT(arguments != NULL && arguments->slot() != NULL);
641 ASSERT(shadow != NULL && shadow->slot() != NULL);
642 JumpTarget done;
643 if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
644 // We have to skip storing into the arguments slot if it has
645 // already been written to. This can happen if the a function
646 // has a local variable named 'arguments'.
647 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
648 frame_->EmitPop(r0);
649 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
650 __ cmp(r0, ip);
651 done.Branch(ne);
652 }
653 StoreToSlot(arguments->slot(), NOT_CONST_INIT);
654 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
655 StoreToSlot(shadow->slot(), NOT_CONST_INIT);
656}
657
658
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000659void CodeGenerator::LoadTypeofExpression(Expression* expr) {
660 // Special handling of identifiers as subexpressions of typeof.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000661 Variable* variable = expr->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000662 if (variable != NULL && !variable->is_this() && variable->is_global()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000663 // For a global variable we build the property reference
664 // <global>.<variable> and perform a (regular non-contextual) property
665 // load to make sure we do not get reference errors.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000666 Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
667 Literal key(variable->name());
ager@chromium.org236ad962008-09-25 09:45:57 +0000668 Property property(&global, &key, RelocInfo::kNoPosition);
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000669 Reference ref(this, &property);
ager@chromium.org357bf652010-04-12 11:30:10 +0000670 ref.GetValue();
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000671 } else if (variable != NULL && variable->slot() != NULL) {
672 // For a variable that rewrites to a slot, we signal it is the immediate
673 // subexpression of a typeof.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +0000674 LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000675 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000676 // Anything else can be handled normally.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000677 Load(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000678 }
679}
680
681
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000682Reference::Reference(CodeGenerator* cgen,
683 Expression* expression,
684 bool persist_after_get)
685 : cgen_(cgen),
686 expression_(expression),
687 type_(ILLEGAL),
688 persist_after_get_(persist_after_get) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689 cgen->LoadReference(this);
690}
691
692
693Reference::~Reference() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000694 ASSERT(is_unloaded() || is_illegal());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695}
696
697
ager@chromium.org7c537e22008-10-16 08:43:32 +0000698void CodeGenerator::LoadReference(Reference* ref) {
699 Comment cmnt(masm_, "[ LoadReference");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000700 Expression* e = ref->expression();
701 Property* property = e->AsProperty();
702 Variable* var = e->AsVariableProxy()->AsVariable();
703
704 if (property != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000705 // The expression is either a property or a variable proxy that rewrites
706 // to a property.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000707 Load(property->obj());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +0000708 if (property->key()->IsPropertyName()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000709 ref->set_type(Reference::NAMED);
710 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000711 Load(property->key());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000712 ref->set_type(Reference::KEYED);
713 }
714 } else if (var != NULL) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000715 // The expression is a variable proxy that does not rewrite to a
716 // property. Global variables are treated as named property references.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000717 if (var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000718 LoadGlobal();
719 ref->set_type(Reference::NAMED);
720 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000721 ASSERT(var->slot() != NULL);
722 ref->set_type(Reference::SLOT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000723 }
724 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000725 // Anything else is a runtime error.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000726 Load(e);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000727 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 }
729}
730
731
ager@chromium.org7c537e22008-10-16 08:43:32 +0000732void CodeGenerator::UnloadReference(Reference* ref) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733 int size = ref->size();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000734 ref->set_unloaded();
ager@chromium.org357bf652010-04-12 11:30:10 +0000735 if (size == 0) return;
736
737 // Pop a reference from the stack while preserving TOS.
738 VirtualFrame::RegisterAllocationScope scope(this);
739 Comment cmnt(masm_, "[ UnloadReference");
740 if (size > 0) {
741 Register tos = frame_->PopToRegister();
742 frame_->Drop(size);
743 frame_->EmitPush(tos);
744 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000745}
746
747
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000748// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
749// register to a boolean in the condition code register. The code
750// may jump to 'false_target' in case the register converts to 'false'.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000751void CodeGenerator::ToBoolean(JumpTarget* true_target,
752 JumpTarget* false_target) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000753 VirtualFrame::SpilledScope spilled_scope(frame_);
mads.s.ager31e71382008-08-13 09:32:07 +0000754 // Note: The generated code snippet does not change stack variables.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000755 // Only the condition code should be set.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000756 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000757
758 // Fast case checks
759
mads.s.ager31e71382008-08-13 09:32:07 +0000760 // Check if the value is 'false'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000761 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
762 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000763 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000764
mads.s.ager31e71382008-08-13 09:32:07 +0000765 // Check if the value is 'true'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000766 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
767 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000768 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769
mads.s.ager31e71382008-08-13 09:32:07 +0000770 // Check if the value is 'undefined'.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000771 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
772 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000773 false_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000774
mads.s.ager31e71382008-08-13 09:32:07 +0000775 // Check if the value is a smi.
776 __ cmp(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000777 false_target->Branch(eq);
mads.s.ager31e71382008-08-13 09:32:07 +0000778 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000779 true_target->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000780
781 // Slow case: call the runtime.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000782 frame_->EmitPush(r0);
783 frame_->CallRuntime(Runtime::kToBool, 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000784 // Convert the result (r0) to a condition code.
ager@chromium.orgab99eea2009-08-25 07:05:41 +0000785 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
786 __ cmp(r0, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000787
788 cc_reg_ = ne;
789}
790
791
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000792void CodeGenerator::GenericBinaryOperation(Token::Value op,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000793 OverwriteMode overwrite_mode,
794 int constant_rhs) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000795 VirtualFrame::SpilledScope spilled_scope(frame_);
mads.s.ager31e71382008-08-13 09:32:07 +0000796 // sp[0] : y
797 // sp[1] : x
798 // result : r0
799
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 // Stub is entered with a call: 'return address' is in lr.
801 switch (op) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000802 case Token::ADD:
803 case Token::SUB:
804 case Token::MUL:
805 case Token::DIV:
806 case Token::MOD:
807 case Token::BIT_OR:
808 case Token::BIT_AND:
809 case Token::BIT_XOR:
810 case Token::SHL:
811 case Token::SHR:
812 case Token::SAR: {
813 frame_->EmitPop(r0); // r0 : y
814 frame_->EmitPop(r1); // r1 : x
815 GenericBinaryOpStub stub(op, overwrite_mode, r1, r0, constant_rhs);
816 frame_->CallStub(&stub, 0);
817 break;
818 }
819
820 case Token::COMMA:
821 frame_->EmitPop(r0);
822 // Simply discard left value.
823 frame_->Drop();
824 break;
825
826 default:
827 // Other cases should have been handled before this point.
828 UNREACHABLE();
829 break;
830 }
831}
832
833
834void CodeGenerator::VirtualFrameBinaryOperation(Token::Value op,
835 OverwriteMode overwrite_mode,
836 int constant_rhs) {
837 // top of virtual frame: y
838 // 2nd elt. on virtual frame : x
839 // result : top of virtual frame
840
841 // Stub is entered with a call: 'return address' is in lr.
842 switch (op) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000843 case Token::ADD: // fall through.
844 case Token::SUB: // fall through.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000845 case Token::MUL:
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000846 case Token::DIV:
847 case Token::MOD:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000848 case Token::BIT_OR:
849 case Token::BIT_AND:
850 case Token::BIT_XOR:
851 case Token::SHL:
852 case Token::SHR:
853 case Token::SAR: {
ager@chromium.org357bf652010-04-12 11:30:10 +0000854 Register rhs = frame_->PopToRegister();
855 Register lhs = frame_->PopToRegister(rhs); // Don't pop to rhs register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000856 GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
857 frame_->SpillAll();
858 frame_->CallStub(&stub, 0);
ager@chromium.org357bf652010-04-12 11:30:10 +0000859 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860 break;
861 }
862
ager@chromium.org357bf652010-04-12 11:30:10 +0000863 case Token::COMMA: {
864 Register scratch = frame_->PopToRegister();
865 // Simply discard left value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000866 frame_->Drop();
ager@chromium.org357bf652010-04-12 11:30:10 +0000867 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868 break;
ager@chromium.org357bf652010-04-12 11:30:10 +0000869 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000870
871 default:
872 // Other cases should have been handled before this point.
873 UNREACHABLE();
874 break;
875 }
876}
877
878
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000879class DeferredInlineSmiOperation: public DeferredCode {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000880 public:
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000881 DeferredInlineSmiOperation(Token::Value op,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000882 int value,
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000883 bool reversed,
ager@chromium.org357bf652010-04-12 11:30:10 +0000884 OverwriteMode overwrite_mode,
885 Register tos)
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +0000886 : op_(op),
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000887 value_(value),
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000888 reversed_(reversed),
ager@chromium.org357bf652010-04-12 11:30:10 +0000889 overwrite_mode_(overwrite_mode),
890 tos_register_(tos) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000891 set_comment("[ DeferredInlinedSmiOperation");
892 }
893
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000894 virtual void Generate();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000895
896 private:
897 Token::Value op_;
898 int value_;
899 bool reversed_;
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000900 OverwriteMode overwrite_mode_;
ager@chromium.org357bf652010-04-12 11:30:10 +0000901 Register tos_register_;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000902};
903
904
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000905void DeferredInlineSmiOperation::Generate() {
ager@chromium.org357bf652010-04-12 11:30:10 +0000906 Register lhs = r1;
907 Register rhs = r0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000908 switch (op_) {
909 case Token::ADD: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000910 // Revert optimistic add.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000911 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000912 __ sub(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000913 __ mov(r1, Operand(Smi::FromInt(value_)));
914 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000915 __ sub(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000916 __ mov(r0, Operand(Smi::FromInt(value_)));
917 }
918 break;
919 }
920
921 case Token::SUB: {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000922 // Revert optimistic sub.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000923 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000924 __ rsb(r0, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000925 __ mov(r1, Operand(Smi::FromInt(value_)));
926 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000927 __ add(r1, tos_register_, Operand(Smi::FromInt(value_)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000928 __ mov(r0, Operand(Smi::FromInt(value_)));
929 }
930 break;
931 }
932
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000933 // For these operations there is no optimistic operation that needs to be
934 // reverted.
935 case Token::MUL:
936 case Token::MOD:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000937 case Token::BIT_OR:
938 case Token::BIT_XOR:
939 case Token::BIT_AND: {
940 if (reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000941 if (tos_register_.is(r0)) {
942 __ mov(r1, Operand(Smi::FromInt(value_)));
943 } else {
944 ASSERT(tos_register_.is(r1));
945 __ mov(r0, Operand(Smi::FromInt(value_)));
946 lhs = r0;
947 rhs = r1;
948 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000949 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +0000950 if (tos_register_.is(r1)) {
951 __ mov(r0, Operand(Smi::FromInt(value_)));
952 } else {
953 ASSERT(tos_register_.is(r0));
954 __ mov(r1, Operand(Smi::FromInt(value_)));
955 lhs = r0;
956 rhs = r1;
957 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000958 }
959 break;
960 }
961
962 case Token::SHL:
963 case Token::SHR:
964 case Token::SAR: {
965 if (!reversed_) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000966 if (tos_register_.is(r1)) {
967 __ mov(r0, Operand(Smi::FromInt(value_)));
968 } else {
969 ASSERT(tos_register_.is(r0));
970 __ mov(r1, Operand(Smi::FromInt(value_)));
971 lhs = r0;
972 rhs = r1;
973 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000974 } else {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000975 UNREACHABLE(); // Should have been handled in SmiOperation.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000976 }
977 break;
978 }
979
980 default:
ager@chromium.orge2902be2009-06-08 12:21:35 +0000981 // Other cases should have been handled before this point.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000982 UNREACHABLE();
983 break;
984 }
985
ager@chromium.org357bf652010-04-12 11:30:10 +0000986 GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_);
ager@chromium.orge2902be2009-06-08 12:21:35 +0000987 __ CallStub(&stub);
ager@chromium.org357bf652010-04-12 11:30:10 +0000988 // The generic stub returns its value in r0, but that's not
989 // necessarily what we want. We want whatever the inlined code
990 // expected, which is that the answer is in the same register as
991 // the operand was.
992 __ Move(tos_register_, r0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000993}
994
995
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000996static bool PopCountLessThanEqual2(unsigned int x) {
997 x &= x - 1;
998 return (x & (x - 1)) == 0;
999}
1000
1001
1002// Returns the index of the lowest bit set.
1003static int BitPosition(unsigned x) {
1004 int bit_posn = 0;
1005 while ((x & 0xf) == 0) {
1006 bit_posn += 4;
1007 x >>= 4;
1008 }
1009 while ((x & 1) == 0) {
1010 bit_posn++;
1011 x >>= 1;
1012 }
1013 return bit_posn;
1014}
1015
1016
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001017void CodeGenerator::SmiOperation(Token::Value op,
1018 Handle<Object> value,
1019 bool reversed,
1020 OverwriteMode mode) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001021 int int_value = Smi::cast(*value)->value();
1022
1023 bool something_to_inline;
1024 switch (op) {
1025 case Token::ADD:
1026 case Token::SUB:
1027 case Token::BIT_AND:
1028 case Token::BIT_OR:
1029 case Token::BIT_XOR: {
1030 something_to_inline = true;
1031 break;
1032 }
1033 case Token::SHL:
1034 case Token::SHR:
1035 case Token::SAR: {
1036 if (reversed) {
1037 something_to_inline = false;
1038 } else {
1039 something_to_inline = true;
1040 }
1041 break;
1042 }
1043 case Token::MOD: {
1044 if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
1045 something_to_inline = false;
1046 } else {
1047 something_to_inline = true;
1048 }
1049 break;
1050 }
1051 case Token::MUL: {
1052 if (!IsEasyToMultiplyBy(int_value)) {
1053 something_to_inline = false;
1054 } else {
1055 something_to_inline = true;
1056 }
1057 break;
1058 }
1059 default: {
1060 something_to_inline = false;
1061 break;
1062 }
1063 }
1064
1065 if (!something_to_inline) {
1066 if (!reversed) {
1067 // Push the rhs onto the virtual frame by putting it in a TOS register.
1068 Register rhs = frame_->GetTOSRegister();
1069 __ mov(rhs, Operand(value));
1070 frame_->EmitPush(rhs);
1071 VirtualFrameBinaryOperation(op, mode, int_value);
1072 } else {
1073 // Pop the rhs, then push lhs and rhs in the right order. Only performs
1074 // at most one pop, the rest takes place in TOS registers.
1075 Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
1076 Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
1077 __ mov(lhs, Operand(value));
1078 frame_->EmitPush(lhs);
1079 frame_->EmitPush(rhs);
1080 VirtualFrameBinaryOperation(op, mode, kUnknownIntValue);
1081 }
1082 return;
1083 }
1084
1085 // We move the top of stack to a register (normally no move is invoved).
1086 Register tos = frame_->PopToRegister();
1087 // All other registers are spilled. The deferred code expects one argument
1088 // in a register and all other values are flushed to the stack. The
1089 // answer is returned in the same register that the top of stack argument was
1090 // in.
1091 frame_->SpillAll();
1092
1093 switch (op) {
1094 case Token::ADD: {
1095 DeferredCode* deferred =
1096 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1097
1098 __ add(tos, tos, Operand(value), SetCC);
1099 deferred->Branch(vs);
1100 __ tst(tos, Operand(kSmiTagMask));
1101 deferred->Branch(ne);
1102 deferred->BindExit();
1103 frame_->EmitPush(tos);
1104 break;
1105 }
1106
1107 case Token::SUB: {
1108 DeferredCode* deferred =
1109 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1110
1111 if (reversed) {
1112 __ rsb(tos, tos, Operand(value), SetCC);
1113 } else {
1114 __ sub(tos, tos, Operand(value), SetCC);
1115 }
1116 deferred->Branch(vs);
1117 __ tst(tos, Operand(kSmiTagMask));
1118 deferred->Branch(ne);
1119 deferred->BindExit();
1120 frame_->EmitPush(tos);
1121 break;
1122 }
1123
1124
1125 case Token::BIT_OR:
1126 case Token::BIT_XOR:
1127 case Token::BIT_AND: {
1128 DeferredCode* deferred =
1129 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1130 __ tst(tos, Operand(kSmiTagMask));
1131 deferred->Branch(ne);
1132 switch (op) {
1133 case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
1134 case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
1135 case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
1136 default: UNREACHABLE();
1137 }
1138 deferred->BindExit();
1139 frame_->EmitPush(tos);
1140 break;
1141 }
1142
1143 case Token::SHL:
1144 case Token::SHR:
1145 case Token::SAR: {
1146 ASSERT(!reversed);
1147 Register scratch = VirtualFrame::scratch0();
1148 Register scratch2 = VirtualFrame::scratch1();
1149 int shift_value = int_value & 0x1f; // least significant 5 bits
1150 DeferredCode* deferred =
1151 new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001152 uint32_t problematic_mask = kSmiTagMask;
1153 // For unsigned shift by zero all negative smis are problematic.
1154 if (shift_value == 0 && op == Token::SHR) problematic_mask |= 0x80000000;
1155 __ tst(tos, Operand(problematic_mask));
1156 deferred->Branch(ne); // Go slow for problematic input.
ager@chromium.org357bf652010-04-12 11:30:10 +00001157 switch (op) {
1158 case Token::SHL: {
1159 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001160 int adjusted_shift = shift_value - kSmiTagSize;
1161 ASSERT(adjusted_shift >= 0);
1162 if (adjusted_shift != 0) {
1163 __ mov(scratch, Operand(tos, LSL, adjusted_shift));
1164 // Check that the *signed* result fits in a smi.
1165 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
1166 deferred->Branch(mi);
1167 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
1168 } else {
1169 // Check that the *signed* result fits in a smi.
1170 __ add(scratch2, tos, Operand(0x40000000), SetCC);
1171 deferred->Branch(mi);
1172 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
1173 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001174 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001175 break;
1176 }
1177 case Token::SHR: {
ager@chromium.org357bf652010-04-12 11:30:10 +00001178 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001179 __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Remove tag.
1180 // LSR by immediate 0 means shifting 32 bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001181 __ mov(scratch, Operand(scratch, LSR, shift_value));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001182 if (shift_value == 1) {
1183 // check that the *unsigned* result fits in a smi
1184 // neither of the two high-order bits can be set:
1185 // - 0x80000000: high bit would be lost when smi tagging
1186 // - 0x40000000: this number would convert to negative when
1187 // smi tagging these two cases can only happen with shifts
1188 // by 0 or 1 when handed a valid smi
1189 __ tst(scratch, Operand(0xc0000000));
1190 deferred->Branch(ne);
1191 }
1192 __ mov(tos, Operand(scratch, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001193 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001194 break;
1195 }
1196 case Token::SAR: {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001197 // In the ARM instructions set, ASR by immediate 0 means shifting 32
1198 // bits.
ager@chromium.org357bf652010-04-12 11:30:10 +00001199 if (shift_value != 0) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001200 // Do the shift and the tag removal in one operation. If the shift
1201 // is 31 bits (the highest possible value) then we emit the
1202 // instruction as a shift by 0 which means shift arithmetically by
1203 // 32.
1204 __ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
1205 // Put tag back.
1206 __ mov(tos, Operand(tos, LSL, kSmiTagSize));
ager@chromium.org357bf652010-04-12 11:30:10 +00001207 }
1208 break;
1209 }
1210 default: UNREACHABLE();
1211 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001212 deferred->BindExit();
1213 frame_->EmitPush(tos);
1214 break;
1215 }
1216
1217 case Token::MOD: {
1218 ASSERT(!reversed);
1219 ASSERT(int_value >= 2);
1220 ASSERT(IsPowerOf2(int_value));
1221 DeferredCode* deferred =
1222 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1223 unsigned mask = (0x80000000u | kSmiTagMask);
1224 __ tst(tos, Operand(mask));
1225 deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
1226 mask = (int_value << kSmiTagSize) - 1;
1227 __ and_(tos, tos, Operand(mask));
1228 deferred->BindExit();
1229 frame_->EmitPush(tos);
1230 break;
1231 }
1232
1233 case Token::MUL: {
1234 ASSERT(IsEasyToMultiplyBy(int_value));
1235 DeferredCode* deferred =
1236 new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
1237 unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
1238 max_smi_that_wont_overflow <<= kSmiTagSize;
1239 unsigned mask = 0x80000000u;
1240 while ((mask & max_smi_that_wont_overflow) == 0) {
1241 mask |= mask >> 1;
1242 }
1243 mask |= kSmiTagMask;
1244 // This does a single mask that checks for a too high value in a
1245 // conservative way and for a non-Smi. It also filters out negative
1246 // numbers, unfortunately, but since this code is inline we prefer
1247 // brevity to comprehensiveness.
1248 __ tst(tos, Operand(mask));
1249 deferred->Branch(ne);
1250 MultiplyByKnownInt(masm_, tos, tos, int_value);
1251 deferred->BindExit();
1252 frame_->EmitPush(tos);
1253 break;
1254 }
1255
1256 default:
1257 UNREACHABLE();
1258 break;
1259 }
1260}
1261
1262
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001263void CodeGenerator::Comparison(Condition cc,
1264 Expression* left,
1265 Expression* right,
1266 bool strict) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001267 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001268
ager@chromium.org357bf652010-04-12 11:30:10 +00001269 if (left != NULL) Load(left);
1270 if (right != NULL) Load(right);
1271
mads.s.ager31e71382008-08-13 09:32:07 +00001272 // sp[0] : y
1273 // sp[1] : x
1274 // result : cc register
1275
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001276 // Strict only makes sense for equality comparisons.
1277 ASSERT(!strict || cc == eq);
1278
ager@chromium.org357bf652010-04-12 11:30:10 +00001279 Register lhs;
1280 Register rhs;
1281
1282 // We load the top two stack positions into registers chosen by the virtual
1283 // frame. This should keep the register shuffling to a minimum.
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001284 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
1285 if (cc == gt || cc == le) {
1286 cc = ReverseCondition(cc);
ager@chromium.org357bf652010-04-12 11:30:10 +00001287 lhs = frame_->PopToRegister();
1288 rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001289 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00001290 rhs = frame_->PopToRegister();
1291 lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
mads.s.ager@gmail.com769cc962008-08-06 10:02:49 +00001292 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001293
1294 ASSERT(rhs.is(r0) || rhs.is(r1));
1295 ASSERT(lhs.is(r0) || lhs.is(r1));
1296
1297 // Now we have the two sides in r0 and r1. We flush any other registers
1298 // because the stub doesn't know about register allocation.
1299 frame_->SpillAll();
1300 Register scratch = VirtualFrame::scratch0();
1301 __ orr(scratch, lhs, Operand(rhs));
1302 __ tst(scratch, Operand(kSmiTagMask));
1303 JumpTarget smi;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001304 smi.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001305
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001306 // Perform non-smi comparison by stub.
1307 // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
1308 // We call with 0 args because there are 0 on the stack.
ager@chromium.org357bf652010-04-12 11:30:10 +00001309 if (!rhs.is(r0)) {
1310 __ Swap(rhs, lhs, ip);
1311 }
1312
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001313 CompareStub stub(cc, strict);
1314 frame_->CallStub(&stub, 0);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001315 __ cmp(r0, Operand(0));
ager@chromium.org357bf652010-04-12 11:30:10 +00001316 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001317 exit.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001318
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001319 // Do smi comparisons by pointer comparison.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001320 smi.Bind();
ager@chromium.org357bf652010-04-12 11:30:10 +00001321 __ cmp(lhs, Operand(rhs));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001322
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001323 exit.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001324 cc_reg_ = cc;
1325}
1326
1327
mads.s.ager31e71382008-08-13 09:32:07 +00001328// Call the function on the stack with the given arguments.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001329void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001330 CallFunctionFlags flags,
1331 int position) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001332 frame_->AssertIsSpilled();
1333
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001334 // Push the arguments ("left-to-right") on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001335 int arg_count = args->length();
1336 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001337 Load(args->at(i));
mads.s.ager31e71382008-08-13 09:32:07 +00001338 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001339
kasper.lund7276f142008-07-30 08:49:36 +00001340 // Record the position for debugging purposes.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001341 CodeForSourcePosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001342
kasper.lund7276f142008-07-30 08:49:36 +00001343 // Use the shared code stub to call the function.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001344 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001345 CallFunctionStub call_function(arg_count, in_loop, flags);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001346 frame_->CallStub(&call_function, arg_count + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001347
1348 // Restore context and pop function from the stack.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001349 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001350 frame_->Drop(); // discard the TOS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001351}
1352
1353
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001354void CodeGenerator::CallApplyLazy(Expression* applicand,
1355 Expression* receiver,
1356 VariableProxy* arguments,
1357 int position) {
1358 // An optimized implementation of expressions of the form
1359 // x.apply(y, arguments).
1360 // If the arguments object of the scope has not been allocated,
1361 // and x.apply is Function.prototype.apply, this optimization
1362 // just copies y and the arguments of the current function on the
1363 // stack, as receiver and arguments, and calls x.
1364 // In the implementation comments, we call x the applicand
1365 // and y the receiver.
1366 VirtualFrame::SpilledScope spilled_scope(frame_);
1367
1368 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
1369 ASSERT(arguments->IsArguments());
1370
1371 // Load applicand.apply onto the stack. This will usually
1372 // give us a megamorphic load site. Not super, but it works.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001373 Load(applicand);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001374 Handle<String> name = Factory::LookupAsciiSymbol("apply");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001375 frame_->Dup();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001376 frame_->CallLoadIC(name, RelocInfo::CODE_TARGET);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001377 frame_->EmitPush(r0);
1378
1379 // Load the receiver and the existing arguments object onto the
1380 // expression stack. Avoid allocating the arguments object here.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001381 Load(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001382 LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
1383
1384 // Emit the source position information after having loaded the
1385 // receiver and the arguments.
1386 CodeForSourcePosition(position);
1387 // Contents of the stack at this point:
1388 // sp[0]: arguments object of the current function or the hole.
1389 // sp[1]: receiver
1390 // sp[2]: applicand.apply
1391 // sp[3]: applicand.
1392
1393 // Check if the arguments object has been lazily allocated
1394 // already. If so, just use that instead of copying the arguments
1395 // from the stack. This also deals with cases where a local variable
1396 // named 'arguments' has been introduced.
1397 __ ldr(r0, MemOperand(sp, 0));
1398
1399 Label slow, done;
1400 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
1401 __ cmp(ip, r0);
1402 __ b(ne, &slow);
1403
1404 Label build_args;
1405 // Get rid of the arguments object probe.
1406 frame_->Drop();
1407 // Stack now has 3 elements on it.
1408 // Contents of stack at this point:
1409 // sp[0]: receiver
1410 // sp[1]: applicand.apply
1411 // sp[2]: applicand.
1412
1413 // Check that the receiver really is a JavaScript object.
1414 __ ldr(r0, MemOperand(sp, 0));
1415 __ BranchOnSmi(r0, &build_args);
1416 // We allow all JSObjects including JSFunctions. As long as
1417 // JS_FUNCTION_TYPE is the last instance type and it is right
1418 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
1419 // bound.
1420 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
1421 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
1422 __ CompareObjectType(r0, r1, r2, FIRST_JS_OBJECT_TYPE);
1423 __ b(lt, &build_args);
1424
1425 // Check that applicand.apply is Function.prototype.apply.
1426 __ ldr(r0, MemOperand(sp, kPointerSize));
1427 __ BranchOnSmi(r0, &build_args);
1428 __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
1429 __ b(ne, &build_args);
1430 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
1431 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
1432 __ ldr(r1, FieldMemOperand(r0, SharedFunctionInfo::kCodeOffset));
1433 __ cmp(r1, Operand(apply_code));
1434 __ b(ne, &build_args);
1435
1436 // Check that applicand is a function.
1437 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
1438 __ BranchOnSmi(r1, &build_args);
1439 __ CompareObjectType(r1, r2, r3, JS_FUNCTION_TYPE);
1440 __ b(ne, &build_args);
1441
1442 // Copy the arguments to this function possibly from the
1443 // adaptor frame below it.
1444 Label invoke, adapted;
1445 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
1446 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
1447 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
1448 __ b(eq, &adapted);
1449
1450 // No arguments adaptor frame. Copy fixed number of arguments.
1451 __ mov(r0, Operand(scope()->num_parameters()));
1452 for (int i = 0; i < scope()->num_parameters(); i++) {
1453 __ ldr(r2, frame_->ParameterAt(i));
1454 __ push(r2);
1455 }
1456 __ jmp(&invoke);
1457
1458 // Arguments adaptor frame present. Copy arguments from there, but
1459 // avoid copying too many arguments to avoid stack overflows.
1460 __ bind(&adapted);
1461 static const uint32_t kArgumentsLimit = 1 * KB;
1462 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
1463 __ mov(r0, Operand(r0, LSR, kSmiTagSize));
1464 __ mov(r3, r0);
1465 __ cmp(r0, Operand(kArgumentsLimit));
1466 __ b(gt, &build_args);
1467
1468 // Loop through the arguments pushing them onto the execution
1469 // stack. We don't inform the virtual frame of the push, so we don't
1470 // have to worry about getting rid of the elements from the virtual
1471 // frame.
1472 Label loop;
1473 // r3 is a small non-negative integer, due to the test above.
1474 __ cmp(r3, Operand(0));
1475 __ b(eq, &invoke);
1476 // Compute the address of the first argument.
1477 __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2));
1478 __ add(r2, r2, Operand(kPointerSize));
1479 __ bind(&loop);
1480 // Post-decrement argument address by kPointerSize on each iteration.
1481 __ ldr(r4, MemOperand(r2, kPointerSize, NegPostIndex));
1482 __ push(r4);
1483 __ sub(r3, r3, Operand(1), SetCC);
1484 __ b(gt, &loop);
1485
1486 // Invoke the function.
1487 __ bind(&invoke);
1488 ParameterCount actual(r0);
1489 __ InvokeFunction(r1, actual, CALL_FUNCTION);
1490 // Drop applicand.apply and applicand from the stack, and push
1491 // the result of the function call, but leave the spilled frame
1492 // unchanged, with 3 elements, so it is correct when we compile the
1493 // slow-case code.
1494 __ add(sp, sp, Operand(2 * kPointerSize));
1495 __ push(r0);
1496 // Stack now has 1 element:
1497 // sp[0]: result
1498 __ jmp(&done);
1499
1500 // Slow-case: Allocate the arguments object since we know it isn't
1501 // there, and fall-through to the slow-case where we call
1502 // applicand.apply.
1503 __ bind(&build_args);
1504 // Stack now has 3 elements, because we have jumped from where:
1505 // sp[0]: receiver
1506 // sp[1]: applicand.apply
1507 // sp[2]: applicand.
1508 StoreArgumentsObject(false);
1509
1510 // Stack and frame now have 4 elements.
1511 __ bind(&slow);
1512
1513 // Generic computation of x.apply(y, args) with no special optimization.
1514 // Flip applicand.apply and applicand on the stack, so
1515 // applicand looks like the receiver of the applicand.apply call.
1516 // Then process it as a normal function call.
1517 __ ldr(r0, MemOperand(sp, 3 * kPointerSize));
1518 __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00001519 __ Strd(r0, r1, MemOperand(sp, 2 * kPointerSize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001520
1521 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
1522 frame_->CallStub(&call_function, 3);
1523 // The function and its two arguments have been dropped.
1524 frame_->Drop(); // Drop the receiver as well.
1525 frame_->EmitPush(r0);
1526 // Stack now has 1 element:
1527 // sp[0]: result
1528 __ bind(&done);
1529
1530 // Restore the context register after a call.
1531 __ ldr(cp, frame_->Context());
1532}
1533
1534
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001535void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001536 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001537 ASSERT(has_cc());
1538 Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001539 target->Branch(cc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001540 cc_reg_ = al;
1541}
1542
1543
ager@chromium.org7c537e22008-10-16 08:43:32 +00001544void CodeGenerator::CheckStack() {
ager@chromium.org357bf652010-04-12 11:30:10 +00001545 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org3811b432009-10-28 14:53:37 +00001546 Comment cmnt(masm_, "[ check stack");
1547 __ LoadRoot(ip, Heap::kStackLimitRootIndex);
1548 // Put the lr setup instruction in the delay slot. kInstrSize is added to
1549 // the implicit 8 byte offset that always applies to operations with pc and
1550 // gives a return address 12 bytes down.
1551 masm_->add(lr, pc, Operand(Assembler::kInstrSize));
1552 masm_->cmp(sp, Operand(ip));
1553 StackCheckStub stub;
1554 // Call the stub if lower.
1555 masm_->mov(pc,
1556 Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
1557 RelocInfo::CODE_TARGET),
1558 LeaveCC,
1559 lo);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001560}
1561
1562
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001563void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
1564#ifdef DEBUG
1565 int original_height = frame_->height();
1566#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001567 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001568 for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001569 Visit(statements->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001570 }
1571 ASSERT(!has_valid_frame() || frame_->height() == original_height);
1572}
1573
1574
ager@chromium.org7c537e22008-10-16 08:43:32 +00001575void CodeGenerator::VisitBlock(Block* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001576#ifdef DEBUG
1577 int original_height = frame_->height();
1578#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001579 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001580 Comment cmnt(masm_, "[ Block");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001581 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001582 node->break_target()->SetExpectedHeight();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001583 VisitStatements(node->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001584 if (node->break_target()->is_linked()) {
1585 node->break_target()->Bind();
1586 }
1587 node->break_target()->Unuse();
1588 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001589}
1590
1591
ager@chromium.org7c537e22008-10-16 08:43:32 +00001592void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001593 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001594 frame_->EmitPush(Operand(pairs));
1595 frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0)));
1596
1597 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001598 frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
mads.s.ager31e71382008-08-13 09:32:07 +00001599 // The result is discarded.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001600}
1601
1602
ager@chromium.org7c537e22008-10-16 08:43:32 +00001603void CodeGenerator::VisitDeclaration(Declaration* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001604#ifdef DEBUG
1605 int original_height = frame_->height();
1606#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 Comment cmnt(masm_, "[ Declaration");
1608 Variable* var = node->proxy()->var();
1609 ASSERT(var != NULL); // must have been resolved
1610 Slot* slot = var->slot();
1611
1612 // If it was not possible to allocate the variable at compile time,
1613 // we need to "declare" it at runtime to make sure it actually
1614 // exists in the local context.
1615 if (slot != NULL && slot->type() == Slot::LOOKUP) {
1616 // Variables with a "LOOKUP" slot were introduced as non-locals
1617 // during variable resolution and must have mode DYNAMIC.
ager@chromium.org381abbb2009-02-25 13:23:22 +00001618 ASSERT(var->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001619 // For now, just do a runtime call.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001620 frame_->EmitPush(cp);
ager@chromium.orgac091b72010-05-05 07:34:42 +00001621 frame_->EmitPush(Operand(var->name()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001622 // Declaration nodes are always declared in only two modes.
1623 ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
1624 PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
ager@chromium.orgac091b72010-05-05 07:34:42 +00001625 frame_->EmitPush(Operand(Smi::FromInt(attr)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001626 // Push initial value, if any.
1627 // Note: For variables we must not push an initial value (such as
1628 // 'undefined') because we may have a (legal) redeclaration and we
1629 // must not destroy the current value.
1630 if (node->mode() == Variable::CONST) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001631 frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001632 } else if (node->fun() != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001633 Load(node->fun());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001634 } else {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001635 frame_->EmitPush(Operand(0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001636 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00001637
1638 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001639 frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001640 // Ignore the return value (declarations are statements).
ager@chromium.orgac091b72010-05-05 07:34:42 +00001641
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001642 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001643 return;
1644 }
1645
1646 ASSERT(!var->is_global());
1647
1648 // If we have a function or a constant, we need to initialize the variable.
1649 Expression* val = NULL;
1650 if (node->mode() == Variable::CONST) {
1651 val = new Literal(Factory::the_hole_value());
1652 } else {
1653 val = node->fun(); // NULL if we don't have a function
1654 }
1655
1656 if (val != NULL) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001657 // Set initial value.
1658 Reference target(this, node->proxy());
1659 Load(val);
1660 target.SetValue(NOT_CONST_INIT);
1661
iposva@chromium.org245aa852009-02-10 00:49:54 +00001662 // Get rid of the assigned value (declarations are statements).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001663 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001664 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001665 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001666}
1667
1668
ager@chromium.org7c537e22008-10-16 08:43:32 +00001669void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001670#ifdef DEBUG
1671 int original_height = frame_->height();
1672#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001673 Comment cmnt(masm_, "[ ExpressionStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001674 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001675 Expression* expression = node->expression();
1676 expression->MarkAsStatement();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001677 Load(expression);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001678 frame_->Drop();
1679 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001680}
1681
1682
ager@chromium.org7c537e22008-10-16 08:43:32 +00001683void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001684#ifdef DEBUG
1685 int original_height = frame_->height();
1686#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001687 Comment cmnt(masm_, "// EmptyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001688 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001689 // nothing to do
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001690 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001691}
1692
1693
ager@chromium.org7c537e22008-10-16 08:43:32 +00001694void CodeGenerator::VisitIfStatement(IfStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001695#ifdef DEBUG
1696 int original_height = frame_->height();
1697#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001698 Comment cmnt(masm_, "[ IfStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001699 // Generate different code depending on which parts of the if statement
1700 // are present or not.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001701 bool has_then_stm = node->HasThenStatement();
1702 bool has_else_stm = node->HasElseStatement();
1703
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001704 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001705
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001706 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001707 if (has_then_stm && has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001708 Comment cmnt(masm_, "[ IfThenElse");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001709 JumpTarget then;
1710 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001711 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001712 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001713 if (frame_ != NULL) {
1714 Branch(false, &else_);
1715 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001716 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001717 if (frame_ != NULL || then.is_linked()) {
1718 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001719 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001720 }
1721 if (frame_ != NULL) {
1722 exit.Jump();
1723 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001724 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001725 if (else_.is_linked()) {
1726 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001727 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001728 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001729
1730 } else if (has_then_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001731 Comment cmnt(masm_, "[ IfThen");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001732 ASSERT(!has_else_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001733 JumpTarget then;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001734 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001735 LoadCondition(node->condition(), &then, &exit, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001736 if (frame_ != NULL) {
1737 Branch(false, &exit);
1738 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001739 // then
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001740 if (frame_ != NULL || then.is_linked()) {
1741 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001742 Visit(node->then_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001743 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001744
1745 } else if (has_else_stm) {
mads.s.ager31e71382008-08-13 09:32:07 +00001746 Comment cmnt(masm_, "[ IfElse");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001747 ASSERT(!has_then_stm);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001748 JumpTarget else_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001749 // if (!cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001750 LoadCondition(node->condition(), &exit, &else_, true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001751 if (frame_ != NULL) {
1752 Branch(true, &exit);
1753 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001754 // else
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001755 if (frame_ != NULL || else_.is_linked()) {
1756 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001757 Visit(node->else_statement());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001758 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001759
1760 } else {
mads.s.ager31e71382008-08-13 09:32:07 +00001761 Comment cmnt(masm_, "[ If");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001762 ASSERT(!has_then_stm && !has_else_stm);
1763 // if (cond)
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001764 LoadCondition(node->condition(), &exit, &exit, false);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001765 if (frame_ != NULL) {
1766 if (has_cc()) {
1767 cc_reg_ = al;
1768 } else {
1769 frame_->Drop();
1770 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001771 }
1772 }
1773
1774 // end
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001775 if (exit.is_linked()) {
1776 exit.Bind();
1777 }
1778 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779}
1780
1781
ager@chromium.org7c537e22008-10-16 08:43:32 +00001782void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001783 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001784 Comment cmnt(masm_, "[ ContinueStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001785 CodeForStatementPosition(node);
1786 node->target()->continue_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001787}
1788
1789
ager@chromium.org7c537e22008-10-16 08:43:32 +00001790void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001791 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001792 Comment cmnt(masm_, "[ BreakStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001793 CodeForStatementPosition(node);
1794 node->target()->break_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001795}
1796
1797
ager@chromium.org7c537e22008-10-16 08:43:32 +00001798void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001799 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001800 Comment cmnt(masm_, "[ ReturnStatement");
mads.s.ager31e71382008-08-13 09:32:07 +00001801
ager@chromium.org4af710e2009-09-15 12:20:11 +00001802 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001803 Load(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001804 if (function_return_is_shadowed_) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001805 frame_->EmitPop(r0);
1806 function_return_.Jump();
1807 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001808 // Pop the result from the frame and prepare the frame for
1809 // returning thus making it easier to merge.
1810 frame_->EmitPop(r0);
1811 frame_->PrepareForReturn();
1812
1813 function_return_.Jump();
1814 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001815}
1816
1817
ager@chromium.org7c537e22008-10-16 08:43:32 +00001818void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001819#ifdef DEBUG
1820 int original_height = frame_->height();
1821#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001822 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001823 Comment cmnt(masm_, "[ WithEnterStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001824 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001825 Load(node->expression());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001826 if (node->is_catch_block()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001827 frame_->CallRuntime(Runtime::kPushCatchContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001828 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001829 frame_->CallRuntime(Runtime::kPushContext, 1);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001830 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001831#ifdef DEBUG
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001832 JumpTarget verified_true;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001833 __ cmp(r0, cp);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00001834 verified_true.Branch(eq);
1835 __ stop("PushContext: r0 is expected to be the same as cp");
1836 verified_true.Bind();
1837#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001838 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001839 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001840 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841}
1842
1843
ager@chromium.org7c537e22008-10-16 08:43:32 +00001844void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001845#ifdef DEBUG
1846 int original_height = frame_->height();
1847#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001848 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001849 Comment cmnt(masm_, "[ WithExitStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001850 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001851 // Pop context.
1852 __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
1853 // Update context local.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001854 __ str(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001855 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001856}
1857
1858
ager@chromium.org7c537e22008-10-16 08:43:32 +00001859void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001860#ifdef DEBUG
1861 int original_height = frame_->height();
1862#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001863 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001864 Comment cmnt(masm_, "[ SwitchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001865 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001866 node->break_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001867
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001868 Load(node->tag());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001869
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001870 JumpTarget next_test;
1871 JumpTarget fall_through;
1872 JumpTarget default_entry;
1873 JumpTarget default_exit(JumpTarget::BIDIRECTIONAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001874 ZoneList<CaseClause*>* cases = node->cases();
1875 int length = cases->length();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001876 CaseClause* default_clause = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001877
1878 for (int i = 0; i < length; i++) {
1879 CaseClause* clause = cases->at(i);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001880 if (clause->is_default()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001881 // Remember the default clause and compile it at the end.
1882 default_clause = clause;
1883 continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001884 }
1885
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001886 Comment cmnt(masm_, "[ Case clause");
1887 // Compile the test.
1888 next_test.Bind();
1889 next_test.Unuse();
1890 // Duplicate TOS.
1891 __ ldr(r0, frame_->Top());
1892 frame_->EmitPush(r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001893 Comparison(eq, NULL, clause->label(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001894 Branch(false, &next_test);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001895
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001896 // Before entering the body from the test, remove the switch value from
1897 // the stack.
1898 frame_->Drop();
1899
1900 // Label the body so that fall through is enabled.
1901 if (i > 0 && cases->at(i - 1)->is_default()) {
1902 default_exit.Bind();
1903 } else {
1904 fall_through.Bind();
1905 fall_through.Unuse();
1906 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001907 VisitStatements(clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001908
1909 // If control flow can fall through from the body, jump to the next body
1910 // or the end of the statement.
1911 if (frame_ != NULL) {
1912 if (i < length - 1 && cases->at(i + 1)->is_default()) {
1913 default_entry.Jump();
1914 } else {
1915 fall_through.Jump();
1916 }
1917 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001918 }
1919
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001920 // The final "test" removes the switch value.
1921 next_test.Bind();
1922 frame_->Drop();
1923
1924 // If there is a default clause, compile it.
1925 if (default_clause != NULL) {
1926 Comment cmnt(masm_, "[ Default clause");
1927 default_entry.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001928 VisitStatements(default_clause->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001929 // If control flow can fall out of the default and there is a case after
1930 // it, jup to that case's body.
1931 if (frame_ != NULL && default_exit.is_bound()) {
1932 default_exit.Jump();
1933 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00001934 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001935
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001936 if (fall_through.is_linked()) {
1937 fall_through.Bind();
1938 }
1939
1940 if (node->break_target()->is_linked()) {
1941 node->break_target()->Bind();
1942 }
1943 node->break_target()->Unuse();
1944 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001945}
1946
1947
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001948void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001949#ifdef DEBUG
1950 int original_height = frame_->height();
1951#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00001952 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001953 Comment cmnt(masm_, "[ DoWhileStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001954 CodeForStatementPosition(node);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001955 node->break_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001956 JumpTarget body(JumpTarget::BIDIRECTIONAL);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00001957 IncrementLoopNesting();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001958
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001959 // Label the top of the loop for the backward CFG edge. If the test
1960 // is always true we can use the continue target, and if the test is
1961 // always false there is no need.
1962 ConditionAnalysis info = AnalyzeCondition(node->cond());
1963 switch (info) {
1964 case ALWAYS_TRUE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001965 node->continue_target()->SetExpectedHeight();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001966 node->continue_target()->Bind();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001967 break;
1968 case ALWAYS_FALSE:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001969 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001970 break;
1971 case DONT_KNOW:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00001972 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001973 body.Bind();
1974 break;
1975 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001976
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001977 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00001978 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001979
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001980 // Compile the test.
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001981 switch (info) {
1982 case ALWAYS_TRUE:
1983 // If control can fall off the end of the body, jump back to the
1984 // top.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001985 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001986 node->continue_target()->Jump();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001987 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001988 break;
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001989 case ALWAYS_FALSE:
1990 // If we have a continue in the body, we only have to bind its
1991 // jump target.
1992 if (node->continue_target()->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001993 node->continue_target()->Bind();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001994 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001995 break;
1996 case DONT_KNOW:
1997 // We have to compile the test expression if it can be reached by
1998 // control flow falling out of the body or via continue.
1999 if (node->continue_target()->is_linked()) {
2000 node->continue_target()->Bind();
2001 }
2002 if (has_valid_frame()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002003 Comment cmnt(masm_, "[ DoWhileCondition");
2004 CodeForDoWhileConditionPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002005 LoadCondition(node->cond(), &body, node->break_target(), true);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002006 if (has_valid_frame()) {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002007 // A invalid frame here indicates that control did not
2008 // fall out of the test expression.
2009 Branch(true, &body);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002010 }
2011 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002012 break;
2013 }
2014
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002015 if (node->break_target()->is_linked()) {
2016 node->break_target()->Bind();
2017 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002018 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002019 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2020}
2021
2022
2023void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
2024#ifdef DEBUG
2025 int original_height = frame_->height();
2026#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002027 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002028 Comment cmnt(masm_, "[ WhileStatement");
2029 CodeForStatementPosition(node);
2030
2031 // If the test is never true and has no side effects there is no need
2032 // to compile the test or body.
2033 ConditionAnalysis info = AnalyzeCondition(node->cond());
2034 if (info == ALWAYS_FALSE) return;
2035
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002036 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002037 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002038
2039 // Label the top of the loop with the continue target for the backward
2040 // CFG edge.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002041 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002042 node->continue_target()->Bind();
2043
2044 if (info == DONT_KNOW) {
2045 JumpTarget body;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002046 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002047 if (has_valid_frame()) {
2048 // A NULL frame indicates that control did not fall out of the
2049 // test expression.
2050 Branch(false, node->break_target());
2051 }
2052 if (has_valid_frame() || body.is_linked()) {
2053 body.Bind();
2054 }
2055 }
2056
2057 if (has_valid_frame()) {
2058 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002059 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002060
2061 // If control flow can fall out of the body, jump back to the top.
2062 if (has_valid_frame()) {
2063 node->continue_target()->Jump();
2064 }
2065 }
2066 if (node->break_target()->is_linked()) {
2067 node->break_target()->Bind();
2068 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002069 DecrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002070 ASSERT(!has_valid_frame() || frame_->height() == original_height);
2071}
2072
2073
2074void CodeGenerator::VisitForStatement(ForStatement* node) {
2075#ifdef DEBUG
2076 int original_height = frame_->height();
2077#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002078 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002079 Comment cmnt(masm_, "[ ForStatement");
2080 CodeForStatementPosition(node);
2081 if (node->init() != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002082 Visit(node->init());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002083 }
2084
2085 // If the test is never true there is no need to compile the test or
2086 // body.
2087 ConditionAnalysis info = AnalyzeCondition(node->cond());
2088 if (info == ALWAYS_FALSE) return;
2089
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002090 node->break_target()->SetExpectedHeight();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002091 IncrementLoopNesting();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002092
2093 // If there is no update statement, label the top of the loop with the
2094 // continue target, otherwise with the loop target.
2095 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2096 if (node->next() == NULL) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002097 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002098 node->continue_target()->Bind();
2099 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002100 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002101 loop.Bind();
2102 }
2103
2104 // If the test is always true, there is no need to compile it.
2105 if (info == DONT_KNOW) {
2106 JumpTarget body;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002107 LoadCondition(node->cond(), &body, node->break_target(), true);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002108 if (has_valid_frame()) {
2109 Branch(false, node->break_target());
2110 }
2111 if (has_valid_frame() || body.is_linked()) {
2112 body.Bind();
2113 }
2114 }
2115
2116 if (has_valid_frame()) {
2117 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002118 Visit(node->body());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002119
2120 if (node->next() == NULL) {
2121 // If there is no update statement and control flow can fall out
2122 // of the loop, jump directly to the continue label.
2123 if (has_valid_frame()) {
2124 node->continue_target()->Jump();
2125 }
2126 } else {
2127 // If there is an update statement and control flow can reach it
2128 // via falling out of the body of the loop or continuing, we
2129 // compile the update statement.
2130 if (node->continue_target()->is_linked()) {
2131 node->continue_target()->Bind();
2132 }
2133 if (has_valid_frame()) {
2134 // Record source position of the statement as this code which is
2135 // after the code for the body actually belongs to the loop
2136 // statement and not the body.
2137 CodeForStatementPosition(node);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002138 Visit(node->next());
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002139 loop.Jump();
2140 }
2141 }
2142 }
2143 if (node->break_target()->is_linked()) {
2144 node->break_target()->Bind();
2145 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00002146 DecrementLoopNesting();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002147 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002148}
2149
2150
ager@chromium.org7c537e22008-10-16 08:43:32 +00002151void CodeGenerator::VisitForInStatement(ForInStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002152#ifdef DEBUG
2153 int original_height = frame_->height();
2154#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002155 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002156 Comment cmnt(masm_, "[ ForInStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002157 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002158
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002159 JumpTarget primitive;
2160 JumpTarget jsobject;
2161 JumpTarget fixed_array;
2162 JumpTarget entry(JumpTarget::BIDIRECTIONAL);
2163 JumpTarget end_del_check;
2164 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002165
2166 // Get the object to enumerate over (converted to JSObject).
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002167 Load(node->enumerable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002168
2169 // Both SpiderMonkey and kjs ignore null and undefined in contrast
2170 // to the specification. 12.6.4 mandates a call to ToObject.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002171 frame_->EmitPop(r0);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002172 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
2173 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002174 exit.Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002175 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2176 __ cmp(r0, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002177 exit.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002178
2179 // Stack layout in body:
2180 // [iteration counter (Smi)]
2181 // [length of array]
2182 // [FixedArray]
2183 // [Map or 0]
2184 // [Object]
2185
2186 // Check if enumerable is already a JSObject
2187 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002188 primitive.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002189 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002190 jsobject.Branch(hs);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002191
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002192 primitive.Bind();
2193 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002194 frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002195
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002196 jsobject.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002197 // Get the set of properties (as a FixedArray or Map).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002198 // r0: value to be iterated over
2199 frame_->EmitPush(r0); // Push the object being iterated over.
2200
2201 // Check cache validity in generated code. This is a fast case for
2202 // the JSObject::IsSimpleEnum cache validity checks. If we cannot
2203 // guarantee cache validity, call the runtime system to check cache
2204 // validity or get the property names in a fixed array.
2205 JumpTarget call_runtime;
2206 JumpTarget loop(JumpTarget::BIDIRECTIONAL);
2207 JumpTarget check_prototype;
2208 JumpTarget use_cache;
2209 __ mov(r1, Operand(r0));
2210 loop.Bind();
2211 // Check that there are no elements.
2212 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
2213 __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
2214 __ cmp(r2, r4);
2215 call_runtime.Branch(ne);
2216 // Check that instance descriptors are not empty so that we can
2217 // check for an enum cache. Leave the map in r3 for the subsequent
2218 // prototype load.
2219 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
2220 __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
2221 __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
2222 __ cmp(r2, ip);
2223 call_runtime.Branch(eq);
2224 // Check that there in an enum cache in the non-empty instance
2225 // descriptors. This is the case if the next enumeration index
2226 // field does not contain a smi.
2227 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
2228 __ tst(r2, Operand(kSmiTagMask));
2229 call_runtime.Branch(eq);
2230 // For all objects but the receiver, check that the cache is empty.
2231 // r4: empty fixed array root.
2232 __ cmp(r1, r0);
2233 check_prototype.Branch(eq);
2234 __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
2235 __ cmp(r2, r4);
2236 call_runtime.Branch(ne);
2237 check_prototype.Bind();
2238 // Load the prototype from the map and loop if non-null.
2239 __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
2240 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2241 __ cmp(r1, ip);
2242 loop.Branch(ne);
2243 // The enum cache is valid. Load the map of the object being
2244 // iterated over and use the cache for the iteration.
2245 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
2246 use_cache.Jump();
2247
2248 call_runtime.Bind();
2249 // Call the runtime to get the property names for the object.
2250 frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002251 frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002252
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002253 // If we got a map from the runtime call, we can do a fast
2254 // modification check. Otherwise, we got a fixed array, and we have
2255 // to do a slow check.
2256 // r0: map or fixed array (result from call to
2257 // Runtime::kGetPropertyNamesFast)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002258 __ mov(r2, Operand(r0));
2259 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002260 __ LoadRoot(ip, Heap::kMetaMapRootIndex);
2261 __ cmp(r1, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002262 fixed_array.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002263
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002264 use_cache.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002265 // Get enum cache
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00002266 // r0: map (either the result from a call to
2267 // Runtime::kGetPropertyNamesFast or has been fetched directly from
2268 // the object)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002269 __ mov(r1, Operand(r0));
2270 __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
2271 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
2272 __ ldr(r2,
2273 FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
2274
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002275 frame_->EmitPush(r0); // map
2276 frame_->EmitPush(r2); // enum cache bridge cache
mads.s.ager31e71382008-08-13 09:32:07 +00002277 __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002278 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002279 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002280 frame_->EmitPush(r0);
2281 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002282
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002283 fixed_array.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002284 __ mov(r1, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002285 frame_->EmitPush(r1); // insert 0 in place of Map
2286 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002287
2288 // Push the length of the array and the initial index onto the stack.
mads.s.ager31e71382008-08-13 09:32:07 +00002289 __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002290 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002291 __ mov(r0, Operand(Smi::FromInt(0))); // init index
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002292 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002293
2294 // Condition.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002295 entry.Bind();
mads.s.ager31e71382008-08-13 09:32:07 +00002296 // sp[0] : index
2297 // sp[1] : array/enum cache length
2298 // sp[2] : array or enum cache
2299 // sp[3] : 0 or map
2300 // sp[4] : enumerable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002301 // Grab the current frame's height for the break and continue
2302 // targets only after all the state is pushed on the frame.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002303 node->break_target()->SetExpectedHeight();
2304 node->continue_target()->SetExpectedHeight();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002305
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00002306 // Load the current count to r0, load the length to r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002307 __ Ldrd(r0, r1, frame_->ElementAt(0));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002308 __ cmp(r0, r1); // compare to the array length
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002309 node->break_target()->Branch(hs);
2310
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002311 // Get the i'th entry of the array.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002312 __ ldr(r2, frame_->ElementAt(2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002313 __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
2314 __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
2315
2316 // Get Map or 0.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002317 __ ldr(r2, frame_->ElementAt(3));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002318 // Check if this (still) matches the map of the enumerable.
2319 // If not, we have to filter the key.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002320 __ ldr(r1, frame_->ElementAt(4));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002321 __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
2322 __ cmp(r1, Operand(r2));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002323 end_del_check.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002324
2325 // Convert the entry to a string (or null if it isn't a property anymore).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002326 __ ldr(r0, frame_->ElementAt(4)); // push enumerable
2327 frame_->EmitPush(r0);
2328 frame_->EmitPush(r3); // push entry
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00002329 frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00002330 __ mov(r3, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002331
2332 // If the property has been removed while iterating, we just skip it.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002333 __ LoadRoot(ip, Heap::kNullValueRootIndex);
2334 __ cmp(r3, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002335 node->continue_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002336
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002337 end_del_check.Bind();
2338 // Store the entry in the 'each' expression and take another spin in the
2339 // loop. r3: i'th entry of the enum cache (or string there of)
2340 frame_->EmitPush(r3); // push entry
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002341 { Reference each(this, node->each());
2342 if (!each.is_illegal()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002343 if (each.size() > 0) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002344 __ ldr(r0, frame_->ElementAt(each.size()));
2345 frame_->EmitPush(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002346 each.SetValue(NOT_CONST_INIT);
2347 frame_->Drop(2);
2348 } else {
2349 // If the reference was to a slot we rely on the convenient property
2350 // that it doesn't matter whether a value (eg, r3 pushed above) is
2351 // right on top of or right underneath a zero-sized reference.
2352 each.SetValue(NOT_CONST_INIT);
2353 frame_->Drop();
mads.s.ager31e71382008-08-13 09:32:07 +00002354 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002355 }
2356 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002357 // Body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002358 CheckStack(); // TODO(1222600): ignore if body contains calls.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002359 Visit(node->body());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002360
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002361 // Next. Reestablish a spilled frame in case we are coming here via
2362 // a continue in the body.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002363 node->continue_target()->Bind();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002364 frame_->SpillAll();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002365 frame_->EmitPop(r0);
2366 __ add(r0, r0, Operand(Smi::FromInt(1)));
2367 frame_->EmitPush(r0);
2368 entry.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002369
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002370 // Cleanup. No need to spill because VirtualFrame::Drop is safe for
2371 // any frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002372 node->break_target()->Bind();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002373 frame_->Drop(5);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002374
2375 // Exit.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002376 exit.Bind();
2377 node->continue_target()->Unuse();
2378 node->break_target()->Unuse();
2379 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002380}
2381
2382
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002383void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002384#ifdef DEBUG
2385 int original_height = frame_->height();
2386#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002387 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002388 Comment cmnt(masm_, "[ TryCatchStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002389 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002390
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002391 JumpTarget try_block;
2392 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002393
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002394 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002395 // --- Catch block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002396 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002397
2398 // Store the caught exception in the catch variable.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002399 Variable* catch_var = node->catch_var()->var();
2400 ASSERT(catch_var != NULL && catch_var->slot() != NULL);
2401 StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002402
2403 // Remove the exception from the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002404 frame_->Drop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002405
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002406 VisitStatements(node->catch_block()->statements());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002407 if (frame_ != NULL) {
2408 exit.Jump();
2409 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410
2411
2412 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002413 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002415 frame_->PushTryHandler(TRY_CATCH_HANDLER);
2416 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002417
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002418 // Shadow the labels for all escapes from the try block, including
2419 // returns. During shadowing, the original label is hidden as the
2420 // LabelShadow and operations on the original actually affect the
2421 // shadowing label.
2422 //
2423 // We should probably try to unify the escaping labels and the return
2424 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002425 int nof_escapes = node->escaping_targets()->length();
2426 List<ShadowTarget*> shadows(1 + nof_escapes);
2427
2428 // Add the shadow target for the function return.
2429 static const int kReturnShadowIndex = 0;
2430 shadows.Add(new ShadowTarget(&function_return_));
2431 bool function_return_was_shadowed = function_return_is_shadowed_;
2432 function_return_is_shadowed_ = true;
2433 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2434
2435 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002436 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002437 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002438 }
2439
2440 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002441 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002442
2443 // Stop the introduced shadowing and count the number of required unlinks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002444 // After shadowing stops, the original labels are unshadowed and the
2445 // LabelShadows represent the formerly shadowing labels.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002446 bool has_unlinks = false;
2447 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002448 shadows[i]->StopShadowing();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002449 has_unlinks = has_unlinks || shadows[i]->is_linked();
2450 }
2451 function_return_is_shadowed_ = function_return_was_shadowed;
2452
2453 // Get an external reference to the handler address.
2454 ExternalReference handler_address(Top::k_handler_address);
2455
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002456 // If we can fall off the end of the try block, unlink from try chain.
2457 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002458 // The next handler address is on top of the frame. Unlink from
2459 // the handler list and drop the rest of this handler from the
2460 // frame.
2461 ASSERT(StackHandlerConstants::kNextOffset == 0);
2462 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002463 __ mov(r3, Operand(handler_address));
2464 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002465 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002466 if (has_unlinks) {
2467 exit.Jump();
2468 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002469 }
2470
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002471 // Generate unlink code for the (formerly) shadowing labels that have been
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002472 // jumped to. Deallocate each shadow target.
2473 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474 if (shadows[i]->is_linked()) {
mads.s.ager31e71382008-08-13 09:32:07 +00002475 // Unlink from try chain;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002476 shadows[i]->Bind();
2477 // Because we can be jumping here (to spilled code) from unspilled
2478 // code, we need to reestablish a spilled frame at this block.
2479 frame_->SpillAll();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002480
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002481 // Reload sp from the top handler, because some statements that we
2482 // break from (eg, for...in) may have left stuff on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002483 __ mov(r3, Operand(handler_address));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002484 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002485 frame_->Forget(frame_->height() - handler_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002486
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002487 ASSERT(StackHandlerConstants::kNextOffset == 0);
2488 frame_->EmitPop(r1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002489 __ str(r1, MemOperand(r3));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00002490 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002491
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002492 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
2493 frame_->PrepareForReturn();
2494 }
2495 shadows[i]->other_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002496 }
2497 }
2498
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002499 exit.Bind();
2500 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002501}
2502
2503
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002504void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002505#ifdef DEBUG
2506 int original_height = frame_->height();
2507#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002508 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00002509 Comment cmnt(masm_, "[ TryFinallyStatement");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002510 CodeForStatementPosition(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002511
2512 // State: Used to keep track of reason for entering the finally
2513 // block. Should probably be extended to hold information for
2514 // break/continue from within the try block.
2515 enum { FALLING, THROWING, JUMPING };
2516
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002517 JumpTarget try_block;
2518 JumpTarget finally_block;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002519
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002520 try_block.Call();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002521
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002522 frame_->EmitPush(r0); // save exception object on the stack
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002523 // In case of thrown exceptions, this is where we continue.
2524 __ mov(r2, Operand(Smi::FromInt(THROWING)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002525 finally_block.Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526
2527 // --- Try block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002528 try_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002529
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002530 frame_->PushTryHandler(TRY_FINALLY_HANDLER);
2531 int handler_height = frame_->height();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002532
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002533 // Shadow the labels for all escapes from the try block, including
2534 // returns. Shadowing hides the original label as the LabelShadow and
2535 // operations on the original actually affect the shadowing label.
2536 //
2537 // We should probably try to unify the escaping labels and the return
2538 // label.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002539 int nof_escapes = node->escaping_targets()->length();
2540 List<ShadowTarget*> shadows(1 + nof_escapes);
2541
2542 // Add the shadow target for the function return.
2543 static const int kReturnShadowIndex = 0;
2544 shadows.Add(new ShadowTarget(&function_return_));
2545 bool function_return_was_shadowed = function_return_is_shadowed_;
2546 function_return_is_shadowed_ = true;
2547 ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
2548
2549 // Add the remaining shadow targets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002550 for (int i = 0; i < nof_escapes; i++) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002551 shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002552 }
2553
2554 // Generate code for the statements in the try block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002555 VisitStatements(node->try_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002556
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002557 // Stop the introduced shadowing and count the number of required unlinks.
2558 // After shadowing stops, the original labels are unshadowed and the
2559 // LabelShadows represent the formerly shadowing labels.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002560 int nof_unlinks = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002561 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002562 shadows[i]->StopShadowing();
2563 if (shadows[i]->is_linked()) nof_unlinks++;
2564 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002565 function_return_is_shadowed_ = function_return_was_shadowed;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002566
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002567 // Get an external reference to the handler address.
2568 ExternalReference handler_address(Top::k_handler_address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002570 // If we can fall off the end of the try block, unlink from the try
2571 // chain and set the state on the frame to FALLING.
2572 if (has_valid_frame()) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002573 // The next handler address is on top of the frame.
2574 ASSERT(StackHandlerConstants::kNextOffset == 0);
2575 frame_->EmitPop(r1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002576 __ mov(r3, Operand(handler_address));
2577 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002578 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002579
2580 // Fake a top of stack value (unneeded when FALLING) and set the
2581 // state in r2, then jump around the unlink blocks if any.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002582 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002583 frame_->EmitPush(r0);
2584 __ mov(r2, Operand(Smi::FromInt(FALLING)));
2585 if (nof_unlinks > 0) {
2586 finally_block.Jump();
2587 }
2588 }
2589
2590 // Generate code to unlink and set the state for the (formerly)
2591 // shadowing targets that have been jumped to.
2592 for (int i = 0; i < shadows.length(); i++) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593 if (shadows[i]->is_linked()) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002594 // If we have come from the shadowed return, the return value is
2595 // in (a non-refcounted reference to) r0. We must preserve it
2596 // until it is pushed.
2597 //
2598 // Because we can be jumping here (to spilled code) from
2599 // unspilled code, we need to reestablish a spilled frame at
2600 // this block.
2601 shadows[i]->Bind();
2602 frame_->SpillAll();
2603
2604 // Reload sp from the top handler, because some statements that
2605 // we break from (eg, for...in) may have left stuff on the
2606 // stack.
2607 __ mov(r3, Operand(handler_address));
2608 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002609 frame_->Forget(frame_->height() - handler_height);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002610
2611 // Unlink this handler and drop it from the frame. The next
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002612 // handler address is currently on top of the frame.
2613 ASSERT(StackHandlerConstants::kNextOffset == 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002614 frame_->EmitPop(r1);
2615 __ str(r1, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002616 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002617
2618 if (i == kReturnShadowIndex) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002619 // If this label shadowed the function return, materialize the
2620 // return value on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002621 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00002622 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002623 // Fake TOS for targets that shadowed breaks and continues.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002624 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002625 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002626 }
2627 __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002628 if (--nof_unlinks > 0) {
2629 // If this is not the last unlink block, jump around the next.
2630 finally_block.Jump();
2631 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002632 }
2633 }
2634
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002635 // --- Finally block ---
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002636 finally_block.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002637
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002638 // Push the state on the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002639 frame_->EmitPush(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002640
2641 // We keep two elements on the stack - the (possibly faked) result
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002642 // and the state - while evaluating the finally block.
2643 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002644 // Generate code for the statements in the finally block.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002645 VisitStatements(node->finally_block()->statements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002646
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002647 if (has_valid_frame()) {
2648 // Restore state and return value or faked TOS.
2649 frame_->EmitPop(r2);
2650 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002651 }
2652
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002653 // Generate code to jump to the right destination for all used
2654 // formerly shadowing targets. Deallocate each shadow target.
2655 for (int i = 0; i < shadows.length(); i++) {
2656 if (has_valid_frame() && shadows[i]->is_bound()) {
2657 JumpTarget* original = shadows[i]->other_target();
2658 __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
2659 if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002660 JumpTarget skip;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002661 skip.Branch(ne);
2662 frame_->PrepareForReturn();
2663 original->Jump();
2664 skip.Bind();
2665 } else {
2666 original->Branch(eq);
2667 }
2668 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002669 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002670
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002671 if (has_valid_frame()) {
2672 // Check if we need to rethrow the exception.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002673 JumpTarget exit;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002674 __ cmp(r2, Operand(Smi::FromInt(THROWING)));
2675 exit.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002676
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002677 // Rethrow exception.
2678 frame_->EmitPush(r0);
2679 frame_->CallRuntime(Runtime::kReThrow, 1);
2680
2681 // Done.
2682 exit.Bind();
2683 }
2684 ASSERT(!has_valid_frame() || frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685}
2686
2687
ager@chromium.org7c537e22008-10-16 08:43:32 +00002688void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002689#ifdef DEBUG
2690 int original_height = frame_->height();
2691#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002692 Comment cmnt(masm_, "[ DebuggerStatament");
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002693 CodeForStatementPosition(node);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002694#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5c838252010-02-19 08:53:10 +00002695 frame_->DebugBreak();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002696#endif
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00002697 // Ignore the return value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002698 ASSERT(frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002699}
2700
2701
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002702void CodeGenerator::InstantiateFunction(
2703 Handle<SharedFunctionInfo> function_info) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002704 // Use the fast case closure allocation code that allocates in new
2705 // space for nested functions that don't need literals cloning.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002706 if (scope()->is_function_scope() && function_info->num_literals() == 0) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002707 FastNewClosureStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002708 frame_->EmitPush(Operand(function_info));
2709 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002710 frame_->CallStub(&stub, 1);
2711 frame_->EmitPush(r0);
2712 } else {
2713 // Create a new closure.
2714 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002715 frame_->EmitPush(Operand(function_info));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002716 frame_->CallRuntime(Runtime::kNewClosure, 2);
2717 frame_->EmitPush(r0);
2718 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002719}
2720
2721
ager@chromium.org7c537e22008-10-16 08:43:32 +00002722void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002723#ifdef DEBUG
2724 int original_height = frame_->height();
2725#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726 Comment cmnt(masm_, "[ FunctionLiteral");
2727
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002728 // Build the function info and instantiate it.
2729 Handle<SharedFunctionInfo> function_info =
2730 Compiler::BuildFunctionInfo(node, script(), this);
kasper.lund212ac232008-07-16 07:07:30 +00002731 // Check for stack-overflow exception.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002732 if (HasStackOverflow()) {
2733 ASSERT(frame_->height() == original_height);
2734 return;
2735 }
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002736 InstantiateFunction(function_info);
ager@chromium.orgac091b72010-05-05 07:34:42 +00002737 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002738}
2739
2740
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002741void CodeGenerator::VisitSharedFunctionInfoLiteral(
2742 SharedFunctionInfoLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002743#ifdef DEBUG
2744 int original_height = frame_->height();
2745#endif
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00002746 Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
2747 InstantiateFunction(node->shared_function_info());
ager@chromium.orgac091b72010-05-05 07:34:42 +00002748 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002749}
2750
2751
ager@chromium.org7c537e22008-10-16 08:43:32 +00002752void CodeGenerator::VisitConditional(Conditional* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002753#ifdef DEBUG
2754 int original_height = frame_->height();
2755#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00002756 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002757 Comment cmnt(masm_, "[ Conditional");
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002758 JumpTarget then;
2759 JumpTarget else_;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002760 LoadCondition(node->condition(), &then, &else_, true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002761 if (has_valid_frame()) {
2762 Branch(false, &else_);
2763 }
2764 if (has_valid_frame() || then.is_linked()) {
2765 then.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002766 Load(node->then_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002767 }
2768 if (else_.is_linked()) {
2769 JumpTarget exit;
2770 if (has_valid_frame()) exit.Jump();
2771 else_.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002772 Load(node->else_expression());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002773 if (exit.is_linked()) exit.Bind();
2774 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00002775 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002776}
2777
2778
ager@chromium.org7c537e22008-10-16 08:43:32 +00002779void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
2780 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002781 ASSERT(slot->var()->is_dynamic());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002782
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002783 // JumpTargets do not yet support merging frames so the frame must be
2784 // spilled when jumping to these targets.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002785 JumpTarget slow;
2786 JumpTarget done;
ager@chromium.org381abbb2009-02-25 13:23:22 +00002787
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00002788 // Generate fast case for loading from slots that correspond to
2789 // local/global variables or arguments unless they are shadowed by
2790 // eval-introduced bindings.
2791 EmitDynamicLoadFromSlotFastCase(slot,
2792 typeof_state,
2793 &slow,
2794 &done);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002795
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002796 slow.Bind();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002797 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002798 frame_->EmitPush(cp);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002799 __ mov(r0, Operand(slot->var()->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002800 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002801
ager@chromium.org7c537e22008-10-16 08:43:32 +00002802 if (typeof_state == INSIDE_TYPEOF) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002803 frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002804 } else {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002805 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002806 }
ager@chromium.org381abbb2009-02-25 13:23:22 +00002807
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002808 done.Bind();
2809 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002810
2811 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00002812 Register scratch = VirtualFrame::scratch0();
2813 frame_->EmitPush(SlotOperand(slot, scratch));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002814 if (slot->var()->mode() == Variable::CONST) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00002815 // Const slots may contain 'the hole' value (the constant hasn't been
2816 // initialized yet) which needs to be converted into the 'undefined'
2817 // value.
2818 Comment cmnt(masm_, "[ Unhole const");
ager@chromium.org357bf652010-04-12 11:30:10 +00002819 frame_->EmitPop(scratch);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002820 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00002821 __ cmp(scratch, ip);
2822 __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex, eq);
2823 frame_->EmitPush(scratch);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002824 }
2825 }
2826}
2827
2828
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002829void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
2830 TypeofState state) {
2831 LoadFromSlot(slot, state);
2832
2833 // Bail out quickly if we're not using lazy arguments allocation.
2834 if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
2835
2836 // ... or if the slot isn't a non-parameter arguments slot.
2837 if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
2838
2839 VirtualFrame::SpilledScope spilled_scope(frame_);
2840
2841 // Load the loaded value from the stack into r0 but leave it on the
2842 // stack.
2843 __ ldr(r0, MemOperand(sp, 0));
2844
2845 // If the loaded value is the sentinel that indicates that we
2846 // haven't loaded the arguments object yet, we need to do it now.
2847 JumpTarget exit;
2848 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
2849 __ cmp(r0, ip);
2850 exit.Branch(ne);
2851 frame_->Drop();
2852 StoreArgumentsObject(false);
2853 exit.Bind();
2854}
2855
2856
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002857void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
2858 ASSERT(slot != NULL);
2859 if (slot->type() == Slot::LOOKUP) {
ager@chromium.org357bf652010-04-12 11:30:10 +00002860 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002861 ASSERT(slot->var()->is_dynamic());
2862
2863 // For now, just do a runtime call.
2864 frame_->EmitPush(cp);
2865 __ mov(r0, Operand(slot->var()->name()));
2866 frame_->EmitPush(r0);
2867
2868 if (init_state == CONST_INIT) {
2869 // Same as the case for a normal store, but ignores attribute
2870 // (e.g. READ_ONLY) of context slot so that we can initialize
2871 // const properties (introduced via eval("const foo = (some
2872 // expr);")). Also, uses the current function context instead of
2873 // the top context.
2874 //
2875 // Note that we must declare the foo upon entry of eval(), via a
2876 // context slot declaration, but we cannot initialize it at the
2877 // same time, because the const declaration may be at the end of
2878 // the eval code (sigh...) and the const variable may have been
2879 // used before (where its value is 'undefined'). Thus, we can only
2880 // do the initialization when we actually encounter the expression
2881 // and when the expression operands are defined and valid, and
2882 // thus we need the split into 2 operations: declaration of the
2883 // context slot followed by initialization.
2884 frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
2885 } else {
2886 frame_->CallRuntime(Runtime::kStoreContextSlot, 3);
2887 }
2888 // Storing a variable must keep the (new) value on the expression
2889 // stack. This is necessary for compiling assignment expressions.
2890 frame_->EmitPush(r0);
2891
2892 } else {
2893 ASSERT(!slot->var()->is_dynamic());
ager@chromium.org357bf652010-04-12 11:30:10 +00002894 Register scratch = VirtualFrame::scratch0();
2895 VirtualFrame::RegisterAllocationScope scope(this);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002896
ager@chromium.org357bf652010-04-12 11:30:10 +00002897 // The frame must be spilled when branching to this target.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002898 JumpTarget exit;
ager@chromium.org357bf652010-04-12 11:30:10 +00002899
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002900 if (init_state == CONST_INIT) {
2901 ASSERT(slot->var()->mode() == Variable::CONST);
2902 // Only the first const initialization must be executed (the slot
2903 // still contains 'the hole' value). When the assignment is
2904 // executed, the code is identical to a normal store (see below).
2905 Comment cmnt(masm_, "[ Init const");
ager@chromium.org357bf652010-04-12 11:30:10 +00002906 __ ldr(scratch, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002907 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00002908 __ cmp(scratch, ip);
2909 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002910 exit.Branch(ne);
2911 }
2912
2913 // We must execute the store. Storing a variable must keep the
2914 // (new) value on the stack. This is necessary for compiling
2915 // assignment expressions.
2916 //
2917 // Note: We will reach here even with slot->var()->mode() ==
2918 // Variable::CONST because of const declarations which will
2919 // initialize consts to 'the hole' value and by doing so, end up
2920 // calling this code. r2 may be loaded with context; used below in
2921 // RecordWrite.
ager@chromium.org357bf652010-04-12 11:30:10 +00002922 Register tos = frame_->Peek();
2923 __ str(tos, SlotOperand(slot, scratch));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002924 if (slot->type() == Slot::CONTEXT) {
2925 // Skip write barrier if the written value is a smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00002926 __ tst(tos, Operand(kSmiTagMask));
2927 // We don't use tos any more after here.
2928 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002929 exit.Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00002930 // scratch is loaded with context when calling SlotOperand above.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002931 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
2932 __ mov(r3, Operand(offset));
ager@chromium.org357bf652010-04-12 11:30:10 +00002933 // r1 could be identical with tos, but that doesn't matter.
2934 __ RecordWrite(scratch, r3, r1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002935 }
2936 // If we definitely did not jump over the assignment, we do not need
2937 // to bind the exit label. Doing so can defeat peephole
2938 // optimization.
2939 if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
ager@chromium.org357bf652010-04-12 11:30:10 +00002940 frame_->SpillAll();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00002941 exit.Bind();
2942 }
2943 }
2944}
2945
2946
ager@chromium.org381abbb2009-02-25 13:23:22 +00002947void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
2948 TypeofState typeof_state,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002949 JumpTarget* slow) {
ager@chromium.org381abbb2009-02-25 13:23:22 +00002950 // Check that no extension objects have been created by calls to
2951 // eval from the current scope to the global scope.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002952 Register tmp = frame_->scratch0();
2953 Register tmp2 = frame_->scratch1();
ager@chromium.org381abbb2009-02-25 13:23:22 +00002954 Register context = cp;
2955 Scope* s = scope();
2956 while (s != NULL) {
2957 if (s->num_heap_slots() > 0) {
2958 if (s->calls_eval()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002959 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00002960 // Check that extension is NULL.
2961 __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
2962 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002963 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002964 }
2965 // Load next context in chain.
2966 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
2967 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
2968 context = tmp;
2969 }
2970 // If no outer scope calls eval, we do not need to check more
2971 // context extensions.
2972 if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
2973 s = s->outer_scope();
2974 }
2975
2976 if (s->is_eval_scope()) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00002977 frame_->SpillAll();
ager@chromium.org381abbb2009-02-25 13:23:22 +00002978 Label next, fast;
ager@chromium.org357bf652010-04-12 11:30:10 +00002979 __ Move(tmp, context);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002980 __ bind(&next);
2981 // Terminate at global context.
2982 __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00002983 __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
2984 __ cmp(tmp2, ip);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002985 __ b(eq, &fast);
2986 // Check that extension is NULL.
2987 __ ldr(tmp2, ContextOperand(tmp, Context::EXTENSION_INDEX));
2988 __ tst(tmp2, tmp2);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002989 slow->Branch(ne);
ager@chromium.org381abbb2009-02-25 13:23:22 +00002990 // Load next context in chain.
2991 __ ldr(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
2992 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
2993 __ b(&next);
2994 __ bind(&fast);
2995 }
2996
ager@chromium.org381abbb2009-02-25 13:23:22 +00002997 // Load the global object.
2998 LoadGlobal();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00002999 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00003000 frame_->CallLoadIC(slot->var()->name(),
3001 typeof_state == INSIDE_TYPEOF
3002 ? RelocInfo::CODE_TARGET
3003 : RelocInfo::CODE_TARGET_CONTEXT);
ager@chromium.org381abbb2009-02-25 13:23:22 +00003004}
3005
3006
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003007void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
3008 TypeofState typeof_state,
3009 JumpTarget* slow,
3010 JumpTarget* done) {
3011 // Generate fast-case code for variables that might be shadowed by
3012 // eval-introduced variables. Eval is used a lot without
3013 // introducing variables. In those cases, we do not want to
3014 // perform a runtime call for all variables in the scope
3015 // containing the eval.
3016 if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
3017 LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
3018 frame_->SpillAll();
3019 done->Jump();
3020
3021 } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
3022 frame_->SpillAll();
3023 Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
3024 Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
3025 if (potential_slot != NULL) {
3026 // Generate fast case for locals that rewrite to slots.
3027 __ ldr(r0,
3028 ContextSlotOperandCheckExtensions(potential_slot,
3029 r1,
3030 r2,
3031 slow));
3032 if (potential_slot->var()->mode() == Variable::CONST) {
3033 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
3034 __ cmp(r0, ip);
3035 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
3036 }
3037 done->Jump();
3038 } else if (rewrite != NULL) {
3039 // Generate fast case for argument loads.
3040 Property* property = rewrite->AsProperty();
3041 if (property != NULL) {
3042 VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
3043 Literal* key_literal = property->key()->AsLiteral();
3044 if (obj_proxy != NULL &&
3045 key_literal != NULL &&
3046 obj_proxy->IsArguments() &&
3047 key_literal->handle()->IsSmi()) {
3048 // Load arguments object if there are no eval-introduced
3049 // variables. Then load the argument from the arguments
3050 // object using keyed load.
3051 __ ldr(r0,
3052 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
3053 r1,
3054 r2,
3055 slow));
3056 frame_->EmitPush(r0);
3057 __ mov(r1, Operand(key_literal->handle()));
3058 frame_->EmitPush(r1);
3059 EmitKeyedLoad();
3060 done->Jump();
3061 }
3062 }
3063 }
3064 }
3065}
3066
3067
ager@chromium.org7c537e22008-10-16 08:43:32 +00003068void CodeGenerator::VisitSlot(Slot* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003069#ifdef DEBUG
3070 int original_height = frame_->height();
3071#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003072 Comment cmnt(masm_, "[ Slot");
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003073 LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003074 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org7c537e22008-10-16 08:43:32 +00003075}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003076
ager@chromium.org7c537e22008-10-16 08:43:32 +00003077
3078void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003079#ifdef DEBUG
3080 int original_height = frame_->height();
3081#endif
ager@chromium.org7c537e22008-10-16 08:43:32 +00003082 Comment cmnt(masm_, "[ VariableProxy");
3083
3084 Variable* var = node->var();
3085 Expression* expr = var->rewrite();
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003086 if (expr != NULL) {
3087 Visit(expr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003088 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003089 ASSERT(var->is_global());
3090 Reference ref(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003091 ref.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003092 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003093 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003094}
3095
3096
ager@chromium.org7c537e22008-10-16 08:43:32 +00003097void CodeGenerator::VisitLiteral(Literal* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003098#ifdef DEBUG
3099 int original_height = frame_->height();
3100#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003101 Comment cmnt(masm_, "[ Literal");
ager@chromium.org357bf652010-04-12 11:30:10 +00003102 Register reg = frame_->GetTOSRegister();
3103 __ mov(reg, Operand(node->handle()));
3104 frame_->EmitPush(reg);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003105 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003106}
3107
3108
ager@chromium.org7c537e22008-10-16 08:43:32 +00003109void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003110#ifdef DEBUG
3111 int original_height = frame_->height();
3112#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003113 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003114 Comment cmnt(masm_, "[ RexExp Literal");
3115
3116 // Retrieve the literal array and check the allocated entry.
3117
3118 // Load the function of this activation.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003119 __ ldr(r1, frame_->Function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003120
3121 // Load the literals array of the function.
3122 __ ldr(r1, FieldMemOperand(r1, JSFunction::kLiteralsOffset));
3123
3124 // Load the literal at the ast saved index.
3125 int literal_offset =
3126 FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
3127 __ ldr(r2, FieldMemOperand(r1, literal_offset));
3128
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003129 JumpTarget done;
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003130 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
3131 __ cmp(r2, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003132 done.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003133
3134 // If the entry is undefined we call the runtime system to computed
3135 // the literal.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003136 frame_->EmitPush(r1); // literal array (0)
mads.s.ager31e71382008-08-13 09:32:07 +00003137 __ mov(r0, Operand(Smi::FromInt(node->literal_index())));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003138 frame_->EmitPush(r0); // literal index (1)
mads.s.ager31e71382008-08-13 09:32:07 +00003139 __ mov(r0, Operand(node->pattern())); // RegExp pattern (2)
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003140 frame_->EmitPush(r0);
mads.s.ager31e71382008-08-13 09:32:07 +00003141 __ mov(r0, Operand(node->flags())); // RegExp flags (3)
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003142 frame_->EmitPush(r0);
3143 frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
mads.s.ager31e71382008-08-13 09:32:07 +00003144 __ mov(r2, Operand(r0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003145
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003146 done.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003147 // Push the literal.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003148 frame_->EmitPush(r2);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003149 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003150}
3151
3152
ager@chromium.org7c537e22008-10-16 08:43:32 +00003153void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003154#ifdef DEBUG
3155 int original_height = frame_->height();
3156#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003157 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003158 Comment cmnt(masm_, "[ ObjectLiteral");
3159
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003160 // Load the function of this activation.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003161 __ ldr(r3, frame_->Function());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003162 // Literal array.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003163 __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003164 // Literal index.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003165 __ mov(r2, Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003166 // Constant properties.
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003167 __ mov(r1, Operand(node->constant_properties()));
3168 // Should the object literal have fast elements?
3169 __ mov(r0, Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
3170 frame_->EmitPushMultiple(4, r3.bit() | r2.bit() | r1.bit() | r0.bit());
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003171 if (node->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003172 frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003173 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00003174 frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003175 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003176 frame_->EmitPush(r0); // save the result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003177 for (int i = 0; i < node->properties()->length(); i++) {
ager@chromium.org5c838252010-02-19 08:53:10 +00003178 // At the start of each iteration, the top of stack contains
3179 // the newly created object literal.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003180 ObjectLiteral::Property* property = node->properties()->at(i);
3181 Literal* key = property->key();
3182 Expression* value = property->value();
3183 switch (property->kind()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003184 case ObjectLiteral::Property::CONSTANT:
3185 break;
3186 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
3187 if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
3188 // else fall through
ager@chromium.org5c838252010-02-19 08:53:10 +00003189 case ObjectLiteral::Property::COMPUTED:
3190 if (key->handle()->IsSymbol()) {
3191 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003192 Load(value);
ager@chromium.org5c838252010-02-19 08:53:10 +00003193 frame_->EmitPop(r0);
3194 __ mov(r2, Operand(key->handle()));
3195 __ ldr(r1, frame_->Top()); // Load the receiver.
3196 frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
3197 break;
3198 }
3199 // else fall through
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 case ObjectLiteral::Property::PROTOTYPE: {
ager@chromium.org5c838252010-02-19 08:53:10 +00003201 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003202 frame_->EmitPush(r0); // dup the result
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003203 Load(key);
3204 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003205 frame_->CallRuntime(Runtime::kSetProperty, 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003206 break;
3207 }
3208 case ObjectLiteral::Property::SETTER: {
ager@chromium.org5c838252010-02-19 08:53:10 +00003209 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003210 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003211 Load(key);
mads.s.ager31e71382008-08-13 09:32:07 +00003212 __ mov(r0, Operand(Smi::FromInt(1)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003213 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003214 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003215 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003216 break;
3217 }
3218 case ObjectLiteral::Property::GETTER: {
ager@chromium.org5c838252010-02-19 08:53:10 +00003219 __ ldr(r0, frame_->Top());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003220 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003221 Load(key);
mads.s.ager31e71382008-08-13 09:32:07 +00003222 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003223 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003224 Load(value);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003225 frame_->CallRuntime(Runtime::kDefineAccessor, 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003226 break;
3227 }
3228 }
3229 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003230 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003231}
3232
3233
ager@chromium.org7c537e22008-10-16 08:43:32 +00003234void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003235#ifdef DEBUG
3236 int original_height = frame_->height();
3237#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00003238 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003239 Comment cmnt(masm_, "[ ArrayLiteral");
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003240
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003241 // Load the function of this activation.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003242 __ ldr(r2, frame_->Function());
ager@chromium.org5c838252010-02-19 08:53:10 +00003243 // Load the literals array of the function.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003244 __ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003245 __ mov(r1, Operand(Smi::FromInt(node->literal_index())));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003246 __ mov(r0, Operand(node->constant_elements()));
3247 frame_->EmitPushMultiple(3, r2.bit() | r1.bit() | r0.bit());
ager@chromium.org5c838252010-02-19 08:53:10 +00003248 int length = node->values()->length();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003249 if (node->depth() > 1) {
3250 frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003251 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003252 frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
ager@chromium.org5c838252010-02-19 08:53:10 +00003253 } else {
3254 FastCloneShallowArrayStub stub(length);
3255 frame_->CallStub(&stub, 3);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003256 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003257 frame_->EmitPush(r0); // save the result
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003258 // r0: created object literal
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003259
3260 // Generate code to set the elements in the array that are not
3261 // literals.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003262 for (int i = 0; i < node->values()->length(); i++) {
3263 Expression* value = node->values()->at(i);
3264
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003265 // If value is a literal the property value is already set in the
3266 // boilerplate object.
3267 if (value->AsLiteral() != NULL) continue;
3268 // If value is a materialized literal the property value is already set
3269 // in the boilerplate object if it is simple.
3270 if (CompileTimeValue::IsCompileTimeValue(value)) continue;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003271
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003272 // The property must be set by generated code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003273 Load(value);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003274 frame_->EmitPop(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003275
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003276 // Fetch the object literal.
3277 __ ldr(r1, frame_->Top());
3278 // Get the elements array.
3279 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003281 // Write to the indexed properties array.
kasperl@chromium.orge959c182009-07-27 08:59:04 +00003282 int offset = i * kPointerSize + FixedArray::kHeaderSize;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003283 __ str(r0, FieldMemOperand(r1, offset));
3284
3285 // Update the write barrier for the array address.
3286 __ mov(r3, Operand(offset));
3287 __ RecordWrite(r1, r3, r2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003288 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003289 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003290}
3291
3292
ager@chromium.org32912102009-01-16 10:38:43 +00003293void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003294#ifdef DEBUG
3295 int original_height = frame_->height();
3296#endif
ager@chromium.org32912102009-01-16 10:38:43 +00003297 // Call runtime routine to allocate the catch extension object and
3298 // assign the exception value to the catch variable.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003299 Comment cmnt(masm_, "[ CatchExtensionObject");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003300 Load(node->key());
3301 Load(node->value());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003302 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
3303 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003304 ASSERT_EQ(original_height + 1, frame_->height());
3305}
3306
3307
3308void CodeGenerator::EmitSlotAssignment(Assignment* node) {
3309#ifdef DEBUG
3310 int original_height = frame_->height();
3311#endif
3312 Comment cmnt(masm(), "[ Variable Assignment");
3313 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3314 ASSERT(var != NULL);
3315 Slot* slot = var->slot();
3316 ASSERT(slot != NULL);
3317
3318 // Evaluate the right-hand side.
3319 if (node->is_compound()) {
3320 // For a compound assignment the right-hand side is a binary operation
3321 // between the current property value and the actual right-hand side.
3322 LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
3323
3324 // Perform the binary operation.
3325 Literal* literal = node->value()->AsLiteral();
3326 bool overwrite_value =
3327 (node->value()->AsBinaryOperation() != NULL &&
3328 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3329 if (literal != NULL && literal->handle()->IsSmi()) {
3330 SmiOperation(node->binary_op(),
3331 literal->handle(),
3332 false,
3333 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3334 } else {
3335 Load(node->value());
3336 VirtualFrameBinaryOperation(
3337 node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3338 }
3339 } else {
3340 Load(node->value());
3341 }
3342
3343 // Perform the assignment.
3344 if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
3345 CodeForSourcePosition(node->position());
3346 StoreToSlot(slot,
3347 node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
3348 }
3349 ASSERT_EQ(original_height + 1, frame_->height());
3350}
3351
3352
3353void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
3354#ifdef DEBUG
3355 int original_height = frame_->height();
3356#endif
3357 Comment cmnt(masm(), "[ Named Property Assignment");
3358 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3359 Property* prop = node->target()->AsProperty();
3360 ASSERT(var == NULL || (prop == NULL && var->is_global()));
3361
3362 // Initialize name and evaluate the receiver sub-expression if necessary. If
3363 // the receiver is trivial it is not placed on the stack at this point, but
3364 // loaded whenever actually needed.
3365 Handle<String> name;
3366 bool is_trivial_receiver = false;
3367 if (var != NULL) {
3368 name = var->name();
3369 } else {
3370 Literal* lit = prop->key()->AsLiteral();
3371 ASSERT_NOT_NULL(lit);
3372 name = Handle<String>::cast(lit->handle());
3373 // Do not materialize the receiver on the frame if it is trivial.
3374 is_trivial_receiver = prop->obj()->IsTrivial();
3375 if (!is_trivial_receiver) Load(prop->obj());
3376 }
3377
3378 // Change to slow case in the beginning of an initialization block to
3379 // avoid the quadratic behavior of repeatedly adding fast properties.
3380 if (node->starts_initialization_block()) {
3381 // Initialization block consists of assignments of the form expr.x = ..., so
3382 // this will never be an assignment to a variable, so there must be a
3383 // receiver object.
3384 ASSERT_EQ(NULL, var);
3385 if (is_trivial_receiver) {
3386 Load(prop->obj());
3387 } else {
3388 frame_->Dup();
3389 }
3390 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3391 }
3392
3393 // Change to fast case at the end of an initialization block. To prepare for
3394 // that add an extra copy of the receiver to the frame, so that it can be
3395 // converted back to fast case after the assignment.
3396 if (node->ends_initialization_block() && !is_trivial_receiver) {
3397 frame_->Dup();
3398 }
3399
3400 // Stack layout:
3401 // [tos] : receiver (only materialized if non-trivial)
3402 // [tos+1] : receiver if at the end of an initialization block
3403
3404 // Evaluate the right-hand side.
3405 if (node->is_compound()) {
3406 // For a compound assignment the right-hand side is a binary operation
3407 // between the current property value and the actual right-hand side.
3408 if (is_trivial_receiver) {
3409 Load(prop->obj());
3410 } else if (var != NULL) {
3411 LoadGlobal();
3412 } else {
3413 frame_->Dup();
3414 }
3415 EmitNamedLoad(name, var != NULL);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003416
3417 // Perform the binary operation.
3418 Literal* literal = node->value()->AsLiteral();
3419 bool overwrite_value =
3420 (node->value()->AsBinaryOperation() != NULL &&
3421 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3422 if (literal != NULL && literal->handle()->IsSmi()) {
3423 SmiOperation(node->binary_op(),
3424 literal->handle(),
3425 false,
3426 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3427 } else {
3428 Load(node->value());
3429 VirtualFrameBinaryOperation(
3430 node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3431 }
3432 } else {
3433 // For non-compound assignment just load the right-hand side.
3434 Load(node->value());
3435 }
3436
3437 // Stack layout:
3438 // [tos] : value
3439 // [tos+1] : receiver (only materialized if non-trivial)
3440 // [tos+2] : receiver if at the end of an initialization block
3441
3442 // Perform the assignment. It is safe to ignore constants here.
3443 ASSERT(var == NULL || var->mode() != Variable::CONST);
3444 ASSERT_NE(Token::INIT_CONST, node->op());
3445 if (is_trivial_receiver) {
3446 // Load the receiver and swap with the value.
3447 Load(prop->obj());
3448 Register t0 = frame_->PopToRegister();
3449 Register t1 = frame_->PopToRegister(t0);
3450 frame_->EmitPush(t0);
3451 frame_->EmitPush(t1);
3452 }
3453 CodeForSourcePosition(node->position());
3454 bool is_contextual = (var != NULL);
3455 EmitNamedStore(name, is_contextual);
3456 frame_->EmitPush(r0);
3457
3458 // Change to fast case at the end of an initialization block.
3459 if (node->ends_initialization_block()) {
3460 ASSERT_EQ(NULL, var);
3461 // The argument to the runtime call is the receiver.
3462 if (is_trivial_receiver) {
3463 Load(prop->obj());
3464 } else {
3465 // A copy of the receiver is below the value of the assignment. Swap
3466 // the receiver and the value of the assignment expression.
3467 Register t0 = frame_->PopToRegister();
3468 Register t1 = frame_->PopToRegister(t0);
3469 frame_->EmitPush(t0);
3470 frame_->EmitPush(t1);
3471 }
3472 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3473 }
3474
3475 // Stack layout:
3476 // [tos] : result
3477
3478 ASSERT_EQ(original_height + 1, frame_->height());
3479}
3480
3481
3482void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
3483#ifdef DEBUG
3484 int original_height = frame_->height();
3485#endif
3486 Comment cmnt(masm_, "[ Keyed Property Assignment");
3487 Property* prop = node->target()->AsProperty();
3488 ASSERT_NOT_NULL(prop);
3489
3490 // Evaluate the receiver subexpression.
3491 Load(prop->obj());
3492
3493 // Change to slow case in the beginning of an initialization block to
3494 // avoid the quadratic behavior of repeatedly adding fast properties.
3495 if (node->starts_initialization_block()) {
3496 frame_->Dup();
3497 frame_->CallRuntime(Runtime::kToSlowProperties, 1);
3498 }
3499
3500 // Change to fast case at the end of an initialization block. To prepare for
3501 // that add an extra copy of the receiver to the frame, so that it can be
3502 // converted back to fast case after the assignment.
3503 if (node->ends_initialization_block()) {
3504 frame_->Dup();
3505 }
3506
3507 // Evaluate the key subexpression.
3508 Load(prop->key());
3509
3510 // Stack layout:
3511 // [tos] : key
3512 // [tos+1] : receiver
3513 // [tos+2] : receiver if at the end of an initialization block
3514
3515 // Evaluate the right-hand side.
3516 if (node->is_compound()) {
3517 // For a compound assignment the right-hand side is a binary operation
3518 // between the current property value and the actual right-hand side.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003519 // Duplicate receiver and key for loading the current property value.
3520 frame_->Dup2();
ager@chromium.orgac091b72010-05-05 07:34:42 +00003521 EmitKeyedLoad();
3522 frame_->EmitPush(r0);
3523
3524 // Perform the binary operation.
3525 Literal* literal = node->value()->AsLiteral();
3526 bool overwrite_value =
3527 (node->value()->AsBinaryOperation() != NULL &&
3528 node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
3529 if (literal != NULL && literal->handle()->IsSmi()) {
3530 SmiOperation(node->binary_op(),
3531 literal->handle(),
3532 false,
3533 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3534 } else {
3535 Load(node->value());
3536 VirtualFrameBinaryOperation(
3537 node->binary_op(), overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
3538 }
3539 } else {
3540 // For non-compound assignment just load the right-hand side.
3541 Load(node->value());
3542 }
3543
3544 // Stack layout:
3545 // [tos] : value
3546 // [tos+1] : key
3547 // [tos+2] : receiver
3548 // [tos+3] : receiver if at the end of an initialization block
3549
3550 // Perform the assignment. It is safe to ignore constants here.
3551 ASSERT(node->op() != Token::INIT_CONST);
3552 CodeForSourcePosition(node->position());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003553 EmitKeyedStore(prop->key()->type());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003554 frame_->EmitPush(r0);
3555
3556 // Stack layout:
3557 // [tos] : result
3558 // [tos+1] : receiver if at the end of an initialization block
3559
3560 // Change to fast case at the end of an initialization block.
3561 if (node->ends_initialization_block()) {
3562 // The argument to the runtime call is the extra copy of the receiver,
3563 // which is below the value of the assignment. Swap the receiver and
3564 // the value of the assignment expression.
3565 Register t0 = frame_->PopToRegister();
3566 Register t1 = frame_->PopToRegister(t0);
3567 frame_->EmitPush(t1);
3568 frame_->EmitPush(t0);
3569 frame_->CallRuntime(Runtime::kToFastProperties, 1);
3570 }
3571
3572 // Stack layout:
3573 // [tos] : result
3574
3575 ASSERT_EQ(original_height + 1, frame_->height());
ager@chromium.org32912102009-01-16 10:38:43 +00003576}
3577
3578
ager@chromium.org7c537e22008-10-16 08:43:32 +00003579void CodeGenerator::VisitAssignment(Assignment* node) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003580 VirtualFrame::RegisterAllocationScope scope(this);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003581#ifdef DEBUG
3582 int original_height = frame_->height();
3583#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003584 Comment cmnt(masm_, "[ Assignment");
mads.s.ager31e71382008-08-13 09:32:07 +00003585
ager@chromium.orgac091b72010-05-05 07:34:42 +00003586 Variable* var = node->target()->AsVariableProxy()->AsVariable();
3587 Property* prop = node->target()->AsProperty();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003588
ager@chromium.orgac091b72010-05-05 07:34:42 +00003589 if (var != NULL && !var->is_global()) {
3590 EmitSlotAssignment(node);
mads.s.ager31e71382008-08-13 09:32:07 +00003591
ager@chromium.orgac091b72010-05-05 07:34:42 +00003592 } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
3593 (var != NULL && var->is_global())) {
3594 // Properties whose keys are property names and global variables are
3595 // treated as named property references. We do not need to consider
3596 // global 'this' because it is not a valid left-hand side.
3597 EmitNamedPropertyAssignment(node);
3598
3599 } else if (prop != NULL) {
3600 // Other properties (including rewritten parameters for a function that
3601 // uses arguments) are keyed property assignments.
3602 EmitKeyedPropertyAssignment(node);
3603
3604 } else {
3605 // Invalid left-hand side.
3606 Load(node->target());
3607 frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
3608 // The runtime call doesn't actually return but the code generator will
3609 // still generate code and expects a certain frame height.
3610 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003611 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003612 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003613}
3614
3615
ager@chromium.org7c537e22008-10-16 08:43:32 +00003616void CodeGenerator::VisitThrow(Throw* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003617#ifdef DEBUG
3618 int original_height = frame_->height();
3619#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003620 Comment cmnt(masm_, "[ Throw");
3621
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003622 Load(node->exception());
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003623 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003624 frame_->CallRuntime(Runtime::kThrow, 1);
3625 frame_->EmitPush(r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00003626 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003627}
3628
3629
ager@chromium.org7c537e22008-10-16 08:43:32 +00003630void CodeGenerator::VisitProperty(Property* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003631#ifdef DEBUG
3632 int original_height = frame_->height();
3633#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003634 Comment cmnt(masm_, "[ Property");
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003635
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003636 { Reference property(this, node);
ager@chromium.org357bf652010-04-12 11:30:10 +00003637 property.GetValue();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003638 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003639 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003640}
3641
3642
ager@chromium.org7c537e22008-10-16 08:43:32 +00003643void CodeGenerator::VisitCall(Call* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003644#ifdef DEBUG
3645 int original_height = frame_->height();
3646#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003647 Comment cmnt(masm_, "[ Call");
3648
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003649 Expression* function = node->expression();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003650 ZoneList<Expression*>* args = node->arguments();
3651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003652 // Standard function call.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003653 // Check if the function is a variable or a property.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654 Variable* var = function->AsVariableProxy()->AsVariable();
3655 Property* property = function->AsProperty();
3656
3657 // ------------------------------------------------------------------------
3658 // Fast-case: Use inline caching.
3659 // ---
3660 // According to ECMA-262, section 11.2.3, page 44, the function to call
3661 // must be resolved after the arguments have been evaluated. The IC code
3662 // automatically handles this by loading the arguments before the function
3663 // is resolved in cache misses (this also holds for megamorphic calls).
3664 // ------------------------------------------------------------------------
3665
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003666 if (var != NULL && var->is_possibly_eval()) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003667 VirtualFrame::SpilledScope spilled_scope(frame_);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003668 // ----------------------------------
3669 // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
3670 // ----------------------------------
3671
3672 // In a call to eval, we first call %ResolvePossiblyDirectEval to
3673 // resolve the function we need to call and the receiver of the
3674 // call. Then we call the resolved function using the given
3675 // arguments.
3676 // Prepare stack for call to resolved function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003677 Load(function);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003678 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
3679 frame_->EmitPush(r2); // Slot for receiver
3680 int arg_count = args->length();
3681 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003682 Load(args->at(i));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003683 }
3684
3685 // Prepare stack for call to ResolvePossiblyDirectEval.
3686 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize + kPointerSize));
3687 frame_->EmitPush(r1);
3688 if (arg_count > 0) {
3689 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
3690 frame_->EmitPush(r1);
3691 } else {
3692 frame_->EmitPush(r2);
3693 }
3694
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003695 // Push the receiver.
3696 __ ldr(r1, frame_->Receiver());
3697 frame_->EmitPush(r1);
3698
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003699 // Resolve the call.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003700 frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003701
3702 // Touch up stack with the right values for the function and the receiver.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00003703 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003704 __ str(r1, MemOperand(sp, arg_count * kPointerSize));
3705
3706 // Call the function.
3707 CodeForSourcePosition(node->position());
3708
3709 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003710 CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003711 frame_->CallStub(&call_function, arg_count + 1);
3712
3713 __ ldr(cp, frame_->Context());
3714 // Remove the function from the stack.
3715 frame_->Drop();
3716 frame_->EmitPush(r0);
3717
3718 } else if (var != NULL && !var->is_this() && var->is_global()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719 // ----------------------------------
3720 // JavaScript example: 'foo(1, 2, 3)' // foo is global
3721 // ----------------------------------
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003722 // Pass the global object as the receiver and let the IC stub
3723 // patch the stack to use the global proxy as 'this' in the
3724 // invoked function.
3725 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003726
3727 // Load the arguments.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003728 int arg_count = args->length();
3729 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003730 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003731 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003732
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003733 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.org5c838252010-02-19 08:53:10 +00003734 // Setup the name register and call the IC initialization code.
3735 __ mov(r2, Operand(var->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00003736 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3737 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003738 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003739 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
3740 arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003741 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003742 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003743
3744 } else if (var != NULL && var->slot() != NULL &&
3745 var->slot()->type() == Slot::LOOKUP) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003746 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003747 // ----------------------------------
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003748 // JavaScript examples:
3749 //
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003750 // with (obj) foo(1, 2, 3) // foo may be in obj.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003751 //
3752 // function f() {};
3753 // function g() {
3754 // eval(...);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003755 // f(); // f could be in extension object.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003756 // }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003757 // ----------------------------------
3758
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003759 // JumpTargets do not yet support merging frames so the frame must be
3760 // spilled when jumping to these targets.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003761 JumpTarget slow, done;
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003762
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003763 // Generate fast case for loading functions from slots that
3764 // correspond to local/global variables or arguments unless they
3765 // are shadowed by eval-introduced bindings.
3766 EmitDynamicLoadFromSlotFastCase(var->slot(),
3767 NOT_INSIDE_TYPEOF,
3768 &slow,
3769 &done);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003770
3771 slow.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003772 // Load the function
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003773 frame_->EmitPush(cp);
mads.s.ager31e71382008-08-13 09:32:07 +00003774 __ mov(r0, Operand(var->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003775 frame_->EmitPush(r0);
3776 frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003777 // r0: slot value; r1: receiver
3778
3779 // Load the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003780 frame_->EmitPush(r0); // function
3781 frame_->EmitPush(r1); // receiver
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003782
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00003783 // If fast case code has been generated, emit code to push the
3784 // function and receiver and have the slow path jump around this
3785 // code.
3786 if (done.is_linked()) {
3787 JumpTarget call;
3788 call.Jump();
3789 done.Bind();
3790 frame_->EmitPush(r0); // function
3791 LoadGlobalReceiver(r1); // receiver
3792 call.Bind();
3793 }
3794
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003795 // Call the function. At this point, everything is spilled but the
3796 // function and receiver are in r0 and r1.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003797 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003798 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003799
3800 } else if (property != NULL) {
3801 // Check if the key is a literal string.
3802 Literal* literal = property->key()->AsLiteral();
3803
3804 if (literal != NULL && literal->handle()->IsSymbol()) {
3805 // ------------------------------------------------------------------
3806 // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
3807 // ------------------------------------------------------------------
3808
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003809 Handle<String> name = Handle<String>::cast(literal->handle());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003811 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
3812 name->IsEqualTo(CStrVector("apply")) &&
3813 args->length() == 2 &&
3814 args->at(1)->AsVariableProxy() != NULL &&
3815 args->at(1)->AsVariableProxy()->IsArguments()) {
3816 // Use the optimized Function.prototype.apply that avoids
3817 // allocating lazily allocated arguments objects.
3818 CallApplyLazy(property->obj(),
3819 args->at(0),
3820 args->at(1)->AsVariableProxy(),
3821 node->position());
3822
3823 } else {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003824 Load(property->obj()); // Receiver.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003825 // Load the arguments.
3826 int arg_count = args->length();
3827 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003828 Load(args->at(i));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003829 }
3830
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003831 VirtualFrame::SpilledScope spilled_scope(frame_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00003832 // Set the name register and call the IC initialization code.
3833 __ mov(r2, Operand(name));
3834 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
3835 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
3836 CodeForSourcePosition(node->position());
3837 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
3838 __ ldr(cp, frame_->Context());
3839 frame_->EmitPush(r0);
3840 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003841
3842 } else {
3843 // -------------------------------------------
3844 // JavaScript example: 'array[index](1, 2, 3)'
3845 // -------------------------------------------
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003846 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003848 Load(property->obj());
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003849 if (!property->is_synthetic()) {
3850 // Duplicate receiver for later use.
3851 __ ldr(r0, MemOperand(sp, 0));
3852 frame_->EmitPush(r0);
3853 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003854 Load(property->key());
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00003855 EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003856 // Put the function below the receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003857 if (property->is_synthetic()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003858 // Use the global receiver.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003859 frame_->EmitPush(r0); // Function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003860 LoadGlobalReceiver(r0);
3861 } else {
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00003862 // Switch receiver and function.
3863 frame_->EmitPop(r1); // Receiver.
3864 frame_->EmitPush(r0); // Function.
3865 frame_->EmitPush(r1); // Receiver.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003866 }
3867
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003868 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003869 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003870 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003871 }
3872
3873 } else {
3874 // ----------------------------------
3875 // JavaScript example: 'foo(1, 2, 3)' // foo is not global
3876 // ----------------------------------
3877
3878 // Load the function.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003879 Load(function);
3880
3881 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003882
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003883 // Pass the global proxy as the receiver.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003884 LoadGlobalReceiver(r0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003886 // Call the function.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003887 CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003888 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003889 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00003890 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003891}
3892
3893
ager@chromium.org7c537e22008-10-16 08:43:32 +00003894void CodeGenerator::VisitCallNew(CallNew* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003895#ifdef DEBUG
3896 int original_height = frame_->height();
3897#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003898 Comment cmnt(masm_, "[ CallNew");
3899
3900 // According to ECMA-262, section 11.2.2, page 44, the function
3901 // expression in new calls must be evaluated before the
3902 // arguments. This is different from ordinary calls, where the
3903 // actual function to call is resolved after the arguments have been
3904 // evaluated.
3905
3906 // Compute function to call and use the global object as the
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003907 // receiver. There is no need to use the global proxy here because
3908 // it will always be replaced with a newly allocated object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003909 Load(node->expression());
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003910 LoadGlobal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003911
3912 // Push the arguments ("left-to-right") on the stack.
3913 ZoneList<Expression*>* args = node->arguments();
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003914 int arg_count = args->length();
3915 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003916 Load(args->at(i));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003917 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003918
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003919 VirtualFrame::SpilledScope spilled_scope(frame_);
3920
mads.s.ager31e71382008-08-13 09:32:07 +00003921 // r0: the number of arguments.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003922 __ mov(r0, Operand(arg_count));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003923 // Load the function into r1 as per calling convention.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00003924 __ ldr(r1, frame_->ElementAt(arg_count + 1));
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003925
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003926 // Call the construct call builtin that handles allocation and
3927 // constructor invocation.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003928 CodeForSourcePosition(node->position());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003929 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003930 frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
mads.s.ager31e71382008-08-13 09:32:07 +00003931
3932 // Discard old TOS value and push r0 on the stack (same as Pop(), push(r0)).
ager@chromium.org3bf7b912008-11-17 09:09:45 +00003933 __ str(r0, frame_->Top());
ager@chromium.orgac091b72010-05-05 07:34:42 +00003934 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935}
3936
3937
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003938void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003939 VirtualFrame::SpilledScope spilled_scope(frame_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003940 ASSERT(args->length() == 1);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003941 JumpTarget leave, null, function, non_function_constructor;
3942
3943 // Load the object into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00003944 Load(args->at(0));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003945 frame_->EmitPop(r0);
3946
3947 // If the object is a smi, we return null.
3948 __ tst(r0, Operand(kSmiTagMask));
3949 null.Branch(eq);
3950
3951 // Check that the object is a JS object but take special care of JS
3952 // functions to make sure they have 'Function' as their class.
3953 __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE);
3954 null.Branch(lt);
3955
3956 // As long as JS_FUNCTION_TYPE is the last instance type and it is
3957 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
3958 // LAST_JS_OBJECT_TYPE.
3959 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
3960 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
3961 __ cmp(r1, Operand(JS_FUNCTION_TYPE));
3962 function.Branch(eq);
3963
3964 // Check if the constructor in the map is a function.
3965 __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset));
3966 __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
3967 non_function_constructor.Branch(ne);
3968
3969 // The r0 register now contains the constructor function. Grab the
3970 // instance class name from there.
3971 __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
3972 __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003973 frame_->EmitPush(r0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003974 leave.Jump();
3975
3976 // Functions have class 'Function'.
3977 function.Bind();
3978 __ mov(r0, Operand(Factory::function_class_symbol()));
3979 frame_->EmitPush(r0);
3980 leave.Jump();
3981
3982 // Objects with a non-function constructor have class 'Object'.
3983 non_function_constructor.Bind();
3984 __ mov(r0, Operand(Factory::Object_symbol()));
3985 frame_->EmitPush(r0);
3986 leave.Jump();
3987
3988 // Non-JS objects have class null.
3989 null.Bind();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00003990 __ LoadRoot(r0, Heap::kNullValueRootIndex);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00003991 frame_->EmitPush(r0);
3992
3993 // All done.
3994 leave.Bind();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00003995}
3996
3997
ager@chromium.org7c537e22008-10-16 08:43:32 +00003998void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00003999 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004000 ASSERT(args->length() == 1);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004001 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004002 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004003 frame_->EmitPop(r0); // r0 contains object.
mads.s.ager31e71382008-08-13 09:32:07 +00004004 // if (object->IsSmi()) return the object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004005 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004006 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004007 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4008 __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004009 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004010 // Load the value.
4011 __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004012 leave.Bind();
4013 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004014}
4015
4016
ager@chromium.org7c537e22008-10-16 08:43:32 +00004017void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004018 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004019 ASSERT(args->length() == 2);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004020 JumpTarget leave;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004021 Load(args->at(0)); // Load the object.
4022 Load(args->at(1)); // Load the value.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004023 frame_->EmitPop(r0); // r0 contains value
4024 frame_->EmitPop(r1); // r1 contains object
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004025 // if (object->IsSmi()) return object.
4026 __ tst(r1, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004027 leave.Branch(eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004028 // It is a heap object - get map. If (!object->IsJSValue()) return the object.
4029 __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004030 leave.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031 // Store the value.
4032 __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset));
4033 // Update the write barrier.
4034 __ mov(r2, Operand(JSValue::kValueOffset - kHeapObjectTag));
4035 __ RecordWrite(r1, r2, r3);
4036 // Leave.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004037 leave.Bind();
4038 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004039}
4040
4041
ager@chromium.org7c537e22008-10-16 08:43:32 +00004042void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004043 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004044 Load(args->at(0));
4045 Register reg = frame_->PopToRegister();
4046 __ tst(reg, Operand(kSmiTagMask));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004047 cc_reg_ = eq;
4048}
4049
4050
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004051void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
4052 // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc.
4053 ASSERT_EQ(args->length(), 3);
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004054#ifdef ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004055 if (ShouldGenerateLog(args->at(0))) {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004056 Load(args->at(1));
4057 Load(args->at(2));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004058 frame_->CallRuntime(Runtime::kLog, 2);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004059 }
christian.plesner.hansen@gmail.comaca49682009-01-07 14:29:04 +00004060#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004061 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004062}
4063
4064
ager@chromium.org7c537e22008-10-16 08:43:32 +00004065void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004066 ASSERT(args->length() == 1);
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004067 Load(args->at(0));
4068 Register reg = frame_->PopToRegister();
4069 __ tst(reg, Operand(kSmiTagMask | 0x80000000u));
ager@chromium.orgc27e4e72008-09-04 13:52:27 +00004070 cc_reg_ = eq;
4071}
4072
4073
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004074// Generates the Math.pow method - currently just calls runtime.
4075void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
4076 ASSERT(args->length() == 2);
4077 Load(args->at(0));
4078 Load(args->at(1));
4079 frame_->CallRuntime(Runtime::kMath_pow, 2);
4080 frame_->EmitPush(r0);
4081}
4082
4083
4084// Generates the Math.sqrt method - currently just calls runtime.
4085void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
4086 ASSERT(args->length() == 1);
4087 Load(args->at(0));
4088 frame_->CallRuntime(Runtime::kMath_sqrt, 1);
4089 frame_->EmitPush(r0);
4090}
4091
4092
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004093class DeferredStringCharCodeAt : public DeferredCode {
4094 public:
4095 DeferredStringCharCodeAt(Register object,
4096 Register index,
4097 Register scratch,
4098 Register result)
4099 : result_(result),
4100 char_code_at_generator_(object,
4101 index,
4102 scratch,
4103 result,
4104 &need_conversion_,
4105 &need_conversion_,
4106 &index_out_of_range_,
4107 STRING_INDEX_IS_NUMBER) {}
4108
4109 StringCharCodeAtGenerator* fast_case_generator() {
4110 return &char_code_at_generator_;
4111 }
4112
4113 virtual void Generate() {
4114 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4115 char_code_at_generator_.GenerateSlow(masm(), call_helper);
4116
4117 __ bind(&need_conversion_);
4118 // Move the undefined value into the result register, which will
4119 // trigger conversion.
4120 __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
4121 __ jmp(exit_label());
4122
4123 __ bind(&index_out_of_range_);
4124 // When the index is out of range, the spec requires us to return
4125 // NaN.
4126 __ LoadRoot(result_, Heap::kNanValueRootIndex);
4127 __ jmp(exit_label());
4128 }
4129
4130 private:
4131 Register result_;
4132
4133 Label need_conversion_;
4134 Label index_out_of_range_;
4135
4136 StringCharCodeAtGenerator char_code_at_generator_;
4137};
4138
4139
4140// This generates code that performs a String.prototype.charCodeAt() call
4141// or returns a smi in order to trigger conversion.
4142void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
4143 VirtualFrame::SpilledScope spilled_scope(frame_);
4144 Comment(masm_, "[ GenerateStringCharCodeAt");
kasper.lund7276f142008-07-30 08:49:36 +00004145 ASSERT(args->length() == 2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004146
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004147 Load(args->at(0));
4148 Load(args->at(1));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004149
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004150 Register index = r1;
4151 Register object = r2;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004152
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004153 frame_->EmitPop(r1);
4154 frame_->EmitPop(r2);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004155
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004156 // We need two extra registers.
4157 Register scratch = r3;
4158 Register result = r0;
4159
4160 DeferredStringCharCodeAt* deferred =
4161 new DeferredStringCharCodeAt(object,
4162 index,
4163 scratch,
4164 result);
4165 deferred->fast_case_generator()->GenerateFast(masm_);
4166 deferred->BindExit();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004167 frame_->EmitPush(result);
kasper.lund7276f142008-07-30 08:49:36 +00004168}
4169
4170
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004171class DeferredStringCharFromCode : public DeferredCode {
4172 public:
4173 DeferredStringCharFromCode(Register code,
4174 Register result)
4175 : char_from_code_generator_(code, result) {}
4176
4177 StringCharFromCodeGenerator* fast_case_generator() {
4178 return &char_from_code_generator_;
4179 }
4180
4181 virtual void Generate() {
4182 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4183 char_from_code_generator_.GenerateSlow(masm(), call_helper);
4184 }
4185
4186 private:
4187 StringCharFromCodeGenerator char_from_code_generator_;
4188};
4189
4190
4191// Generates code for creating a one-char string from a char code.
4192void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
4193 VirtualFrame::SpilledScope spilled_scope(frame_);
4194 Comment(masm_, "[ GenerateStringCharFromCode");
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004195 ASSERT(args->length() == 1);
4196
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004197 Load(args->at(0));
4198
ager@chromium.orgac091b72010-05-05 07:34:42 +00004199 Register code = r1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00004200 Register result = r0;
4201
ager@chromium.orgac091b72010-05-05 07:34:42 +00004202 frame_->EmitPop(code);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004203
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004204 DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
4205 code, result);
4206 deferred->fast_case_generator()->GenerateFast(masm_);
4207 deferred->BindExit();
4208 frame_->EmitPush(result);
4209}
4210
4211
4212class DeferredStringCharAt : public DeferredCode {
4213 public:
4214 DeferredStringCharAt(Register object,
4215 Register index,
4216 Register scratch1,
4217 Register scratch2,
4218 Register result)
4219 : result_(result),
4220 char_at_generator_(object,
4221 index,
4222 scratch1,
4223 scratch2,
4224 result,
4225 &need_conversion_,
4226 &need_conversion_,
4227 &index_out_of_range_,
4228 STRING_INDEX_IS_NUMBER) {}
4229
4230 StringCharAtGenerator* fast_case_generator() {
4231 return &char_at_generator_;
4232 }
4233
4234 virtual void Generate() {
4235 VirtualFrameRuntimeCallHelper call_helper(frame_state());
4236 char_at_generator_.GenerateSlow(masm(), call_helper);
4237
4238 __ bind(&need_conversion_);
4239 // Move smi zero into the result register, which will trigger
4240 // conversion.
4241 __ mov(result_, Operand(Smi::FromInt(0)));
4242 __ jmp(exit_label());
4243
4244 __ bind(&index_out_of_range_);
4245 // When the index is out of range, the spec requires us to return
4246 // the empty string.
4247 __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
4248 __ jmp(exit_label());
4249 }
4250
4251 private:
4252 Register result_;
4253
4254 Label need_conversion_;
4255 Label index_out_of_range_;
4256
4257 StringCharAtGenerator char_at_generator_;
4258};
4259
4260
4261// This generates code that performs a String.prototype.charAt() call
4262// or returns a smi in order to trigger conversion.
4263void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
4264 VirtualFrame::SpilledScope spilled_scope(frame_);
4265 Comment(masm_, "[ GenerateStringCharAt");
4266 ASSERT(args->length() == 2);
4267
4268 Load(args->at(0));
4269 Load(args->at(1));
4270
4271 Register index = r1;
4272 Register object = r2;
4273
4274 frame_->EmitPop(r1);
4275 frame_->EmitPop(r2);
4276
4277 // We need three extra registers.
4278 Register scratch1 = r3;
4279 Register scratch2 = r4;
4280 Register result = r0;
4281
4282 DeferredStringCharAt* deferred =
4283 new DeferredStringCharAt(object,
4284 index,
4285 scratch1,
4286 scratch2,
4287 result);
4288 deferred->fast_case_generator()->GenerateFast(masm_);
4289 deferred->BindExit();
ager@chromium.orgac091b72010-05-05 07:34:42 +00004290 frame_->EmitPush(result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004291}
4292
4293
ager@chromium.org7c537e22008-10-16 08:43:32 +00004294void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004295 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004296 Load(args->at(0));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004297 JumpTarget answer;
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004298 // We need the CC bits to come out as not_equal in the case where the
4299 // object is a smi. This can't be done with the usual test opcode so
4300 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004301 Register possible_array = frame_->PopToRegister();
4302 Register scratch = VirtualFrame::scratch0();
4303 __ and_(scratch, possible_array, Operand(kSmiTagMask));
4304 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004305 answer.Branch(ne);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004306 // It is a heap object - get the map. Check if the object is a JS array.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004307 __ CompareObjectType(possible_array, scratch, scratch, JS_ARRAY_TYPE);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004308 answer.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004309 cc_reg_ = eq;
4310}
4311
4312
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004313void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004314 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004315 Load(args->at(0));
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004316 JumpTarget answer;
4317 // We need the CC bits to come out as not_equal in the case where the
4318 // object is a smi. This can't be done with the usual test opcode so
4319 // we use XOR to get the right CC bits.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004320 Register possible_regexp = frame_->PopToRegister();
4321 Register scratch = VirtualFrame::scratch0();
4322 __ and_(scratch, possible_regexp, Operand(kSmiTagMask));
4323 __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004324 answer.Branch(ne);
4325 // It is a heap object - get the map. Check if the object is a regexp.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004326 __ CompareObjectType(possible_regexp, scratch, scratch, JS_REGEXP_TYPE);
fschneider@chromium.orgb95b98b2010-02-23 10:34:29 +00004327 answer.Bind();
4328 cc_reg_ = eq;
4329}
4330
4331
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004332void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
4333 // This generates a fast version of:
4334 // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004335 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004336 Load(args->at(0));
4337 Register possible_object = frame_->PopToRegister();
4338 __ tst(possible_object, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004339 false_target()->Branch(eq);
4340
4341 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004342 __ cmp(possible_object, ip);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004343 true_target()->Branch(eq);
4344
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004345 Register map_reg = VirtualFrame::scratch0();
4346 __ ldr(map_reg, FieldMemOperand(possible_object, HeapObject::kMapOffset));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004347 // Undetectable objects behave like undefined when tested with typeof.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004348 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kBitFieldOffset));
4349 __ tst(possible_object, Operand(1 << Map::kIsUndetectable));
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004350 false_target()->Branch(ne);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004351
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004352 __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
4353 __ cmp(possible_object, Operand(FIRST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004354 false_target()->Branch(lt);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004355 __ cmp(possible_object, Operand(LAST_JS_OBJECT_TYPE));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004356 cc_reg_ = le;
4357}
4358
4359
4360void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
4361 // This generates a fast version of:
4362 // (%_ClassOf(arg) === 'Function')
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004363 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004364 Load(args->at(0));
4365 Register possible_function = frame_->PopToRegister();
4366 __ tst(possible_function, Operand(kSmiTagMask));
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004367 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004368 Register map_reg = VirtualFrame::scratch0();
4369 Register scratch = VirtualFrame::scratch1();
4370 __ CompareObjectType(possible_function, map_reg, scratch, JS_FUNCTION_TYPE);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00004371 cc_reg_ = eq;
4372}
4373
4374
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004375void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004376 ASSERT(args->length() == 1);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004377 Load(args->at(0));
4378 Register possible_undetectable = frame_->PopToRegister();
4379 __ tst(possible_undetectable, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004380 false_target()->Branch(eq);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004381 Register scratch = VirtualFrame::scratch0();
4382 __ ldr(scratch,
4383 FieldMemOperand(possible_undetectable, HeapObject::kMapOffset));
4384 __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
4385 __ tst(scratch, Operand(1 << Map::kIsUndetectable));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004386 cc_reg_ = ne;
4387}
4388
4389
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004390void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004391 ASSERT(args->length() == 0);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004392
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004393 Register scratch0 = VirtualFrame::scratch0();
4394 Register scratch1 = VirtualFrame::scratch1();
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004395 // Get the frame pointer for the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004396 __ ldr(scratch0, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004397
4398 // Skip the arguments adaptor frame if it exists.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004399 __ ldr(scratch1,
4400 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4401 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4402 __ ldr(scratch0,
4403 MemOperand(scratch0, StandardFrameConstants::kCallerFPOffset), eq);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004404
4405 // Check the marker in the calling frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004406 __ ldr(scratch1,
4407 MemOperand(scratch0, StandardFrameConstants::kMarkerOffset));
4408 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004409 cc_reg_ = eq;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004410}
4411
4412
ager@chromium.org7c537e22008-10-16 08:43:32 +00004413void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004414 ASSERT(args->length() == 0);
4415
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004416 Register tos = frame_->GetTOSRegister();
4417 Register scratch0 = VirtualFrame::scratch0();
4418 Register scratch1 = VirtualFrame::scratch1();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004419
lrn@chromium.org25156de2010-04-06 13:10:27 +00004420 // Check if the calling frame is an arguments adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004421 __ ldr(scratch0,
4422 MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
4423 __ ldr(scratch1,
4424 MemOperand(scratch0, StandardFrameConstants::kContextOffset));
4425 __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
4426
4427 // Get the number of formal parameters.
4428 __ mov(tos, Operand(Smi::FromInt(scope()->num_parameters())), LeaveCC, ne);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004429
4430 // Arguments adaptor case: Read the arguments length from the
4431 // adaptor frame.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004432 __ ldr(tos,
4433 MemOperand(scratch0, ArgumentsAdaptorFrameConstants::kLengthOffset),
4434 eq);
lrn@chromium.org25156de2010-04-06 13:10:27 +00004435
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004436 frame_->EmitPush(tos);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004437}
4438
4439
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00004440void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ager@chromium.org357bf652010-04-12 11:30:10 +00004441 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004442 ASSERT(args->length() == 1);
4443
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004444 // Satisfy contract with ArgumentsAccessStub:
4445 // Load the key into r1 and the formal parameters count into r0.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004446 Load(args->at(0));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004447 frame_->EmitPop(r1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004448 __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004449
4450 // Call the shared stub to get to arguments[key].
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004451 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004452 frame_->CallStub(&stub, 0);
4453 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004454}
4455
4456
ager@chromium.org357bf652010-04-12 11:30:10 +00004457void CodeGenerator::GenerateRandomHeapNumber(
4458 ZoneList<Expression*>* args) {
4459 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004460 ASSERT(args->length() == 0);
ager@chromium.org357bf652010-04-12 11:30:10 +00004461
4462 Label slow_allocate_heapnumber;
4463 Label heapnumber_allocated;
4464
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004465 __ AllocateHeapNumber(r4, r1, r2, &slow_allocate_heapnumber);
ager@chromium.org357bf652010-04-12 11:30:10 +00004466 __ jmp(&heapnumber_allocated);
4467
4468 __ bind(&slow_allocate_heapnumber);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004469 // To allocate a heap number, and ensure that it is not a smi, we
4470 // call the runtime function FUnaryMinus on 0, returning the double
4471 // -0.0. A new, distinct heap number is returned each time.
ager@chromium.org357bf652010-04-12 11:30:10 +00004472 __ mov(r0, Operand(Smi::FromInt(0)));
4473 __ push(r0);
4474 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004475 __ mov(r4, Operand(r0));
ager@chromium.org357bf652010-04-12 11:30:10 +00004476
4477 __ bind(&heapnumber_allocated);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004478
4479 // Convert 32 random bits in r0 to 0.(32 random bits) in a double
4480 // by computing:
4481 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
4482 if (CpuFeatures::IsSupported(VFP3)) {
4483 __ PrepareCallCFunction(0, r1);
4484 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
4485
4486 CpuFeatures::Scope scope(VFP3);
4487 // 0x41300000 is the top half of 1.0 x 2^20 as a double.
4488 // Create this constant using mov/orr to avoid PC relative load.
4489 __ mov(r1, Operand(0x41000000));
4490 __ orr(r1, r1, Operand(0x300000));
4491 // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
4492 __ vmov(d7, r0, r1);
4493 // Move 0x4130000000000000 to VFP.
4494 __ mov(r0, Operand(0));
4495 __ vmov(d8, r0, r1);
4496 // Subtract and store the result in the heap number.
4497 __ vsub(d7, d7, d8);
4498 __ sub(r0, r4, Operand(kHeapObjectTag));
4499 __ vstr(d7, r0, HeapNumber::kValueOffset);
4500 frame_->EmitPush(r4);
4501 } else {
4502 __ mov(r0, Operand(r4));
4503 __ PrepareCallCFunction(1, r1);
4504 __ CallCFunction(
4505 ExternalReference::fill_heap_number_with_random_function(), 1);
4506 frame_->EmitPush(r0);
4507 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004508}
4509
4510
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004511void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
4512 ASSERT_EQ(2, args->length());
4513
4514 Load(args->at(0));
4515 Load(args->at(1));
4516
ager@chromium.org5c838252010-02-19 08:53:10 +00004517 StringAddStub stub(NO_STRING_ADD_FLAGS);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004518 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004519 frame_->CallStub(&stub, 2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00004520 frame_->EmitPush(r0);
4521}
4522
4523
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004524void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
4525 ASSERT_EQ(3, args->length());
4526
4527 Load(args->at(0));
4528 Load(args->at(1));
4529 Load(args->at(2));
4530
ager@chromium.org5c838252010-02-19 08:53:10 +00004531 SubStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004532 frame_->SpillAll();
ager@chromium.org5c838252010-02-19 08:53:10 +00004533 frame_->CallStub(&stub, 3);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004534 frame_->EmitPush(r0);
4535}
4536
4537
4538void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
4539 ASSERT_EQ(2, args->length());
4540
4541 Load(args->at(0));
4542 Load(args->at(1));
4543
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004544 StringCompareStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004545 frame_->SpillAll();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00004546 frame_->CallStub(&stub, 2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004547 frame_->EmitPush(r0);
4548}
4549
4550
4551void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
4552 ASSERT_EQ(4, args->length());
4553
4554 Load(args->at(0));
4555 Load(args->at(1));
4556 Load(args->at(2));
4557 Load(args->at(3));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004558 RegExpExecStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004559 frame_->SpillAll();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004560 frame_->CallStub(&stub, 4);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00004561 frame_->EmitPush(r0);
4562}
4563
4564
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004565void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
4566 // No stub. This code only occurs a few times in regexp.js.
4567 const int kMaxInlineLength = 100;
4568 ASSERT_EQ(3, args->length());
4569 Load(args->at(0)); // Size of array, smi.
4570 Load(args->at(1)); // "index" property value.
4571 Load(args->at(2)); // "input" property value.
4572 {
4573 VirtualFrame::SpilledScope spilled_scope(frame_);
4574 Label slowcase;
4575 Label done;
4576 __ ldr(r1, MemOperand(sp, kPointerSize * 2));
4577 STATIC_ASSERT(kSmiTag == 0);
4578 STATIC_ASSERT(kSmiTagSize == 1);
4579 __ tst(r1, Operand(kSmiTagMask));
4580 __ b(ne, &slowcase);
4581 __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
4582 __ b(hi, &slowcase);
4583 // Smi-tagging is equivalent to multiplying by 2.
4584 // Allocate RegExpResult followed by FixedArray with size in ebx.
4585 // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
4586 // Elements: [Map][Length][..elements..]
4587 // Size of JSArray with two in-object properties and the header of a
4588 // FixedArray.
4589 int objects_size =
4590 (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
4591 __ mov(r5, Operand(r1, LSR, kSmiTagSize + kSmiShiftSize));
4592 __ add(r2, r5, Operand(objects_size));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00004593 __ AllocateInNewSpace(
4594 r2, // In: Size, in words.
4595 r0, // Out: Start of allocation (tagged).
4596 r3, // Scratch register.
4597 r4, // Scratch register.
4598 &slowcase,
4599 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004600 // r0: Start of allocated area, object-tagged.
4601 // r1: Number of elements in array, as smi.
4602 // r5: Number of elements, untagged.
4603
4604 // Set JSArray map to global.regexp_result_map().
4605 // Set empty properties FixedArray.
4606 // Set elements to point to FixedArray allocated right after the JSArray.
4607 // Interleave operations for better latency.
4608 __ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
4609 __ add(r3, r0, Operand(JSRegExpResult::kSize));
4610 __ mov(r4, Operand(Factory::empty_fixed_array()));
4611 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
4612 __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
4613 __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
4614 __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
4615 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
4616
4617 // Set input, index and length fields from arguments.
4618 __ ldm(ia_w, sp, static_cast<RegList>(r2.bit() | r4.bit()));
4619 __ str(r1, FieldMemOperand(r0, JSArray::kLengthOffset));
4620 __ add(sp, sp, Operand(kPointerSize));
4621 __ str(r4, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
4622 __ str(r2, FieldMemOperand(r0, JSRegExpResult::kInputOffset));
4623
4624 // Fill out the elements FixedArray.
4625 // r0: JSArray, tagged.
4626 // r3: FixedArray, tagged.
4627 // r5: Number of elements in array, untagged.
4628
4629 // Set map.
4630 __ mov(r2, Operand(Factory::fixed_array_map()));
4631 __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
4632 // Set FixedArray length.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004633 __ mov(r6, Operand(r5, LSL, kSmiTagSize));
4634 __ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00004635 // Fill contents of fixed-array with the-hole.
4636 __ mov(r2, Operand(Factory::the_hole_value()));
4637 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4638 // Fill fixed array elements with hole.
4639 // r0: JSArray, tagged.
4640 // r2: the hole.
4641 // r3: Start of elements in FixedArray.
4642 // r5: Number of elements to fill.
4643 Label loop;
4644 __ tst(r5, Operand(r5));
4645 __ bind(&loop);
4646 __ b(le, &done); // Jump if r1 is negative or zero.
4647 __ sub(r5, r5, Operand(1), SetCC);
4648 __ str(r2, MemOperand(r3, r5, LSL, kPointerSizeLog2));
4649 __ jmp(&loop);
4650
4651 __ bind(&slowcase);
4652 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
4653
4654 __ bind(&done);
4655 }
4656 frame_->Forget(3);
4657 frame_->EmitPush(r0);
4658}
4659
4660
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004661class DeferredSearchCache: public DeferredCode {
4662 public:
4663 DeferredSearchCache(Register dst, Register cache, Register key)
4664 : dst_(dst), cache_(cache), key_(key) {
4665 set_comment("[ DeferredSearchCache");
4666 }
4667
4668 virtual void Generate();
4669
4670 private:
4671 Register dst_, cache_, key_;
4672};
4673
4674
4675void DeferredSearchCache::Generate() {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00004676 __ Push(cache_, key_);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004677 __ CallRuntime(Runtime::kGetFromCache, 2);
4678 if (!dst_.is(r0)) {
4679 __ mov(dst_, r0);
4680 }
4681}
4682
4683
4684void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
4685 ASSERT_EQ(2, args->length());
4686
4687 ASSERT_NE(NULL, args->at(0)->AsLiteral());
4688 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
4689
4690 Handle<FixedArray> jsfunction_result_caches(
4691 Top::global_context()->jsfunction_result_caches());
4692 if (jsfunction_result_caches->length() <= cache_id) {
4693 __ Abort("Attempt to use undefined cache.");
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004694 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004695 return;
4696 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004697
4698 Load(args->at(1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004699
4700 VirtualFrame::SpilledScope spilled_scope(frame_);
4701
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004702 frame_->EmitPop(r2);
4703
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00004704 __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
4705 __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
4706 __ ldr(r1, ContextOperand(r1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
4707 __ ldr(r1, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(cache_id)));
4708
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004709 DeferredSearchCache* deferred = new DeferredSearchCache(r0, r1, r2);
4710
4711 const int kFingerOffset =
4712 FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
4713 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00004714 __ ldr(r0, FieldMemOperand(r1, kFingerOffset));
4715 // r0 now holds finger offset as a smi.
4716 __ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4717 // r3 now points to the start of fixed array elements.
4718 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
4719 // Note side effect of PreIndex: r3 now points to the key of the pair.
4720 __ cmp(r2, r0);
4721 deferred->Branch(ne);
4722
4723 __ ldr(r0, MemOperand(r3, kPointerSize));
4724
4725 deferred->BindExit();
4726 frame_->EmitPush(r0);
4727}
4728
4729
ager@chromium.org5c838252010-02-19 08:53:10 +00004730void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
4731 ASSERT_EQ(args->length(), 1);
4732
4733 // Load the argument on the stack and jump to the runtime.
4734 Load(args->at(0));
4735
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004736 NumberToStringStub stub;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004737 frame_->SpillAll();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00004738 frame_->CallStub(&stub, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00004739 frame_->EmitPush(r0);
4740}
4741
4742
ager@chromium.orgac091b72010-05-05 07:34:42 +00004743class DeferredSwapElements: public DeferredCode {
4744 public:
4745 DeferredSwapElements(Register object, Register index1, Register index2)
4746 : object_(object), index1_(index1), index2_(index2) {
4747 set_comment("[ DeferredSwapElements");
4748 }
4749
4750 virtual void Generate();
4751
4752 private:
4753 Register object_, index1_, index2_;
4754};
4755
4756
4757void DeferredSwapElements::Generate() {
4758 __ push(object_);
4759 __ push(index1_);
4760 __ push(index2_);
4761 __ CallRuntime(Runtime::kSwapElements, 3);
4762}
4763
4764
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004765void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
4766 Comment cmnt(masm_, "[ GenerateSwapElements");
4767
4768 ASSERT_EQ(3, args->length());
4769
4770 Load(args->at(0));
4771 Load(args->at(1));
4772 Load(args->at(2));
4773
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004774 VirtualFrame::SpilledScope spilled_scope(frame_);
4775
ager@chromium.orgac091b72010-05-05 07:34:42 +00004776 Register index2 = r2;
4777 Register index1 = r1;
4778 Register object = r0;
4779 Register tmp1 = r3;
4780 Register tmp2 = r4;
4781
4782 frame_->EmitPop(index2);
4783 frame_->EmitPop(index1);
4784 frame_->EmitPop(object);
4785
4786 DeferredSwapElements* deferred =
4787 new DeferredSwapElements(object, index1, index2);
4788
4789 // Fetch the map and check if array is in fast case.
4790 // Check that object doesn't require security checks and
4791 // has no indexed interceptor.
4792 __ CompareObjectType(object, tmp1, tmp2, FIRST_JS_OBJECT_TYPE);
4793 deferred->Branch(lt);
4794 __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
4795 __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
4796 deferred->Branch(nz);
4797
4798 // Check the object's elements are in fast case.
4799 __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
4800 __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
4801 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
4802 __ cmp(tmp2, ip);
4803 deferred->Branch(ne);
4804
4805 // Smi-tagging is equivalent to multiplying by 2.
4806 STATIC_ASSERT(kSmiTag == 0);
4807 STATIC_ASSERT(kSmiTagSize == 1);
4808
4809 // Check that both indices are smis.
4810 __ mov(tmp2, index1);
4811 __ orr(tmp2, tmp2, index2);
4812 __ tst(tmp2, Operand(kSmiTagMask));
4813 deferred->Branch(nz);
4814
4815 // Bring the offsets into the fixed array in tmp1 into index1 and
4816 // index2.
4817 __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4818 __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
4819 __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
4820
4821 // Swap elements.
4822 Register tmp3 = object;
4823 object = no_reg;
4824 __ ldr(tmp3, MemOperand(tmp1, index1));
4825 __ ldr(tmp2, MemOperand(tmp1, index2));
4826 __ str(tmp3, MemOperand(tmp1, index2));
4827 __ str(tmp2, MemOperand(tmp1, index1));
4828
4829 Label done;
4830 __ InNewSpace(tmp1, tmp2, eq, &done);
4831 // Possible optimization: do a check that both values are Smis
4832 // (or them and test against Smi mask.)
4833
4834 __ mov(tmp2, tmp1);
4835 RecordWriteStub recordWrite1(tmp1, index1, tmp3);
4836 __ CallStub(&recordWrite1);
4837
4838 RecordWriteStub recordWrite2(tmp2, index2, tmp3);
4839 __ CallStub(&recordWrite2);
4840
4841 __ bind(&done);
4842
4843 deferred->BindExit();
4844 __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
4845 frame_->EmitPush(tmp1);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00004846}
4847
4848
ager@chromium.org357bf652010-04-12 11:30:10 +00004849void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
4850 Comment cmnt(masm_, "[ GenerateCallFunction");
4851
4852 ASSERT(args->length() >= 2);
4853
4854 int n_args = args->length() - 2; // for receiver and function.
4855 Load(args->at(0)); // receiver
4856 for (int i = 0; i < n_args; i++) {
4857 Load(args->at(i + 1));
4858 }
4859 Load(args->at(n_args + 1)); // function
4860 frame_->CallJSFunction(n_args);
4861 frame_->EmitPush(r0);
4862}
4863
4864
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00004865void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
4866 ASSERT_EQ(args->length(), 1);
4867 // Load the argument on the stack and jump to the runtime.
4868 Load(args->at(0));
4869 frame_->CallRuntime(Runtime::kMath_sin, 1);
4870 frame_->EmitPush(r0);
4871}
4872
4873
4874void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
4875 ASSERT_EQ(args->length(), 1);
4876 // Load the argument on the stack and jump to the runtime.
4877 Load(args->at(0));
4878 frame_->CallRuntime(Runtime::kMath_cos, 1);
4879 frame_->EmitPush(r0);
4880}
4881
4882
ager@chromium.org7c537e22008-10-16 08:43:32 +00004883void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004884 ASSERT(args->length() == 2);
4885
4886 // Load the two objects into registers and perform the comparison.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00004887 Load(args->at(0));
4888 Load(args->at(1));
4889 Register lhs = frame_->PopToRegister();
4890 Register rhs = frame_->PopToRegister(lhs);
4891 __ cmp(lhs, rhs);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004892 cc_reg_ = eq;
4893}
4894
4895
ager@chromium.org7c537e22008-10-16 08:43:32 +00004896void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004897#ifdef DEBUG
4898 int original_height = frame_->height();
4899#endif
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004900 if (CheckForInlineRuntimeCall(node)) {
4901 ASSERT((has_cc() && frame_->height() == original_height) ||
4902 (!has_cc() && frame_->height() == original_height + 1));
4903 return;
4904 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004905
4906 ZoneList<Expression*>* args = node->arguments();
4907 Comment cmnt(masm_, "[ CallRuntime");
4908 Runtime::Function* function = node->function();
4909
ager@chromium.org41826e72009-03-30 13:30:57 +00004910 if (function == NULL) {
mads.s.ager31e71382008-08-13 09:32:07 +00004911 // Prepare stack for calling JS runtime function.
mads.s.ager31e71382008-08-13 09:32:07 +00004912 // Push the builtins object found in the current global object.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004913 Register scratch = VirtualFrame::scratch0();
4914 __ ldr(scratch, GlobalObject());
4915 Register builtins = frame_->GetTOSRegister();
4916 __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset));
4917 frame_->EmitPush(builtins);
ager@chromium.org41826e72009-03-30 13:30:57 +00004918 }
mads.s.ager31e71382008-08-13 09:32:07 +00004919
ager@chromium.org41826e72009-03-30 13:30:57 +00004920 // Push the arguments ("left-to-right").
4921 int arg_count = args->length();
4922 for (int i = 0; i < arg_count; i++) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004923 Load(args->at(i));
ager@chromium.org41826e72009-03-30 13:30:57 +00004924 }
mads.s.ager31e71382008-08-13 09:32:07 +00004925
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004926 VirtualFrame::SpilledScope spilled_scope(frame_);
4927
ager@chromium.org41826e72009-03-30 13:30:57 +00004928 if (function == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004929 // Call the JS runtime function.
ager@chromium.org5c838252010-02-19 08:53:10 +00004930 __ mov(r2, Operand(node->name()));
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004931 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
4932 Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004933 frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00004934 __ ldr(cp, frame_->Context());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004935 frame_->EmitPush(r0);
ager@chromium.org41826e72009-03-30 13:30:57 +00004936 } else {
4937 // Call the C runtime function.
4938 frame_->CallRuntime(function, arg_count);
4939 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004940 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00004941 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004942}
4943
4944
ager@chromium.org7c537e22008-10-16 08:43:32 +00004945void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004946#ifdef DEBUG
4947 int original_height = frame_->height();
4948#endif
ager@chromium.org357bf652010-04-12 11:30:10 +00004949 VirtualFrame::SpilledScope spilled_scope(frame_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004950 Comment cmnt(masm_, "[ UnaryOperation");
4951
4952 Token::Value op = node->op();
4953
4954 if (op == Token::NOT) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004955 LoadCondition(node->expression(), false_target(), true_target(), true);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004956 // LoadCondition may (and usually does) leave a test and branch to
4957 // be emitted by the caller. In that case, negate the condition.
4958 if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004959
4960 } else if (op == Token::DELETE) {
4961 Property* property = node->expression()->AsProperty();
mads.s.ager31e71382008-08-13 09:32:07 +00004962 Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004963 if (property != NULL) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004964 Load(property->obj());
4965 Load(property->key());
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004966 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004967 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004968
mads.s.ager31e71382008-08-13 09:32:07 +00004969 } else if (variable != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004970 Slot* slot = variable->slot();
4971 if (variable->is_global()) {
4972 LoadGlobal();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004973 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004974 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004975 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004976
4977 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
4978 // lookup the context holding the named variable
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004979 frame_->EmitPush(cp);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004980 frame_->EmitPush(Operand(variable->name()));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004981 frame_->CallRuntime(Runtime::kLookupContext, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004982 // r0: context
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004983 frame_->EmitPush(r0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004984 frame_->EmitPush(Operand(variable->name()));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00004985 frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 2);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004986 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004987
mads.s.ager31e71382008-08-13 09:32:07 +00004988 } else {
4989 // Default: Result of deleting non-global, not dynamically
4990 // introduced variables is false.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004991 frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
mads.s.ager31e71382008-08-13 09:32:07 +00004992 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004993
4994 } else {
4995 // Default: Result of deleting expressions is true.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004996 Load(node->expression()); // may have side-effects
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00004997 frame_->Drop();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00004998 frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004999 }
5000
5001 } else if (op == Token::TYPEOF) {
5002 // Special case for loading the typeof expression; see comment on
5003 // LoadTypeofExpression().
5004 LoadTypeofExpression(node->expression());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005005 frame_->CallRuntime(Runtime::kTypeof, 1);
5006 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005007
5008 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005009 bool overwrite =
5010 (node->expression()->AsBinaryOperation() != NULL &&
5011 node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005012 Load(node->expression());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005013 switch (op) {
5014 case Token::NOT:
5015 case Token::DELETE:
5016 case Token::TYPEOF:
5017 UNREACHABLE(); // handled above
5018 break;
5019
5020 case Token::SUB: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005021 VirtualFrame::SpilledScope spilled(frame_);
5022 frame_->EmitPop(r0);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00005023 GenericUnaryOpStub stub(Token::SUB, overwrite);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005024 frame_->CallStub(&stub, 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005025 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005026 break;
5027 }
5028
5029 case Token::BIT_NOT: {
5030 // smi check
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005031 VirtualFrame::SpilledScope spilled(frame_);
5032 frame_->EmitPop(r0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005033 JumpTarget smi_label;
5034 JumpTarget continue_label;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005035 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005036 smi_label.Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005037
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005038 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
5039 frame_->CallStub(&stub, 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005040 continue_label.Jump();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005041
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005042 smi_label.Bind();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005043 __ mvn(r0, Operand(r0));
5044 __ bic(r0, r0, Operand(kSmiTagMask)); // bit-clear inverted smi-tag
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005045 continue_label.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005046 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005047 break;
5048 }
5049
5050 case Token::VOID:
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005051 frame_->Drop();
5052 frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005053 break;
5054
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005055 case Token::ADD: {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005056 VirtualFrame::SpilledScope spilled(frame_);
5057 frame_->EmitPop(r0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005058 // Smi check.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005059 JumpTarget continue_label;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005060 __ tst(r0, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005061 continue_label.Branch(eq);
5062 frame_->EmitPush(r0);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005063 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005064 continue_label.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005065 frame_->EmitPush(r0); // r0 has result
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005066 break;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005067 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005068 default:
5069 UNREACHABLE();
5070 }
5071 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005072 ASSERT(!has_valid_frame() ||
5073 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005074 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005075}
5076
5077
ager@chromium.org7c537e22008-10-16 08:43:32 +00005078void CodeGenerator::VisitCountOperation(CountOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005079#ifdef DEBUG
5080 int original_height = frame_->height();
5081#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005082 Comment cmnt(masm_, "[ CountOperation");
5083
5084 bool is_postfix = node->is_postfix();
5085 bool is_increment = node->op() == Token::INC;
5086
5087 Variable* var = node->expression()->AsVariableProxy()->AsVariable();
5088 bool is_const = (var != NULL && var->mode() == Variable::CONST);
5089
mads.s.ager31e71382008-08-13 09:32:07 +00005090 if (is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005091 frame_->EmitPush(Operand(Smi::FromInt(0)));
mads.s.ager31e71382008-08-13 09:32:07 +00005092 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005093
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005094 // A constant reference is not saved to, so a constant reference is not a
5095 // compound assignment reference.
5096 { Reference target(this, node->expression(), !is_const);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005097 if (target.is_illegal()) {
5098 // Spoof the virtual frame to have the expected height (one higher
5099 // than on entry).
5100 if (!is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005101 frame_->EmitPush(Operand(Smi::FromInt(0)));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005102 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00005103 ASSERT_EQ(original_height + 1, frame_->height());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005104 return;
5105 }
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005106 // This pushes 0, 1 or 2 words on the object to be used later when updating
5107 // the target. It also pushes the current value of the target.
ager@chromium.org357bf652010-04-12 11:30:10 +00005108 target.GetValue();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005109
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005110 JumpTarget slow;
5111 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005112
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005113 // Check for smi operand.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005114 Register value = frame_->PopToRegister();
5115 __ tst(value, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005116 slow.Branch(ne);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005117
5118 // Postfix: Store the old value as the result.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005119 if (is_postfix) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005120 frame_->SetElementAt(value, target.size());
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005121 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005122
5123 // Perform optimistic increment/decrement.
5124 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005125 __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005126 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005127 __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005128 }
5129
5130 // If the increment/decrement didn't overflow, we're done.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005131 exit.Branch(vc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005132
5133 // Revert optimistic increment/decrement.
5134 if (is_increment) {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005135 __ sub(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005136 } else {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005137 __ add(value, value, Operand(Smi::FromInt(1)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005138 }
5139
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005140 // Slow case: Convert to number. At this point the
5141 // value to be incremented is in the value register..
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005142 slow.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005143
5144 // Convert the operand to a number.
5145 frame_->EmitPush(value);
5146
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +00005147 {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005148 VirtualFrame::SpilledScope spilled(frame_);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005149 frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005150
5151 if (is_postfix) {
5152 // Postfix: store to result (on the stack).
5153 __ str(r0, frame_->ElementAt(target.size()));
5154 }
5155
5156 // Compute the new value.
5157 frame_->EmitPush(r0);
5158 frame_->EmitPush(Operand(Smi::FromInt(1)));
5159 if (is_increment) {
5160 frame_->CallRuntime(Runtime::kNumberAdd, 2);
5161 } else {
5162 frame_->CallRuntime(Runtime::kNumberSub, 2);
5163 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005164 }
5165
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005166 __ Move(value, r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005167 // Store the new value in the target if not const.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005168 // At this point the answer is in the value register.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005169 exit.Bind();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005170 frame_->EmitPush(value);
5171 // Set the target with the result, leaving the result on
5172 // top of the stack. Removes the target from the stack if
5173 // it has a non-zero size.
ager@chromium.org7c537e22008-10-16 08:43:32 +00005174 if (!is_const) target.SetValue(NOT_CONST_INIT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005175 }
5176
5177 // Postfix: Discard the new value and use the old.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005178 if (is_postfix) frame_->Pop();
ager@chromium.orgac091b72010-05-05 07:34:42 +00005179 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005180}
5181
5182
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005183void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005184 // According to ECMA-262 section 11.11, page 58, the binary logical
5185 // operators must yield the result of one of the two expressions
5186 // before any ToBoolean() conversions. This means that the value
5187 // produced by a && or || operator is not necessarily a boolean.
5188
5189 // NOTE: If the left hand side produces a materialized value (not in
5190 // the CC register), we force the right hand side to do the
5191 // same. This is necessary because we may have to branch to the exit
5192 // after evaluating the left hand side (due to the shortcut
5193 // semantics), but the compiler must (statically) know if the result
5194 // of compiling the binary operation is materialized or not.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005195 VirtualFrame::SpilledScope spilled_scope(frame_);
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005196 if (node->op() == Token::AND) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005197 JumpTarget is_true;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005198 LoadCondition(node->left(), &is_true, false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005199 if (has_valid_frame() && !has_cc()) {
5200 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005201 JumpTarget pop_and_continue;
5202 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005203
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005204 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005205 // Avoid popping the result if it converts to 'false' using the
5206 // standard ToBoolean() conversion as described in ECMA-262,
5207 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005208 ToBoolean(&pop_and_continue, &exit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005209 Branch(false, &exit);
5210
5211 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005212 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005213 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005214
5215 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005216 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005217 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005218
5219 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005220 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005221 } else if (has_cc() || is_true.is_linked()) {
5222 // The left-hand side is either (a) partially compiled to
5223 // control flow with a final branch left to emit or (b) fully
5224 // compiled to control flow and possibly true.
5225 if (has_cc()) {
5226 Branch(false, false_target());
5227 }
5228 is_true.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005229 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005230 } else {
5231 // Nothing to do.
5232 ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005233 }
5234
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005235 } else {
5236 ASSERT(node->op() == Token::OR);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005237 JumpTarget is_false;
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005238 LoadCondition(node->left(), true_target(), &is_false, false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005239 if (has_valid_frame() && !has_cc()) {
5240 // The left-hand side result is on top of the virtual frame.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005241 JumpTarget pop_and_continue;
5242 JumpTarget exit;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005243
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005244 frame_->Dup();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005245 // Avoid popping the result if it converts to 'true' using the
5246 // standard ToBoolean() conversion as described in ECMA-262,
5247 // section 9.2, page 30.
mads.s.ager31e71382008-08-13 09:32:07 +00005248 ToBoolean(&exit, &pop_and_continue);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005249 Branch(true, &exit);
5250
5251 // Pop the result of evaluating the first part.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005252 pop_and_continue.Bind();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005253 frame_->Pop();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005254
5255 // Evaluate right side expression.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005256 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005257 Load(node->right());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005258
5259 // Exit (always with a materialized value).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005260 exit.Bind();
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005261 } else if (has_cc() || is_false.is_linked()) {
5262 // The left-hand side is either (a) partially compiled to
5263 // control flow with a final branch left to emit or (b) fully
5264 // compiled to control flow and possibly false.
5265 if (has_cc()) {
5266 Branch(true, true_target());
5267 }
5268 is_false.Bind();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005269 LoadCondition(node->right(), true_target(), false_target(), false);
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005270 } else {
5271 // Nothing to do.
5272 ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005273 }
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005274 }
5275}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005276
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005277
5278void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
5279#ifdef DEBUG
5280 int original_height = frame_->height();
5281#endif
ager@chromium.orgb26c50a2010-03-26 09:27:16 +00005282 Comment cmnt(masm_, "[ BinaryOperation");
5283
5284 if (node->op() == Token::AND || node->op() == Token::OR) {
5285 GenerateLogicalBooleanOperation(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005286 } else {
5287 // Optimize for the case where (at least) one of the expressions
5288 // is a literal small integer.
5289 Literal* lliteral = node->left()->AsLiteral();
5290 Literal* rliteral = node->right()->AsLiteral();
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005291 // NOTE: The code below assumes that the slow cases (calls to runtime)
5292 // never return a constant/immutable object.
5293 bool overwrite_left =
5294 (node->left()->AsBinaryOperation() != NULL &&
5295 node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
5296 bool overwrite_right =
5297 (node->right()->AsBinaryOperation() != NULL &&
5298 node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005299
5300 if (rliteral != NULL && rliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005301 VirtualFrame::RegisterAllocationScope scope(this);
5302 Load(node->left());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005303 SmiOperation(node->op(),
5304 rliteral->handle(),
5305 false,
5306 overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005307 } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005308 VirtualFrame::RegisterAllocationScope scope(this);
5309 Load(node->right());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00005310 SmiOperation(node->op(),
5311 lliteral->handle(),
5312 true,
5313 overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005314 } else {
ager@chromium.org357bf652010-04-12 11:30:10 +00005315 VirtualFrame::RegisterAllocationScope scope(this);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005316 OverwriteMode overwrite_mode = NO_OVERWRITE;
5317 if (overwrite_left) {
5318 overwrite_mode = OVERWRITE_LEFT;
5319 } else if (overwrite_right) {
5320 overwrite_mode = OVERWRITE_RIGHT;
5321 }
ager@chromium.org357bf652010-04-12 11:30:10 +00005322 Load(node->left());
5323 Load(node->right());
5324 VirtualFrameBinaryOperation(node->op(), overwrite_mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005325 }
5326 }
kasperl@chromium.orge959c182009-07-27 08:59:04 +00005327 ASSERT(!has_valid_frame() ||
5328 (has_cc() && frame_->height() == original_height) ||
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005329 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005330}
5331
5332
ager@chromium.org7c537e22008-10-16 08:43:32 +00005333void CodeGenerator::VisitThisFunction(ThisFunction* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005334#ifdef DEBUG
5335 int original_height = frame_->height();
5336#endif
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00005337 frame_->EmitPush(MemOperand(frame_->Function()));
ager@chromium.orgac091b72010-05-05 07:34:42 +00005338 ASSERT_EQ(original_height + 1, frame_->height());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005339}
5340
5341
ager@chromium.org7c537e22008-10-16 08:43:32 +00005342void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005343#ifdef DEBUG
5344 int original_height = frame_->height();
5345#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005346 Comment cmnt(masm_, "[ CompareOperation");
5347
ager@chromium.org357bf652010-04-12 11:30:10 +00005348 VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
5349
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005350 // Get the expressions from the node.
5351 Expression* left = node->left();
5352 Expression* right = node->right();
5353 Token::Value op = node->op();
5354
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005355 // To make null checks efficient, we check if either left or right is the
5356 // literal 'null'. If so, we optimize the code by inlining a null check
5357 // instead of calling the (very) general runtime routine for checking
5358 // equality.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005359 if (op == Token::EQ || op == Token::EQ_STRICT) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005360 bool left_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005361 left->AsLiteral() != NULL && left->AsLiteral()->IsNull();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005362 bool right_is_null =
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005363 right->AsLiteral() != NULL && right->AsLiteral()->IsNull();
5364 // The 'null' value can only be equal to 'null' or 'undefined'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005365 if (left_is_null || right_is_null) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005366 Load(left_is_null ? right : left);
5367 Register tos = frame_->PopToRegister();
5368 // JumpTargets can't cope with register allocation yet.
5369 frame_->SpillAll();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005370 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005371 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005372
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005373 // The 'null' value is only equal to 'undefined' if using non-strict
5374 // comparisons.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005375 if (op != Token::EQ_STRICT) {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005376 true_target()->Branch(eq);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005377
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005378 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005379 __ cmp(tos, Operand(ip));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005380 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005381
ager@chromium.org357bf652010-04-12 11:30:10 +00005382 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005383 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005384
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005385 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005386 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5387 __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
5388 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5389 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005390 }
5391
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005392 cc_reg_ = eq;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005393 ASSERT(has_cc() && frame_->height() == original_height);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005394 return;
5395 }
5396 }
5397
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005398 // To make typeof testing for natives implemented in JavaScript really
5399 // efficient, we generate special code for expressions of the form:
5400 // 'typeof <expression> == <string>'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005401 UnaryOperation* operation = left->AsUnaryOperation();
5402 if ((op == Token::EQ || op == Token::EQ_STRICT) &&
5403 (operation != NULL && operation->op() == Token::TYPEOF) &&
5404 (right->AsLiteral() != NULL &&
5405 right->AsLiteral()->handle()->IsString())) {
5406 Handle<String> check(String::cast(*right->AsLiteral()->handle()));
5407
ager@chromium.org357bf652010-04-12 11:30:10 +00005408 // Load the operand, move it to a register.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005409 LoadTypeofExpression(operation->expression());
ager@chromium.org357bf652010-04-12 11:30:10 +00005410 Register tos = frame_->PopToRegister();
5411
5412 // JumpTargets can't cope with register allocation yet.
5413 frame_->SpillAll();
5414
5415 Register scratch = VirtualFrame::scratch0();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005416
5417 if (check->Equals(Heap::number_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005418 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005419 true_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005420 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005421 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005422 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005423 cc_reg_ = eq;
5424
5425 } else if (check->Equals(Heap::string_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005426 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005427 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005428
ager@chromium.org357bf652010-04-12 11:30:10 +00005429 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005430
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005431 // It can be an undetectable string object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005432 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5433 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5434 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005435 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005436
ager@chromium.org357bf652010-04-12 11:30:10 +00005437 __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
5438 __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005439 cc_reg_ = lt;
5440
5441 } else if (check->Equals(Heap::boolean_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005442 __ LoadRoot(ip, Heap::kTrueValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005443 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005444 true_target()->Branch(eq);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005445 __ LoadRoot(ip, Heap::kFalseValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005446 __ cmp(tos, ip);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005447 cc_reg_ = eq;
5448
5449 } else if (check->Equals(Heap::undefined_symbol())) {
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005450 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005451 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005452 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005453
ager@chromium.org357bf652010-04-12 11:30:10 +00005454 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005455 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005457 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005458 __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
5459 __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
5460 __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
5461 __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005462
5463 cc_reg_ = eq;
5464
5465 } else if (check->Equals(Heap::function_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005466 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005467 false_target()->Branch(eq);
ager@chromium.org357bf652010-04-12 11:30:10 +00005468 Register map_reg = scratch;
5469 __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005470 true_target()->Branch(eq);
5471 // Regular expressions are callable so typeof == 'function'.
ager@chromium.org357bf652010-04-12 11:30:10 +00005472 __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005473 cc_reg_ = eq;
5474
5475 } else if (check->Equals(Heap::object_symbol())) {
ager@chromium.org357bf652010-04-12 11:30:10 +00005476 __ tst(tos, Operand(kSmiTagMask));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005477 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005478
ager@chromium.orgab99eea2009-08-25 07:05:41 +00005479 __ LoadRoot(ip, Heap::kNullValueRootIndex);
ager@chromium.org357bf652010-04-12 11:30:10 +00005480 __ cmp(tos, ip);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005481 true_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005482
ager@chromium.org357bf652010-04-12 11:30:10 +00005483 Register map_reg = scratch;
5484 __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00005485 false_target()->Branch(eq);
5486
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005487 // It can be an undetectable object.
ager@chromium.org357bf652010-04-12 11:30:10 +00005488 __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
5489 __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
5490 __ cmp(tos, Operand(1 << Map::kIsUndetectable));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005491 false_target()->Branch(eq);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005492
ager@chromium.org357bf652010-04-12 11:30:10 +00005493 __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
5494 __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005495 false_target()->Branch(lt);
ager@chromium.org357bf652010-04-12 11:30:10 +00005496 __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005497 cc_reg_ = le;
5498
5499 } else {
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005500 // Uncommon case: typeof testing against a string literal that is
5501 // never returned from the typeof operator.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005502 false_target()->Jump();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005503 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005504 ASSERT(!has_valid_frame() ||
5505 (has_cc() && frame_->height() == original_height));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005506 return;
5507 }
5508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005509 switch (op) {
5510 case Token::EQ:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005511 Comparison(eq, left, right, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005512 break;
5513
5514 case Token::LT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005515 Comparison(lt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005516 break;
5517
5518 case Token::GT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005519 Comparison(gt, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005520 break;
5521
5522 case Token::LTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005523 Comparison(le, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005524 break;
5525
5526 case Token::GTE:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005527 Comparison(ge, left, right);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005528 break;
5529
5530 case Token::EQ_STRICT:
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005531 Comparison(eq, left, right, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005532 break;
5533
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005534 case Token::IN: {
ager@chromium.org357bf652010-04-12 11:30:10 +00005535 VirtualFrame::SpilledScope scope(frame_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005536 Load(left);
5537 Load(right);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00005538 frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005539 frame_->EmitPush(r0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005541 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005542
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005543 case Token::INSTANCEOF: {
ager@chromium.org357bf652010-04-12 11:30:10 +00005544 VirtualFrame::SpilledScope scope(frame_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005545 Load(left);
5546 Load(right);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005547 InstanceofStub stub;
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005548 frame_->CallStub(&stub, 2);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005549 // At this point if instanceof succeeded then r0 == 0.
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00005550 __ tst(r0, Operand(r0));
ager@chromium.org7c537e22008-10-16 08:43:32 +00005551 cc_reg_ = eq;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 break;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005553 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005554
5555 default:
5556 UNREACHABLE();
5557 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005558 ASSERT((has_cc() && frame_->height() == original_height) ||
5559 (!has_cc() && frame_->height() == original_height + 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005560}
5561
5562
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005563class DeferredReferenceGetNamedValue: public DeferredCode {
5564 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005565 explicit DeferredReferenceGetNamedValue(Register receiver,
5566 Handle<String> name)
5567 : receiver_(receiver), name_(name) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005568 set_comment("[ DeferredReferenceGetNamedValue");
5569 }
5570
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005571 virtual void Generate();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005572
5573 private:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005574 Register receiver_;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005575 Handle<String> name_;
5576};
5577
5578
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005579// Convention for this is that on entry the receiver is in a register that
5580// is not used by the stack. On exit the answer is found in that same
5581// register and the stack has the same height.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005582void DeferredReferenceGetNamedValue::Generate() {
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005583#ifdef DEBUG
5584 int expected_height = frame_state()->frame()->height();
5585#endif
5586 VirtualFrame copied_frame(*frame_state()->frame());
5587 copied_frame.SpillAll();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005588
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005589 Register scratch1 = VirtualFrame::scratch0();
5590 Register scratch2 = VirtualFrame::scratch1();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005591 ASSERT(!receiver_.is(scratch1) && !receiver_.is(scratch2));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005592 __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2);
5593 __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2);
5594
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005595 // Ensure receiver in r0 and name in r2 to match load ic calling convention.
5596 __ Move(r0, receiver_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005597 __ mov(r2, Operand(name_));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005598
5599 // The rest of the instructions in the deferred code must be together.
5600 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5601 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
5602 __ Call(ic, RelocInfo::CODE_TARGET);
5603 // The call must be followed by a nop(1) instruction to indicate that the
5604 // in-object has been inlined.
5605 __ nop(PROPERTY_ACCESS_INLINED);
5606
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005607 // At this point the answer is in r0. We move it to the expected register
5608 // if necessary.
5609 __ Move(receiver_, r0);
5610
5611 // Now go back to the frame that we entered with. This will not overwrite
5612 // the receiver register since that register was not in use when we came
5613 // in. The instructions emitted by this merge are skipped over by the
5614 // inline load patching mechanism when looking for the branch instruction
5615 // that tells it where the code to patch is.
5616 copied_frame.MergeTo(frame_state()->frame());
5617
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005618 // Block the constant pool for one more instruction after leaving this
5619 // constant pool block scope to include the branch instruction ending the
5620 // deferred code.
5621 __ BlockConstPoolFor(1);
5622 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005623 ASSERT_EQ(expected_height, frame_state()->frame()->height());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005624}
5625
5626
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005627class DeferredReferenceGetKeyedValue: public DeferredCode {
5628 public:
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005629 DeferredReferenceGetKeyedValue(Register key, Register receiver)
5630 : key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005631 set_comment("[ DeferredReferenceGetKeyedValue");
5632 }
5633
5634 virtual void Generate();
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005635
5636 private:
5637 Register key_;
5638 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005639};
5640
5641
5642void DeferredReferenceGetKeyedValue::Generate() {
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005643 ASSERT((key_.is(r0) && receiver_.is(r1)) ||
5644 (key_.is(r1) && receiver_.is(r0)));
5645
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005646 Register scratch1 = VirtualFrame::scratch0();
5647 Register scratch2 = VirtualFrame::scratch1();
5648 __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2);
5649 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2);
5650
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005651 // Ensure key in r0 and receiver in r1 to match keyed load ic calling
5652 // convention.
5653 if (key_.is(r1)) {
5654 __ Swap(r0, r1, ip);
5655 }
5656
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005657 // The rest of the instructions in the deferred code must be together.
5658 { Assembler::BlockConstPoolScope block_const_pool(masm_);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00005659 // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005660 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
5661 __ Call(ic, RelocInfo::CODE_TARGET);
5662 // The call must be followed by a nop instruction to indicate that the
5663 // keyed load has been inlined.
5664 __ nop(PROPERTY_ACCESS_INLINED);
5665
5666 // Block the constant pool for one more instruction after leaving this
5667 // constant pool block scope to include the branch instruction ending the
5668 // deferred code.
5669 __ BlockConstPoolFor(1);
5670 }
5671}
5672
5673
5674class DeferredReferenceSetKeyedValue: public DeferredCode {
5675 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005676 DeferredReferenceSetKeyedValue(Register value,
5677 Register key,
5678 Register receiver)
5679 : value_(value), key_(key), receiver_(receiver) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005680 set_comment("[ DeferredReferenceSetKeyedValue");
5681 }
5682
5683 virtual void Generate();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005684
5685 private:
5686 Register value_;
5687 Register key_;
5688 Register receiver_;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005689};
5690
5691
5692void DeferredReferenceSetKeyedValue::Generate() {
5693 Register scratch1 = VirtualFrame::scratch0();
5694 Register scratch2 = VirtualFrame::scratch1();
5695 __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2);
5696 __ IncrementCounter(
5697 &Counters::keyed_store_inline_miss, 1, scratch1, scratch2);
5698
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005699 // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
5700 // calling convention.
5701 if (value_.is(r1)) {
5702 __ Swap(r0, r1, ip);
5703 }
5704 ASSERT(receiver_.is(r2));
5705
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005706 // The rest of the instructions in the deferred code must be together.
5707 { Assembler::BlockConstPoolScope block_const_pool(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005708 // Call keyed store IC. It has the arguments value, key and receiver in r0,
5709 // r1 and r2.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005710 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
5711 __ Call(ic, RelocInfo::CODE_TARGET);
5712 // The call must be followed by a nop instruction to indicate that the
5713 // keyed store has been inlined.
5714 __ nop(PROPERTY_ACCESS_INLINED);
5715
5716 // Block the constant pool for one more instruction after leaving this
5717 // constant pool block scope to include the branch instruction ending the
5718 // deferred code.
5719 __ BlockConstPoolFor(1);
5720 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005721}
5722
5723
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005724// Consumes the top of stack (the receiver) and pushes the result instead.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005725void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
5726 if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
5727 Comment cmnt(masm(), "[ Load from named Property");
5728 // Setup the name register and call load IC.
ager@chromium.orgac091b72010-05-05 07:34:42 +00005729 frame_->CallLoadIC(name,
5730 is_contextual
5731 ? RelocInfo::CODE_TARGET_CONTEXT
5732 : RelocInfo::CODE_TARGET);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005733 frame_->EmitPush(r0); // Push answer.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005734 } else {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005735 // Inline the in-object property case.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005736 Comment cmnt(masm(), "[ Inlined named property load");
5737
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005738 // Counter will be decremented in the deferred code. Placed here to avoid
5739 // having it in the instruction stream below where patching will occur.
5740 __ IncrementCounter(&Counters::named_load_inline, 1,
5741 frame_->scratch0(), frame_->scratch1());
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005742
5743 // The following instructions are the inlined load of an in-object property.
5744 // Parts of this code is patched, so the exact instructions generated needs
5745 // to be fixed. Therefore the instruction pool is blocked when generating
5746 // this code
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005747
5748 // Load the receiver from the stack.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005749 Register receiver = frame_->PopToRegister();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005750
5751 DeferredReferenceGetNamedValue* deferred =
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005752 new DeferredReferenceGetNamedValue(receiver, name);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005753
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005754#ifdef DEBUG
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005755 int kInlinedNamedLoadInstructions = 7;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005756 Label check_inlined_codesize;
5757 masm_->bind(&check_inlined_codesize);
5758#endif
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005759
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005760 { Assembler::BlockConstPoolScope block_const_pool(masm_);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005761 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005762 __ tst(receiver, Operand(kSmiTagMask));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005763 deferred->Branch(eq);
5764
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005765 Register scratch = VirtualFrame::scratch0();
5766 Register scratch2 = VirtualFrame::scratch1();
5767
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005768 // Check the map. The null map used below is patched by the inline cache
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005769 // code. Therefore we can't use a LoadRoot call.
5770 __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
5771 __ mov(scratch2, Operand(Factory::null_value()));
5772 __ cmp(scratch, scratch2);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005773 deferred->Branch(ne);
5774
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005775 // Initially use an invalid index. The index will be patched by the
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005776 // inline cache code.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005777 __ ldr(receiver, MemOperand(receiver, 0));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005778
5779 // Make sure that the expected number of instructions are generated.
5780 ASSERT_EQ(kInlinedNamedLoadInstructions,
5781 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005782 }
5783
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005784 deferred->BindExit();
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005785 // At this point the receiver register has the result, either from the
5786 // deferred code or from the inlined code.
5787 frame_->EmitPush(receiver);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00005788 }
5789}
5790
5791
ager@chromium.orgac091b72010-05-05 07:34:42 +00005792void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
5793#ifdef DEBUG
5794 int expected_height = frame_->height() - (is_contextual ? 1 : 2);
5795#endif
5796 frame_->CallStoreIC(name, is_contextual);
5797
5798 ASSERT_EQ(expected_height, frame_->height());
5799}
5800
5801
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005802void CodeGenerator::EmitKeyedLoad() {
5803 if (loop_nesting() == 0) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005804 Comment cmnt(masm_, "[ Load from keyed property");
5805 frame_->CallKeyedLoadIC();
5806 } else {
5807 // Inline the keyed load.
5808 Comment cmnt(masm_, "[ Inlined load from keyed property");
5809
5810 // Counter will be decremented in the deferred code. Placed here to avoid
5811 // having it in the instruction stream below where patching will occur.
5812 __ IncrementCounter(&Counters::keyed_load_inline, 1,
5813 frame_->scratch0(), frame_->scratch1());
5814
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005815 // Load the key and receiver from the stack.
5816 Register key = frame_->PopToRegister();
5817 Register receiver = frame_->PopToRegister(key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005818 VirtualFrame::SpilledScope spilled(frame_);
5819
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005820 // The deferred code expects key and receiver in registers.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005821 DeferredReferenceGetKeyedValue* deferred =
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005822 new DeferredReferenceGetKeyedValue(key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005823
5824 // Check that the receiver is a heap object.
5825 __ tst(receiver, Operand(kSmiTagMask));
5826 deferred->Branch(eq);
5827
5828 // The following instructions are the part of the inlined load keyed
5829 // property code which can be patched. Therefore the exact number of
5830 // instructions generated need to be fixed, so the constant pool is blocked
5831 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005832 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5833 Register scratch1 = VirtualFrame::scratch0();
5834 Register scratch2 = VirtualFrame::scratch1();
5835 // Check the map. The null map used below is patched by the inline cache
5836 // code.
5837 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005838#ifdef DEBUG
5839 Label check_inlined_codesize;
5840 masm_->bind(&check_inlined_codesize);
5841#endif
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005842 __ mov(scratch2, Operand(Factory::null_value()));
5843 __ cmp(scratch1, scratch2);
5844 deferred->Branch(ne);
5845
5846 // Check that the key is a smi.
5847 __ tst(key, Operand(kSmiTagMask));
5848 deferred->Branch(ne);
5849
5850 // Get the elements array from the receiver and check that it
5851 // is not a dictionary.
5852 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
5853 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
5854 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
5855 __ cmp(scratch2, ip);
5856 deferred->Branch(ne);
5857
5858 // Check that key is within bounds. Use unsigned comparison to handle
5859 // negative keys.
5860 __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00005861 __ cmp(scratch2, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005862 deferred->Branch(ls); // Unsigned less equal.
5863
5864 // Load and check that the result is not the hole (key is a smi).
5865 __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
5866 __ add(scratch1,
5867 scratch1,
5868 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005869 __ ldr(scratch1,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005870 MemOperand(scratch1, key, LSL,
5871 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005872 __ cmp(scratch1, scratch2);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005873 deferred->Branch(eq);
5874
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00005875 __ mov(r0, scratch1);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005876 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005877 ASSERT_EQ(kInlinedKeyedLoadInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005878 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
5879 }
5880
5881 deferred->BindExit();
5882 }
5883}
5884
5885
5886void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005887 // Generate inlined version of the keyed store if the code is in a loop
5888 // and the key is likely to be a smi.
5889 if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
5890 // Inline the keyed store.
5891 Comment cmnt(masm_, "[ Inlined store to keyed property");
5892
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005893 Register scratch1 = VirtualFrame::scratch0();
5894 Register scratch2 = VirtualFrame::scratch1();
5895 Register scratch3 = r3;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005896
5897 // Counter will be decremented in the deferred code. Placed here to avoid
5898 // having it in the instruction stream below where patching will occur.
5899 __ IncrementCounter(&Counters::keyed_store_inline, 1,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005900 scratch1, scratch2);
5901
5902 // Load the value, key and receiver from the stack.
5903 Register value = frame_->PopToRegister();
5904 Register key = frame_->PopToRegister(value);
5905 Register receiver = r2;
5906 frame_->EmitPop(receiver);
5907 VirtualFrame::SpilledScope spilled(frame_);
5908
5909 // The deferred code expects value, key and receiver in registers.
5910 DeferredReferenceSetKeyedValue* deferred =
5911 new DeferredReferenceSetKeyedValue(value, key, receiver);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005912
5913 // Check that the value is a smi. As this inlined code does not set the
5914 // write barrier it is only possible to store smi values.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005915 __ tst(value, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005916 deferred->Branch(ne);
5917
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005918 // Check that the key is a smi.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005919 __ tst(key, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005920 deferred->Branch(ne);
5921
5922 // Check that the receiver is a heap object.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005923 __ tst(receiver, Operand(kSmiTagMask));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005924 deferred->Branch(eq);
5925
5926 // Check that the receiver is a JSArray.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005927 __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005928 deferred->Branch(ne);
5929
5930 // Check that the key is within bounds. Both the key and the length of
5931 // the JSArray are smis. Use unsigned comparison to handle negative keys.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005932 __ ldr(scratch1, FieldMemOperand(receiver, JSArray::kLengthOffset));
5933 __ cmp(scratch1, key);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005934 deferred->Branch(ls); // Unsigned less equal.
5935
5936 // The following instructions are the part of the inlined store keyed
5937 // property code which can be patched. Therefore the exact number of
5938 // instructions generated need to be fixed, so the constant pool is blocked
5939 // while generating this code.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005940 { Assembler::BlockConstPoolScope block_const_pool(masm_);
5941 // Get the elements array from the receiver and check that it
5942 // is not a dictionary.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005943 __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
5944 __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005945 // Read the fixed array map from the constant pool (not from the root
5946 // array) so that the value can be patched. When debugging, we patch this
5947 // comparison to always fail so that we will hit the IC call in the
5948 // deferred code which will allow the debugger to break for fast case
5949 // stores.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005950#ifdef DEBUG
5951 Label check_inlined_codesize;
5952 masm_->bind(&check_inlined_codesize);
5953#endif
5954 __ mov(scratch3, Operand(Factory::fixed_array_map()));
5955 __ cmp(scratch2, scratch3);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005956 deferred->Branch(ne);
5957
5958 // Store the value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005959 __ add(scratch1, scratch1,
5960 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
5961 __ str(value,
5962 MemOperand(scratch1, key, LSL,
5963 kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005964
5965 // Make sure that the expected number of instructions are generated.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00005966 ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00005967 masm_->InstructionsGeneratedSince(&check_inlined_codesize));
5968 }
5969
5970 deferred->BindExit();
5971 } else {
5972 frame()->CallKeyedStoreIC();
5973 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00005974}
5975
5976
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005977#ifdef DEBUG
5978bool CodeGenerator::HasValidEntryRegisters() { return true; }
5979#endif
5980
5981
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005982#undef __
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005983#define __ ACCESS_MASM(masm)
5984
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005985
ager@chromium.org7c537e22008-10-16 08:43:32 +00005986Handle<String> Reference::GetName() {
5987 ASSERT(type_ == NAMED);
5988 Property* property = expression_->AsProperty();
5989 if (property == NULL) {
5990 // Global variable reference treated as a named property reference.
5991 VariableProxy* proxy = expression_->AsVariableProxy();
5992 ASSERT(proxy->AsVariable() != NULL);
5993 ASSERT(proxy->AsVariable()->is_global());
5994 return proxy->name();
5995 } else {
5996 Literal* raw_name = property->key()->AsLiteral();
5997 ASSERT(raw_name != NULL);
5998 return Handle<String>(String::cast(*raw_name->handle()));
5999 }
6000}
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006001
ager@chromium.org7c537e22008-10-16 08:43:32 +00006002
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006003void Reference::DupIfPersist() {
6004 if (persist_after_get_) {
6005 switch (type_) {
6006 case KEYED:
6007 cgen_->frame()->Dup2();
6008 break;
6009 case NAMED:
6010 cgen_->frame()->Dup();
6011 // Fall through.
6012 case UNLOADED:
6013 case ILLEGAL:
6014 case SLOT:
6015 // Do nothing.
6016 ;
6017 }
6018 } else {
6019 set_unloaded();
6020 }
6021}
6022
6023
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006024void Reference::GetValue() {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006025 ASSERT(cgen_->HasValidEntryRegisters());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006026 ASSERT(!is_illegal());
6027 ASSERT(!cgen_->has_cc());
6028 MacroAssembler* masm = cgen_->masm();
6029 Property* property = expression_->AsProperty();
6030 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006031 cgen_->CodeForSourcePosition(property->position());
ager@chromium.org7c537e22008-10-16 08:43:32 +00006032 }
6033
6034 switch (type_) {
6035 case SLOT: {
6036 Comment cmnt(masm, "[ Load from Slot");
6037 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
6038 ASSERT(slot != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006039 DupIfPersist();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006040 cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006041 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006042 }
6043
ager@chromium.org7c537e22008-10-16 08:43:32 +00006044 case NAMED: {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006045 Variable* var = expression_->AsVariableProxy()->AsVariable();
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006046 bool is_global = var != NULL;
6047 ASSERT(!is_global || var->is_global());
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006048 Handle<String> name = GetName();
6049 DupIfPersist();
6050 cgen_->EmitNamedLoad(name, is_global);
ager@chromium.org7c537e22008-10-16 08:43:32 +00006051 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006052 }
6053
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006054 case KEYED: {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006055 ASSERT(property != NULL);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006056 DupIfPersist();
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006057 cgen_->EmitKeyedLoad();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006058 cgen_->frame()->EmitPush(r0);
6059 break;
6060 }
6061
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006062 default:
6063 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006064 }
6065}
6066
6067
ager@chromium.org7c537e22008-10-16 08:43:32 +00006068void Reference::SetValue(InitState init_state) {
6069 ASSERT(!is_illegal());
6070 ASSERT(!cgen_->has_cc());
6071 MacroAssembler* masm = cgen_->masm();
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006072 VirtualFrame* frame = cgen_->frame();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006073 Property* property = expression_->AsProperty();
6074 if (property != NULL) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006075 cgen_->CodeForSourcePosition(property->position());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006076 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006077
ager@chromium.org7c537e22008-10-16 08:43:32 +00006078 switch (type_) {
6079 case SLOT: {
6080 Comment cmnt(masm, "[ Store to Slot");
6081 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006082 cgen_->StoreToSlot(slot, init_state);
ager@chromium.orgac091b72010-05-05 07:34:42 +00006083 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006084 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006085 }
6086
ager@chromium.org7c537e22008-10-16 08:43:32 +00006087 case NAMED: {
6088 Comment cmnt(masm, "[ Store to named Property");
ager@chromium.orgac091b72010-05-05 07:34:42 +00006089 cgen_->EmitNamedStore(GetName(), false);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006090 frame->EmitPush(r0);
ager@chromium.org5c838252010-02-19 08:53:10 +00006091 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006092 break;
6093 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006094
ager@chromium.org7c537e22008-10-16 08:43:32 +00006095 case KEYED: {
6096 Comment cmnt(masm, "[ Store to keyed Property");
6097 Property* property = expression_->AsProperty();
6098 ASSERT(property != NULL);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006099 cgen_->CodeForSourcePosition(property->position());
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006100 cgen_->EmitKeyedStore(property->key()->type());
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00006101 frame->EmitPush(r0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00006102 set_unloaded();
ager@chromium.org7c537e22008-10-16 08:43:32 +00006103 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006104 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00006105
6106 default:
6107 UNREACHABLE();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00006108 }
6109}
6110
6111
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006112void FastNewClosureStub::Generate(MacroAssembler* masm) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006113 // Create a new closure from the given function info in new
6114 // space. Set the context to the current context in cp.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006115 Label gc;
6116
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006117 // Pop the function info from the stack.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006118 __ pop(r3);
6119
6120 // Attempt to allocate new JSFunction in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006121 __ AllocateInNewSpace(JSFunction::kSize,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006122 r0,
6123 r1,
6124 r2,
6125 &gc,
6126 TAG_OBJECT);
6127
6128 // Compute the function map in the current global context and set that
6129 // as the map of the allocated object.
6130 __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6131 __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
6132 __ ldr(r2, MemOperand(r2, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
6133 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
6134
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006135 // Initialize the rest of the function. We don't have to update the
6136 // write barrier because the allocated object is in new space.
6137 __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
6138 __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
6139 __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
6140 __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
6141 __ str(r2, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
6142 __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
6143 __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
6144 __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006145
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006146 // Return result. The argument function info has been popped already.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006147 __ Ret();
6148
6149 // Create a new closure through the slower runtime call.
6150 __ bind(&gc);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006151 __ Push(cp, r3);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006152 __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006153}
6154
6155
6156void FastNewContextStub::Generate(MacroAssembler* masm) {
6157 // Try to allocate the context in new space.
6158 Label gc;
6159 int length = slots_ + Context::MIN_CONTEXT_SLOTS;
6160
6161 // Attempt to allocate the context in new space.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006162 __ AllocateInNewSpace(FixedArray::SizeFor(length),
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006163 r0,
6164 r1,
6165 r2,
6166 &gc,
6167 TAG_OBJECT);
6168
6169 // Load the function from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00006170 __ ldr(r3, MemOperand(sp, 0));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006171
6172 // Setup the object header.
6173 __ LoadRoot(r2, Heap::kContextMapRootIndex);
6174 __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006175 __ mov(r2, Operand(Smi::FromInt(length)));
6176 __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset));
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006177
6178 // Setup the fixed slots.
6179 __ mov(r1, Operand(Smi::FromInt(0)));
6180 __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
6181 __ str(r0, MemOperand(r0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
6182 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
6183 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
6184
6185 // Copy the global object from the surrounding context.
6186 __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
6187 __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX)));
6188
6189 // Initialize the rest of the slots to undefined.
6190 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
6191 for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
6192 __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
6193 }
6194
6195 // Remove the on-stack argument and return.
6196 __ mov(cp, r0);
6197 __ pop();
6198 __ Ret();
6199
6200 // Need to collect. Call into runtime system.
6201 __ bind(&gc);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006202 __ TailCallRuntime(Runtime::kNewContext, 1, 1);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006203}
6204
6205
ager@chromium.org5c838252010-02-19 08:53:10 +00006206void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
6207 // Stack layout on entry:
6208 //
6209 // [sp]: constant elements.
6210 // [sp + kPointerSize]: literal index.
6211 // [sp + (2 * kPointerSize)]: literals array.
6212
6213 // All sizes here are multiples of kPointerSize.
6214 int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
6215 int size = JSArray::kSize + elements_size;
6216
6217 // Load boilerplate object into r3 and check if we need to create a
6218 // boilerplate.
6219 Label slow_case;
6220 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
6221 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
6222 __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
6223 __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
6224 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
6225 __ cmp(r3, ip);
6226 __ b(eq, &slow_case);
6227
6228 // Allocate both the JS array and the elements array in one big
6229 // allocation. This avoids multiple limit checks.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00006230 __ AllocateInNewSpace(size,
ager@chromium.org5c838252010-02-19 08:53:10 +00006231 r0,
6232 r1,
6233 r2,
6234 &slow_case,
6235 TAG_OBJECT);
6236
6237 // Copy the JS array part.
6238 for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
6239 if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
6240 __ ldr(r1, FieldMemOperand(r3, i));
6241 __ str(r1, FieldMemOperand(r0, i));
6242 }
6243 }
6244
6245 if (length_ > 0) {
6246 // Get hold of the elements array of the boilerplate and setup the
6247 // elements pointer in the resulting object.
6248 __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
6249 __ add(r2, r0, Operand(JSArray::kSize));
6250 __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
6251
6252 // Copy the elements array.
6253 for (int i = 0; i < elements_size; i += kPointerSize) {
6254 __ ldr(r1, FieldMemOperand(r3, i));
6255 __ str(r1, FieldMemOperand(r2, i));
6256 }
6257 }
6258
6259 // Return and remove the on-stack parameters.
6260 __ add(sp, sp, Operand(3 * kPointerSize));
6261 __ Ret();
6262
6263 __ bind(&slow_case);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00006264 __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00006265}
6266
6267
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006268// Takes a Smi and converts to an IEEE 64 bit floating point value in two
6269// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
6270// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
6271// scratch register. Destroys the source register. No GC occurs during this
6272// stub so you don't have to set up the frame.
6273class ConvertToDoubleStub : public CodeStub {
6274 public:
6275 ConvertToDoubleStub(Register result_reg_1,
6276 Register result_reg_2,
6277 Register source_reg,
6278 Register scratch_reg)
6279 : result1_(result_reg_1),
6280 result2_(result_reg_2),
6281 source_(source_reg),
6282 zeros_(scratch_reg) { }
6283
6284 private:
6285 Register result1_;
6286 Register result2_;
6287 Register source_;
6288 Register zeros_;
6289
6290 // Minor key encoding in 16 bits.
6291 class ModeBits: public BitField<OverwriteMode, 0, 2> {};
6292 class OpBits: public BitField<Token::Value, 2, 14> {};
6293
6294 Major MajorKey() { return ConvertToDouble; }
6295 int MinorKey() {
6296 // Encode the parameters in a unique 16 bit value.
6297 return result1_.code() +
6298 (result2_.code() << 4) +
6299 (source_.code() << 8) +
6300 (zeros_.code() << 12);
6301 }
6302
6303 void Generate(MacroAssembler* masm);
6304
6305 const char* GetName() { return "ConvertToDoubleStub"; }
6306
6307#ifdef DEBUG
6308 void Print() { PrintF("ConvertToDoubleStub\n"); }
6309#endif
6310};
6311
6312
6313void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
6314#ifndef BIG_ENDIAN_FLOATING_POINT
6315 Register exponent = result1_;
6316 Register mantissa = result2_;
6317#else
6318 Register exponent = result2_;
6319 Register mantissa = result1_;
6320#endif
6321 Label not_special;
6322 // Convert from Smi to integer.
6323 __ mov(source_, Operand(source_, ASR, kSmiTagSize));
6324 // Move sign bit from source to destination. This works because the sign bit
6325 // in the exponent word of the double has the same position and polarity as
6326 // the 2's complement sign bit in a Smi.
6327 ASSERT(HeapNumber::kSignMask == 0x80000000u);
6328 __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
6329 // Subtract from 0 if source was negative.
6330 __ rsb(source_, source_, Operand(0), LeaveCC, ne);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006331
6332 // We have -1, 0 or 1, which we treat specially. Register source_ contains
6333 // absolute value: it is either equal to 1 (special case of -1 and 1),
6334 // greater than 1 (not a special case) or less than 1 (special case of 0).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006335 __ cmp(source_, Operand(1));
6336 __ b(gt, &not_special);
6337
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006338 // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
6339 static const uint32_t exponent_word_for_1 =
6340 HeapNumber::kExponentBias << HeapNumber::kExponentShift;
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006341 __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006342 // 1, 0 and -1 all have 0 for the second word.
6343 __ mov(mantissa, Operand(0));
6344 __ Ret();
6345
6346 __ bind(&not_special);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006347 // Count leading zeros. Uses mantissa for a scratch register on pre-ARM5.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006348 // Gets the wrong answer for 0, but we already checked for that case above.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006349 __ CountLeadingZeros(source_, mantissa, zeros_);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006350 // Compute exponent and or it into the exponent register.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006351 // We use mantissa as a scratch register here.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006352 __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias));
6353 __ orr(exponent,
6354 exponent,
6355 Operand(mantissa, LSL, HeapNumber::kExponentShift));
6356 // Shift up the source chopping the top bit off.
6357 __ add(zeros_, zeros_, Operand(1));
6358 // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
6359 __ mov(source_, Operand(source_, LSL, zeros_));
6360 // Compute lower part of fraction (last 12 bits).
6361 __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
6362 // And the top (top 20 bits).
6363 __ orr(exponent,
6364 exponent,
6365 Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
6366 __ Ret();
6367}
6368
6369
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006370// See comment for class.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006371void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006372 Label max_negative_int;
6373 // the_int_ has the answer which is a signed int32 but not a Smi.
6374 // We test for the special value that has a different exponent. This test
6375 // has the neat side effect of setting the flags according to the sign.
6376 ASSERT(HeapNumber::kSignMask == 0x80000000u);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00006377 __ cmp(the_int_, Operand(0x80000000u));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006378 __ b(eq, &max_negative_int);
6379 // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
6380 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
6381 uint32_t non_smi_exponent =
6382 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
6383 __ mov(scratch_, Operand(non_smi_exponent));
6384 // Set the sign bit in scratch_ if the value was negative.
6385 __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
6386 // Subtract from 0 if the value was negative.
6387 __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs);
6388 // We should be masking the implict first digit of the mantissa away here,
6389 // but it just ends up combining harmlessly with the last digit of the
6390 // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
6391 // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
6392 ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
6393 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
6394 __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
6395 __ str(scratch_, FieldMemOperand(the_heap_number_,
6396 HeapNumber::kExponentOffset));
6397 __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
6398 __ str(scratch_, FieldMemOperand(the_heap_number_,
6399 HeapNumber::kMantissaOffset));
6400 __ Ret();
6401
6402 __ bind(&max_negative_int);
6403 // The max negative int32 is stored as a positive number in the mantissa of
6404 // a double because it uses a sign bit instead of using two's complement.
6405 // The actual mantissa bits stored are all 0 because the implicit most
6406 // significant 1 bit is not stored.
6407 non_smi_exponent += 1 << HeapNumber::kExponentShift;
6408 __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
6409 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
6410 __ mov(ip, Operand(0));
6411 __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
6412 __ Ret();
6413}
6414
6415
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006416// Handle the case where the lhs and rhs are the same object.
6417// Equality is almost reflexive (everything but NaN), so this is a test
6418// for "identity and not NaN".
6419static void EmitIdenticalObjectComparison(MacroAssembler* masm,
6420 Label* slow,
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006421 Condition cc,
6422 bool never_nan_nan) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006423 Label not_identical;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006424 Label heap_number, return_equal;
6425 Register exp_mask_reg = r5;
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006426 __ cmp(r0, r1);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006427 __ b(ne, &not_identical);
6428
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006429 // The two objects are identical. If we know that one of them isn't NaN then
6430 // we now know they test equal.
6431 if (cc != eq || !never_nan_nan) {
6432 __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006433
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006434 // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
6435 // so we do the second best thing - test it ourselves.
6436 // They are both equal and they are not both Smis so both of them are not
6437 // Smis. If it's not a heap number, then return equal.
6438 if (cc == lt || cc == gt) {
6439 __ CompareObjectType(r0, r4, r4, FIRST_JS_OBJECT_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006440 __ b(ge, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006441 } else {
6442 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6443 __ b(eq, &heap_number);
6444 // Comparing JS objects with <=, >= is complicated.
6445 if (cc != eq) {
6446 __ cmp(r4, Operand(FIRST_JS_OBJECT_TYPE));
6447 __ b(ge, slow);
6448 // Normally here we fall through to return_equal, but undefined is
6449 // special: (undefined == undefined) == true, but
6450 // (undefined <= undefined) == false! See ECMAScript 11.8.5.
6451 if (cc == le || cc == ge) {
6452 __ cmp(r4, Operand(ODDBALL_TYPE));
6453 __ b(ne, &return_equal);
6454 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00006455 __ cmp(r0, r2);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006456 __ b(ne, &return_equal);
6457 if (cc == le) {
6458 // undefined <= undefined should fail.
6459 __ mov(r0, Operand(GREATER));
6460 } else {
6461 // undefined >= undefined should fail.
6462 __ mov(r0, Operand(LESS));
6463 }
6464 __ mov(pc, Operand(lr)); // Return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006465 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006466 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006467 }
6468 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006469
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006470 __ bind(&return_equal);
6471 if (cc == lt) {
6472 __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
6473 } else if (cc == gt) {
6474 __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
6475 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006476 __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006477 }
6478 __ mov(pc, Operand(lr)); // Return.
6479
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006480 if (cc != eq || !never_nan_nan) {
6481 // For less and greater we don't have to check for NaN since the result of
6482 // x < x is false regardless. For the others here is some code to check
6483 // for NaN.
6484 if (cc != lt && cc != gt) {
6485 __ bind(&heap_number);
6486 // It is a heap number, so return non-equal if it's NaN and equal if it's
6487 // not NaN.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006488
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006489 // The representation of NaN values has all exponent bits (52..62) set,
6490 // and not all mantissa bits (0..51) clear.
6491 // Read top bits of double representation (second word of value).
6492 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
6493 // Test that exponent bits are all set.
6494 __ and_(r3, r2, Operand(exp_mask_reg));
6495 __ cmp(r3, Operand(exp_mask_reg));
6496 __ b(ne, &return_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006497
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006498 // Shift out flag and all exponent bits, retaining only mantissa.
6499 __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
6500 // Or with all low-bits of mantissa.
6501 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
6502 __ orr(r0, r3, Operand(r2), SetCC);
6503 // For equal we already have the right value in r0: Return zero (equal)
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006504 // if all bits in mantissa are zero (it's an Infinity) and non-zero if
6505 // not (it's a NaN). For <= and >= we need to load r0 with the failing
6506 // value if it's a NaN.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006507 if (cc != eq) {
6508 // All-zero means Infinity means equal.
6509 __ mov(pc, Operand(lr), LeaveCC, eq); // Return equal
6510 if (cc == le) {
6511 __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
6512 } else {
6513 __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
6514 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006515 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006516 __ mov(pc, Operand(lr)); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006517 }
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006518 // No fall through here.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006519 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006520
6521 __ bind(&not_identical);
6522}
6523
6524
6525// See comment at call site.
6526static void EmitSmiNonsmiComparison(MacroAssembler* masm,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006527 Label* lhs_not_nan,
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006528 Label* slow,
6529 bool strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006530 Label rhs_is_smi;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006531 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006532 __ b(eq, &rhs_is_smi);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006533
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006534 // Lhs is a Smi. Check whether the rhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006535 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
6536 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006537 // If rhs is not a number and lhs is a Smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006538 // succeed. Return non-equal (r0 is already not zero)
6539 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6540 } else {
6541 // Smi compared non-strictly with a non-Smi non-heap-number. Call
6542 // the runtime.
6543 __ b(ne, slow);
6544 }
6545
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006546 // Lhs (r1) is a smi, rhs (r0) is a number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006547 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006548 // Convert lhs to a double in d7 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006549 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006550 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
6551 __ vmov(s15, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006552 __ vcvt_f64_s32(d7, s15);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006553 // Load the double from rhs, tagged HeapNumber r0, to d6.
6554 __ sub(r7, r0, Operand(kHeapObjectTag));
6555 __ vldr(d6, r7, HeapNumber::kValueOffset);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006556 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006557 __ push(lr);
6558 // Convert lhs to a double in r2, r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006559 __ mov(r7, Operand(r1));
6560 ConvertToDoubleStub stub1(r3, r2, r7, r6);
6561 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006562 // Load rhs to a double in r0, r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006563 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006564 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006565 }
6566
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006567 // We now have both loaded as doubles but we can skip the lhs nan check
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006568 // since it's a smi.
6569 __ jmp(lhs_not_nan);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006570
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006571 __ bind(&rhs_is_smi);
6572 // Rhs is a smi. Check whether the non-smi lhs is a heap number.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006573 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
6574 if (strict) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006575 // If lhs is not a number and rhs is a smi then strict equality cannot
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006576 // succeed. Return non-equal.
6577 __ mov(r0, Operand(1), LeaveCC, ne); // Non-zero indicates not equal.
6578 __ mov(pc, Operand(lr), LeaveCC, ne); // Return.
6579 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006580 // Smi compared non-strictly with a non-smi non-heap-number. Call
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006581 // the runtime.
6582 __ b(ne, slow);
6583 }
6584
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006585 // Rhs (r0) is a smi, lhs (r1) is a heap number.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006586 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006587 // Convert rhs to a double in d6 .
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006588 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006589 // Load the double from lhs, tagged HeapNumber r1, to d7.
6590 __ sub(r7, r1, Operand(kHeapObjectTag));
6591 __ vldr(d7, r7, HeapNumber::kValueOffset);
6592 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
6593 __ vmov(s13, r7);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00006594 __ vcvt_f64_s32(d6, s13);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006595 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006596 __ push(lr);
6597 // Load lhs to a double in r2, r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006598 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006599 // Convert rhs to a double in r0, r1.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006600 __ mov(r7, Operand(r0));
6601 ConvertToDoubleStub stub2(r1, r0, r7, r6);
6602 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006603 __ pop(lr);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006604 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006605 // Fall through to both_loaded_as_doubles.
6606}
6607
6608
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006609void EmitNanCheck(MacroAssembler* masm, Label* lhs_not_nan, Condition cc) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006610 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006611 Register rhs_exponent = exp_first ? r0 : r1;
6612 Register lhs_exponent = exp_first ? r2 : r3;
6613 Register rhs_mantissa = exp_first ? r1 : r0;
6614 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006615 Label one_is_nan, neither_is_nan;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006616 Label lhs_not_nan_exp_mask_is_loaded;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006617
6618 Register exp_mask_reg = r5;
6619
6620 __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006621 __ and_(r4, lhs_exponent, Operand(exp_mask_reg));
6622 __ cmp(r4, Operand(exp_mask_reg));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006623 __ b(ne, &lhs_not_nan_exp_mask_is_loaded);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006624 __ mov(r4,
6625 Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
6626 SetCC);
6627 __ b(ne, &one_is_nan);
6628 __ cmp(lhs_mantissa, Operand(0));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006629 __ b(ne, &one_is_nan);
6630
6631 __ bind(lhs_not_nan);
6632 __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
6633 __ bind(&lhs_not_nan_exp_mask_is_loaded);
6634 __ and_(r4, rhs_exponent, Operand(exp_mask_reg));
6635 __ cmp(r4, Operand(exp_mask_reg));
6636 __ b(ne, &neither_is_nan);
6637 __ mov(r4,
6638 Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
6639 SetCC);
6640 __ b(ne, &one_is_nan);
6641 __ cmp(rhs_mantissa, Operand(0));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006642 __ b(eq, &neither_is_nan);
6643
6644 __ bind(&one_is_nan);
6645 // NaN comparisons always fail.
6646 // Load whatever we need in r0 to make the comparison fail.
6647 if (cc == lt || cc == le) {
6648 __ mov(r0, Operand(GREATER));
6649 } else {
6650 __ mov(r0, Operand(LESS));
6651 }
6652 __ mov(pc, Operand(lr)); // Return.
6653
6654 __ bind(&neither_is_nan);
6655}
6656
6657
6658// See comment at call site.
6659static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
6660 bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006661 Register rhs_exponent = exp_first ? r0 : r1;
6662 Register lhs_exponent = exp_first ? r2 : r3;
6663 Register rhs_mantissa = exp_first ? r1 : r0;
6664 Register lhs_mantissa = exp_first ? r3 : r2;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006665
6666 // r0, r1, r2, r3 have the two doubles. Neither is a NaN.
6667 if (cc == eq) {
6668 // Doubles are not equal unless they have the same bit pattern.
6669 // Exception: 0 and -0.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006670 __ cmp(rhs_mantissa, Operand(lhs_mantissa));
6671 __ orr(r0, rhs_mantissa, Operand(lhs_mantissa), LeaveCC, ne);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006672 // Return non-zero if the numbers are unequal.
6673 __ mov(pc, Operand(lr), LeaveCC, ne);
6674
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006675 __ sub(r0, rhs_exponent, Operand(lhs_exponent), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006676 // If exponents are equal then return 0.
6677 __ mov(pc, Operand(lr), LeaveCC, eq);
6678
6679 // Exponents are unequal. The only way we can return that the numbers
6680 // are equal is if one is -0 and the other is 0. We already dealt
6681 // with the case where both are -0 or both are 0.
6682 // We start by seeing if the mantissas (that are equal) or the bottom
6683 // 31 bits of the rhs exponent are non-zero. If so we return not
6684 // equal.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006685 __ orr(r4, lhs_mantissa, Operand(lhs_exponent, LSL, kSmiTagSize), SetCC);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006686 __ mov(r0, Operand(r4), LeaveCC, ne);
6687 __ mov(pc, Operand(lr), LeaveCC, ne); // Return conditionally.
6688 // Now they are equal if and only if the lhs exponent is zero in its
6689 // low 31 bits.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006690 __ mov(r0, Operand(rhs_exponent, LSL, kSmiTagSize));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006691 __ mov(pc, Operand(lr));
6692 } else {
6693 // Call a native function to do a comparison between two non-NaNs.
6694 // Call C routine that may not cause GC or other trouble.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00006695 __ push(lr);
6696 __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
6697 __ CallCFunction(ExternalReference::compare_doubles(), 4);
6698 __ pop(pc); // Return.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006699 }
6700}
6701
6702
6703// See comment at call site.
6704static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm) {
6705 // If either operand is a JSObject or an oddball value, then they are
6706 // not equal since their pointers are different.
6707 // There is no test for undetectability in strict equality.
6708 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
6709 Label first_non_object;
6710 // Get the type of the first operand into r2 and compare it with
6711 // FIRST_JS_OBJECT_TYPE.
6712 __ CompareObjectType(r0, r2, r2, FIRST_JS_OBJECT_TYPE);
6713 __ b(lt, &first_non_object);
6714
6715 // Return non-zero (r0 is not zero)
6716 Label return_not_equal;
6717 __ bind(&return_not_equal);
6718 __ mov(pc, Operand(lr)); // Return.
6719
6720 __ bind(&first_non_object);
6721 // Check for oddballs: true, false, null, undefined.
6722 __ cmp(r2, Operand(ODDBALL_TYPE));
6723 __ b(eq, &return_not_equal);
6724
6725 __ CompareObjectType(r1, r3, r3, FIRST_JS_OBJECT_TYPE);
6726 __ b(ge, &return_not_equal);
6727
6728 // Check for oddballs: true, false, null, undefined.
6729 __ cmp(r3, Operand(ODDBALL_TYPE));
6730 __ b(eq, &return_not_equal);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006731
6732 // Now that we have the types we might as well check for symbol-symbol.
6733 // Ensure that no non-strings have the symbol bit set.
6734 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
6735 ASSERT(kSymbolTag != 0);
6736 __ and_(r2, r2, Operand(r3));
6737 __ tst(r2, Operand(kIsSymbolMask));
6738 __ b(ne, &return_not_equal);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006739}
6740
6741
6742// See comment at call site.
6743static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
6744 Label* both_loaded_as_doubles,
6745 Label* not_heap_numbers,
6746 Label* slow) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006747 __ CompareObjectType(r0, r3, r2, HEAP_NUMBER_TYPE);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006748 __ b(ne, not_heap_numbers);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006749 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
6750 __ cmp(r2, r3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006751 __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
6752
6753 // Both are heap numbers. Load them up then jump to the code we have
6754 // for that.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006755 if (CpuFeatures::IsSupported(VFP3)) {
6756 CpuFeatures::Scope scope(VFP3);
6757 __ sub(r7, r0, Operand(kHeapObjectTag));
6758 __ vldr(d6, r7, HeapNumber::kValueOffset);
6759 __ sub(r7, r1, Operand(kHeapObjectTag));
6760 __ vldr(d7, r7, HeapNumber::kValueOffset);
6761 } else {
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00006762 __ Ldrd(r2, r3, FieldMemOperand(r1, HeapNumber::kValueOffset));
6763 __ Ldrd(r0, r1, FieldMemOperand(r0, HeapNumber::kValueOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006764 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006765 __ jmp(both_loaded_as_doubles);
6766}
6767
6768
6769// Fast negative check for symbol-to-symbol equality.
6770static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) {
6771 // r2 is object type of r0.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006772 // Ensure that no non-strings have the symbol bit set.
6773 ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
6774 ASSERT(kSymbolTag != 0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006775 __ tst(r2, Operand(kIsSymbolMask));
6776 __ b(eq, slow);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006777 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
6778 __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006779 __ tst(r3, Operand(kIsSymbolMask));
6780 __ b(eq, slow);
6781
6782 // Both are symbols. We already checked they weren't the same pointer
6783 // so they are not equal.
6784 __ mov(r0, Operand(1)); // Non-zero indicates not equal.
6785 __ mov(pc, Operand(lr)); // Return.
6786}
6787
6788
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006789void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
6790 Register object,
6791 Register result,
6792 Register scratch1,
6793 Register scratch2,
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006794 Register scratch3,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006795 bool object_is_smi,
6796 Label* not_found) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006797 // Use of registers. Register result is used as a temporary.
6798 Register number_string_cache = result;
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006799 Register mask = scratch3;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006800
6801 // Load the number string cache.
6802 __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
6803
6804 // Make the hash mask from the length of the number string cache. It
6805 // contains two elements (number and string) for each cache entry.
6806 __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00006807 // Divide length by two (length is a smi).
6808 __ mov(mask, Operand(mask, ASR, kSmiTagSize + 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006809 __ sub(mask, mask, Operand(1)); // Make mask.
6810
6811 // Calculate the entry in the number string cache. The hash value in the
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006812 // number string cache for smis is just the smi value, and the hash for
6813 // doubles is the xor of the upper and lower words. See
6814 // Heap::GetNumberStringCache.
6815 Label is_smi;
6816 Label load_result_from_cache;
6817 if (!object_is_smi) {
6818 __ BranchOnSmi(object, &is_smi);
6819 if (CpuFeatures::IsSupported(VFP3)) {
6820 CpuFeatures::Scope scope(VFP3);
6821 __ CheckMap(object,
6822 scratch1,
6823 Factory::heap_number_map(),
6824 not_found,
6825 true);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006826
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006827 ASSERT_EQ(8, kDoubleSize);
6828 __ add(scratch1,
6829 object,
6830 Operand(HeapNumber::kValueOffset - kHeapObjectTag));
6831 __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
6832 __ eor(scratch1, scratch1, Operand(scratch2));
6833 __ and_(scratch1, scratch1, Operand(mask));
6834
6835 // Calculate address of entry in string cache: each entry consists
6836 // of two pointer sized fields.
6837 __ add(scratch1,
6838 number_string_cache,
6839 Operand(scratch1, LSL, kPointerSizeLog2 + 1));
6840
6841 Register probe = mask;
6842 __ ldr(probe,
6843 FieldMemOperand(scratch1, FixedArray::kHeaderSize));
6844 __ BranchOnSmi(probe, not_found);
6845 __ sub(scratch2, object, Operand(kHeapObjectTag));
6846 __ vldr(d0, scratch2, HeapNumber::kValueOffset);
6847 __ sub(probe, probe, Operand(kHeapObjectTag));
6848 __ vldr(d1, probe, HeapNumber::kValueOffset);
6849 __ vcmp(d0, d1);
6850 __ vmrs(pc);
6851 __ b(ne, not_found); // The cache did not contain this value.
6852 __ b(&load_result_from_cache);
6853 } else {
6854 __ b(not_found);
6855 }
6856 }
6857
6858 __ bind(&is_smi);
6859 Register scratch = scratch1;
6860 __ and_(scratch, mask, Operand(object, ASR, 1));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006861 // Calculate address of entry in string cache: each entry consists
6862 // of two pointer sized fields.
6863 __ add(scratch,
6864 number_string_cache,
6865 Operand(scratch, LSL, kPointerSizeLog2 + 1));
6866
6867 // Check if the entry is the smi we are looking for.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006868 Register probe = mask;
6869 __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
6870 __ cmp(object, probe);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006871 __ b(ne, not_found);
6872
6873 // Get the result from the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006874 __ bind(&load_result_from_cache);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006875 __ ldr(result,
6876 FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006877 __ IncrementCounter(&Counters::number_to_string_native,
6878 1,
6879 scratch1,
6880 scratch2);
6881}
6882
6883
6884void NumberToStringStub::Generate(MacroAssembler* masm) {
6885 Label runtime;
6886
6887 __ ldr(r1, MemOperand(sp, 0));
6888
6889 // Generate code to lookup number in the number string cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006890 GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006891 __ add(sp, sp, Operand(1 * kPointerSize));
6892 __ Ret();
6893
6894 __ bind(&runtime);
6895 // Handle number to string in the runtime system if not found in the cache.
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00006896 __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00006897}
6898
6899
ager@chromium.orgac091b72010-05-05 07:34:42 +00006900void RecordWriteStub::Generate(MacroAssembler* masm) {
6901 __ RecordWriteHelper(object_, offset_, scratch_);
6902 __ Ret();
6903}
6904
6905
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006906// On entry r0 (rhs) and r1 (lhs) are the values to be compared.
6907// On exit r0 is 0, positive or negative to indicate the result of
6908// the comparison.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006909void CompareStub::Generate(MacroAssembler* masm) {
6910 Label slow; // Call builtin.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006911 Label not_smis, both_loaded_as_doubles, lhs_not_nan;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006912
6913 // NOTICE! This code is only reached after a smi-fast-case check, so
6914 // it is certain that at least one operand isn't a smi.
6915
6916 // Handle the case where the objects are identical. Either returns the answer
6917 // or goes to slow. Only falls through if the objects were not identical.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006918 EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006919
6920 // If either is a Smi (we know that not both are), then they can only
6921 // be strictly equal if the other is a HeapNumber.
6922 ASSERT_EQ(0, kSmiTag);
6923 ASSERT_EQ(0, Smi::FromInt(0));
6924 __ and_(r2, r0, Operand(r1));
6925 __ tst(r2, Operand(kSmiTagMask));
6926 __ b(ne, &not_smis);
6927 // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
6928 // 1) Return the answer.
6929 // 2) Go to slow.
6930 // 3) Fall through to both_loaded_as_doubles.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006931 // 4) Jump to lhs_not_nan.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006932 // In cases 3 and 4 we have found out we were dealing with a number-number
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006933 // comparison. If VFP3 is supported the double values of the numbers have
6934 // been loaded into d7 and d6. Otherwise, the double values have been loaded
6935 // into r0, r1, r2, and r3.
6936 EmitSmiNonsmiComparison(masm, &lhs_not_nan, &slow, strict_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006937
6938 __ bind(&both_loaded_as_doubles);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006939 // The arguments have been converted to doubles and stored in d6 and d7, if
6940 // VFP3 is supported, or in r0, r1, r2, and r3.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006941 if (CpuFeatures::IsSupported(VFP3)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006942 __ bind(&lhs_not_nan);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006943 CpuFeatures::Scope scope(VFP3);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006944 Label no_nan;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006945 // ARMv7 VFP3 instructions to implement double precision comparison.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006946 __ vcmp(d7, d6);
6947 __ vmrs(pc); // Move vector status bits to normal status bits.
6948 Label nan;
6949 __ b(vs, &nan);
6950 __ mov(r0, Operand(EQUAL), LeaveCC, eq);
6951 __ mov(r0, Operand(LESS), LeaveCC, lt);
6952 __ mov(r0, Operand(GREATER), LeaveCC, gt);
6953 __ mov(pc, Operand(lr));
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006954
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006955 __ bind(&nan);
6956 // If one of the sides was a NaN then the v flag is set. Load r0 with
6957 // whatever it takes to make the comparison fail, since comparisons with NaN
6958 // always fail.
6959 if (cc_ == lt || cc_ == le) {
6960 __ mov(r0, Operand(GREATER));
6961 } else {
6962 __ mov(r0, Operand(LESS));
6963 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006964 __ mov(pc, Operand(lr));
6965 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006966 // Checks for NaN in the doubles we have loaded. Can return the answer or
6967 // fall through if neither is a NaN. Also binds lhs_not_nan.
6968 EmitNanCheck(masm, &lhs_not_nan, cc_);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006969 // Compares two doubles in r0, r1, r2, r3 that are not NaNs. Returns the
6970 // answer. Never falls through.
6971 EmitTwoNonNanDoubleComparison(masm, cc_);
6972 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006973
6974 __ bind(&not_smis);
6975 // At this point we know we are dealing with two different objects,
6976 // and neither of them is a Smi. The objects are in r0 and r1.
6977 if (strict_) {
6978 // This returns non-equal for some object types, or falls through if it
6979 // was not lucky.
6980 EmitStrictTwoHeapObjectCompare(masm);
6981 }
6982
6983 Label check_for_symbols;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006984 Label flat_string_check;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006985 // Check for heap-number-heap-number comparison. Can jump to slow case,
6986 // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
6987 // that case. If the inputs are not doubles then jumps to check_for_symbols.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006988 // In this case r2 will contain the type of r0. Never falls through.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006989 EmitCheckForTwoHeapNumbers(masm,
6990 &both_loaded_as_doubles,
6991 &check_for_symbols,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00006992 &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006993
6994 __ bind(&check_for_symbols);
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00006995 // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
6996 // symbols.
6997 if (cc_ == eq && !strict_) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00006998 // Either jumps to slow or returns the answer. Assumes that r2 is the type
6999 // of r0 on entry.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007000 EmitCheckForSymbols(masm, &flat_string_check);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007001 }
7002
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007003 // Check for both being sequential ASCII strings, and inline if that is the
7004 // case.
7005 __ bind(&flat_string_check);
7006
7007 __ JumpIfNonSmisNotBothSequentialAsciiStrings(r0, r1, r2, r3, &slow);
7008
7009 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
7010 StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
7011 r1,
7012 r0,
7013 r2,
7014 r3,
7015 r4,
7016 r5);
7017 // Never falls through to here.
7018
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007019 __ bind(&slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007020
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007021 __ Push(r1, r0);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007022 // Figure out which native to call and setup the arguments.
7023 Builtins::JavaScript native;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007024 if (cc_ == eq) {
7025 native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
7026 } else {
7027 native = Builtins::COMPARE;
7028 int ncr; // NaN compare result
7029 if (cc_ == lt || cc_ == le) {
7030 ncr = GREATER;
7031 } else {
7032 ASSERT(cc_ == gt || cc_ == ge); // remaining cases
7033 ncr = LESS;
7034 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007035 __ mov(r0, Operand(Smi::FromInt(ncr)));
7036 __ push(r0);
7037 }
7038
7039 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
7040 // tagged as a small integer.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007041 __ InvokeBuiltin(native, JUMP_JS);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007042}
7043
7044
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00007045// We fall into this code if the operands were Smis, but the result was
7046// not (eg. overflow). We branch into this code (to the not_smi label) if
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007047// the operands were not both Smi. The operands are in r0 and r1. In order
7048// to call the C-implemented binary fp operation routines we need to end up
7049// with the double precision floating point operands in r0 and r1 (for the
7050// value in r1) and r2 and r3 (for the value in r0).
ager@chromium.org357bf652010-04-12 11:30:10 +00007051void GenericBinaryOpStub::HandleBinaryOpSlowCases(
7052 MacroAssembler* masm,
7053 Label* not_smi,
7054 Register lhs,
7055 Register rhs,
7056 const Builtins::JavaScript& builtin) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007057 Label slow, slow_reverse, do_the_call;
ager@chromium.org357bf652010-04-12 11:30:10 +00007058 bool use_fp_registers = CpuFeatures::IsSupported(VFP3) && Token::MOD != op_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007059
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00007060 ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
ager@chromium.org357bf652010-04-12 11:30:10 +00007061
7062 if (ShouldGenerateSmiCode()) {
7063 // Smi-smi case (overflow).
7064 // Since both are Smis there is no heap number to overwrite, so allocate.
7065 // The new heap number is in r5. r6 and r7 are scratch.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007066 __ AllocateHeapNumber(r5, r6, r7, lhs.is(r0) ? &slow_reverse : &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007067
7068 // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
7069 // using registers d7 and d6 for the double values.
7070 if (use_fp_registers) {
7071 CpuFeatures::Scope scope(VFP3);
7072 __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
7073 __ vmov(s15, r7);
7074 __ vcvt_f64_s32(d7, s15);
7075 __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
7076 __ vmov(s13, r7);
7077 __ vcvt_f64_s32(d6, s13);
7078 } else {
7079 // Write Smi from rhs to r3 and r2 in double format. r6 is scratch.
7080 __ mov(r7, Operand(rhs));
7081 ConvertToDoubleStub stub1(r3, r2, r7, r6);
7082 __ push(lr);
7083 __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
7084 // Write Smi from lhs to r1 and r0 in double format. r6 is scratch.
7085 __ mov(r7, Operand(lhs));
7086 ConvertToDoubleStub stub2(r1, r0, r7, r6);
7087 __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
7088 __ pop(lr);
7089 }
7090 __ jmp(&do_the_call); // Tail call. No return.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007091 }
7092
ager@chromium.org357bf652010-04-12 11:30:10 +00007093 // We branch here if at least one of r0 and r1 is not a Smi.
7094 __ bind(not_smi);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007095
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007096 // After this point we have the left hand side in r1 and the right hand side
7097 // in r0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007098 if (lhs.is(r0)) {
7099 __ Swap(r0, r1, ip);
7100 }
7101
7102 if (ShouldGenerateFPCode()) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007103 Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
7104
ager@chromium.org357bf652010-04-12 11:30:10 +00007105 if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
7106 switch (op_) {
7107 case Token::ADD:
7108 case Token::SUB:
7109 case Token::MUL:
7110 case Token::DIV:
7111 GenerateTypeTransition(masm);
7112 break;
7113
7114 default:
7115 break;
7116 }
7117 }
7118
7119 if (mode_ == NO_OVERWRITE) {
7120 // In the case where there is no chance of an overwritable float we may as
7121 // well do the allocation immediately while r0 and r1 are untouched.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007122 __ AllocateHeapNumber(r5, r6, r7, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007123 }
7124
7125 // Move r0 to a double in r2-r3.
7126 __ tst(r0, Operand(kSmiTagMask));
7127 __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
7128 __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
7129 __ b(ne, &slow);
7130 if (mode_ == OVERWRITE_RIGHT) {
7131 __ mov(r5, Operand(r0)); // Overwrite this heap number.
7132 }
7133 if (use_fp_registers) {
7134 CpuFeatures::Scope scope(VFP3);
7135 // Load the double from tagged HeapNumber r0 to d7.
7136 __ sub(r7, r0, Operand(kHeapObjectTag));
7137 __ vldr(d7, r7, HeapNumber::kValueOffset);
7138 } else {
7139 // Calling convention says that second double is in r2 and r3.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007140 __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007141 }
7142 __ jmp(&finished_loading_r0);
7143 __ bind(&r0_is_smi);
7144 if (mode_ == OVERWRITE_RIGHT) {
7145 // We can't overwrite a Smi so get address of new heap number into r5.
7146 __ AllocateHeapNumber(r5, r6, r7, &slow);
7147 }
7148
7149 if (use_fp_registers) {
7150 CpuFeatures::Scope scope(VFP3);
7151 // Convert smi in r0 to double in d7.
7152 __ mov(r7, Operand(r0, ASR, kSmiTagSize));
7153 __ vmov(s15, r7);
7154 __ vcvt_f64_s32(d7, s15);
7155 } else {
7156 // Write Smi from r0 to r3 and r2 in double format.
7157 __ mov(r7, Operand(r0));
7158 ConvertToDoubleStub stub3(r3, r2, r7, r6);
7159 __ push(lr);
7160 __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
7161 __ pop(lr);
7162 }
7163
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007164 // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
7165 // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
7166 Label r1_is_not_smi;
7167 if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
7168 __ tst(r1, Operand(kSmiTagMask));
7169 __ b(ne, &r1_is_not_smi);
7170 GenerateTypeTransition(masm);
7171 __ jmp(&r1_is_smi);
7172 }
7173
ager@chromium.org357bf652010-04-12 11:30:10 +00007174 __ bind(&finished_loading_r0);
7175
7176 // Move r1 to a double in r0-r1.
7177 __ tst(r1, Operand(kSmiTagMask));
7178 __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007179 __ bind(&r1_is_not_smi);
ager@chromium.org357bf652010-04-12 11:30:10 +00007180 __ CompareObjectType(r1, r4, r4, HEAP_NUMBER_TYPE);
7181 __ b(ne, &slow);
7182 if (mode_ == OVERWRITE_LEFT) {
7183 __ mov(r5, Operand(r1)); // Overwrite this heap number.
7184 }
7185 if (use_fp_registers) {
7186 CpuFeatures::Scope scope(VFP3);
7187 // Load the double from tagged HeapNumber r1 to d6.
7188 __ sub(r7, r1, Operand(kHeapObjectTag));
7189 __ vldr(d6, r7, HeapNumber::kValueOffset);
7190 } else {
7191 // Calling convention says that first double is in r0 and r1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007192 __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007193 }
7194 __ jmp(&finished_loading_r1);
7195 __ bind(&r1_is_smi);
7196 if (mode_ == OVERWRITE_LEFT) {
7197 // We can't overwrite a Smi so get address of new heap number into r5.
7198 __ AllocateHeapNumber(r5, r6, r7, &slow);
7199 }
7200
7201 if (use_fp_registers) {
7202 CpuFeatures::Scope scope(VFP3);
7203 // Convert smi in r1 to double in d6.
7204 __ mov(r7, Operand(r1, ASR, kSmiTagSize));
7205 __ vmov(s13, r7);
7206 __ vcvt_f64_s32(d6, s13);
7207 } else {
7208 // Write Smi from r1 to r1 and r0 in double format.
7209 __ mov(r7, Operand(r1));
7210 ConvertToDoubleStub stub4(r1, r0, r7, r6);
7211 __ push(lr);
7212 __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
7213 __ pop(lr);
7214 }
7215
7216 __ bind(&finished_loading_r1);
7217
7218 __ bind(&do_the_call);
7219 // If we are inlining the operation using VFP3 instructions for
7220 // add, subtract, multiply, or divide, the arguments are in d6 and d7.
7221 if (use_fp_registers) {
7222 CpuFeatures::Scope scope(VFP3);
7223 // ARMv7 VFP3 instructions to implement
7224 // double precision, add, subtract, multiply, divide.
7225
7226 if (Token::MUL == op_) {
7227 __ vmul(d5, d6, d7);
7228 } else if (Token::DIV == op_) {
7229 __ vdiv(d5, d6, d7);
7230 } else if (Token::ADD == op_) {
7231 __ vadd(d5, d6, d7);
7232 } else if (Token::SUB == op_) {
7233 __ vsub(d5, d6, d7);
7234 } else {
7235 UNREACHABLE();
7236 }
7237 __ sub(r0, r5, Operand(kHeapObjectTag));
7238 __ vstr(d5, r0, HeapNumber::kValueOffset);
7239 __ add(r0, r0, Operand(kHeapObjectTag));
7240 __ mov(pc, lr);
7241 } else {
7242 // If we did not inline the operation, then the arguments are in:
7243 // r0: Left value (least significant part of mantissa).
7244 // r1: Left value (sign, exponent, top of mantissa).
7245 // r2: Right value (least significant part of mantissa).
7246 // r3: Right value (sign, exponent, top of mantissa).
7247 // r5: Address of heap number for result.
7248
7249 __ push(lr); // For later.
7250 __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
7251 // Call C routine that may not cause GC or other trouble. r5 is callee
7252 // save.
7253 __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
7254 // Store answer in the overwritable heap number.
7255 #if !defined(USE_ARM_EABI)
7256 // Double returned in fp coprocessor register 0 and 1, encoded as register
7257 // cr8. Offsets must be divisible by 4 for coprocessor so we need to
7258 // substract the tag from r5.
7259 __ sub(r4, r5, Operand(kHeapObjectTag));
7260 __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
7261 #else
7262 // Double returned in registers 0 and 1.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00007263 __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
ager@chromium.org357bf652010-04-12 11:30:10 +00007264 #endif
7265 __ mov(r0, Operand(r5));
7266 // And we are done.
7267 __ pop(pc);
7268 }
7269 }
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00007270
7271
7272 if (lhs.is(r0)) {
7273 __ b(&slow);
7274 __ bind(&slow_reverse);
7275 __ Swap(r0, r1, ip);
7276 }
7277
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007278 // We jump to here if something goes wrong (one param is not a number of any
7279 // sort or new-space allocation fails).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007280 __ bind(&slow);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007281
7282 // Push arguments to the stack
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007283 __ Push(r1, r0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007284
ager@chromium.org357bf652010-04-12 11:30:10 +00007285 if (Token::ADD == op_) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007286 // Test for string arguments before calling runtime.
7287 // r1 : first argument
7288 // r0 : second argument
7289 // sp[0] : second argument
ager@chromium.org5c838252010-02-19 08:53:10 +00007290 // sp[4] : first argument
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007291
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007292 Label not_strings, not_string1, string1, string1_smi2;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007293 __ tst(r1, Operand(kSmiTagMask));
7294 __ b(eq, &not_string1);
7295 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
7296 __ b(ge, &not_string1);
7297
7298 // First argument is a a string, test second.
7299 __ tst(r0, Operand(kSmiTagMask));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007300 __ b(eq, &string1_smi2);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007301 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7302 __ b(ge, &string1);
7303
7304 // First and second argument are strings.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007305 StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
7306 __ TailCallStub(&string_add_stub);
7307
7308 __ bind(&string1_smi2);
7309 // First argument is a string, second is a smi. Try to lookup the number
7310 // string for the smi in the number string cache.
7311 NumberToStringStub::GenerateLookupNumberStringCache(
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007312 masm, r0, r2, r4, r5, r6, true, &string1);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00007313
7314 // Replace second argument on stack and tailcall string add stub to make
7315 // the result.
7316 __ str(r2, MemOperand(sp, 0));
7317 __ TailCallStub(&string_add_stub);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007318
7319 // Only first argument is a string.
7320 __ bind(&string1);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007321 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
7322
7323 // First argument was not a string, test second.
7324 __ bind(&not_string1);
7325 __ tst(r0, Operand(kSmiTagMask));
7326 __ b(eq, &not_strings);
7327 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
7328 __ b(ge, &not_strings);
7329
7330 // Only second argument is a string.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007331 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
7332
7333 __ bind(&not_strings);
7334 }
7335
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007336 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007337}
7338
7339
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007340// Tries to get a signed int32 out of a double precision floating point heap
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007341// number. Rounds towards 0. Fastest for doubles that are in the ranges
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007342// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds
7343// almost to the range of signed int32 values that are not Smis. Jumps to the
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007344// label 'slow' if the double isn't in the range -0x80000000.0 to 0x80000000.0
7345// (excluding the endpoints).
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007346static void GetInt32(MacroAssembler* masm,
7347 Register source,
7348 Register dest,
7349 Register scratch,
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007350 Register scratch2,
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007351 Label* slow) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007352 Label right_exponent, done;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007353 // Get exponent word.
7354 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
7355 // Get exponent alone in scratch2.
7356 __ and_(scratch2, scratch, Operand(HeapNumber::kExponentMask));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007357 // Load dest with zero. We use this either for the final shift or
7358 // for the answer.
7359 __ mov(dest, Operand(0));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007360 // Check whether the exponent matches a 32 bit signed int that is not a Smi.
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007361 // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
7362 // the exponent that we are fastest at and also the highest exponent we can
7363 // handle here.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007364 const uint32_t non_smi_exponent =
7365 (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
7366 __ cmp(scratch2, Operand(non_smi_exponent));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007367 // If we have a match of the int32-but-not-Smi exponent then skip some logic.
7368 __ b(eq, &right_exponent);
7369 // If the exponent is higher than that then go to slow case. This catches
7370 // numbers that don't fit in a signed int32, infinities and NaNs.
7371 __ b(gt, slow);
7372
7373 // We know the exponent is smaller than 30 (biased). If it is less than
7374 // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
7375 // it rounds to zero.
7376 const uint32_t zero_exponent =
7377 (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
7378 __ sub(scratch2, scratch2, Operand(zero_exponent), SetCC);
7379 // Dest already has a Smi zero.
7380 __ b(lt, &done);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007381 if (!CpuFeatures::IsSupported(VFP3)) {
7382 // We have a shifted exponent between 0 and 30 in scratch2.
7383 __ mov(dest, Operand(scratch2, LSR, HeapNumber::kExponentShift));
7384 // We now have the exponent in dest. Subtract from 30 to get
7385 // how much to shift down.
7386 __ rsb(dest, dest, Operand(30));
7387 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007388 __ bind(&right_exponent);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007389 if (CpuFeatures::IsSupported(VFP3)) {
7390 CpuFeatures::Scope scope(VFP3);
7391 // ARMv7 VFP3 instructions implementing double precision to integer
7392 // conversion using round to zero.
7393 __ ldr(scratch2, FieldMemOperand(source, HeapNumber::kMantissaOffset));
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007394 __ vmov(d7, scratch2, scratch);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007395 __ vcvt_s32_f64(s15, d7);
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007396 __ vmov(dest, s15);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007397 } else {
7398 // Get the top bits of the mantissa.
7399 __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask));
7400 // Put back the implicit 1.
7401 __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift));
7402 // Shift up the mantissa bits to take up the space the exponent used to
7403 // take. We just orred in the implicit bit so that took care of one and
7404 // we want to leave the sign bit 0 so we subtract 2 bits from the shift
7405 // distance.
7406 const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
7407 __ mov(scratch2, Operand(scratch2, LSL, shift_distance));
7408 // Put sign in zero flag.
7409 __ tst(scratch, Operand(HeapNumber::kSignMask));
7410 // Get the second half of the double. For some exponents we don't
7411 // actually need this because the bits get shifted out again, but
7412 // it's probably slower to test than just to do it.
7413 __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
7414 // Shift down 22 bits to get the last 10 bits.
7415 __ orr(scratch, scratch2, Operand(scratch, LSR, 32 - shift_distance));
7416 // Move down according to the exponent.
7417 __ mov(dest, Operand(scratch, LSR, dest));
7418 // Fix sign if sign bit was set.
7419 __ rsb(dest, dest, Operand(0), LeaveCC, ne);
7420 }
ager@chromium.org5aa501c2009-06-23 07:57:28 +00007421 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007422}
7423
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007424// For bitwise ops where the inputs are not both Smis we here try to determine
7425// whether both inputs are either Smis or at least heap numbers that can be
7426// represented by a 32 bit signed value. We truncate towards zero as required
7427// by the ES spec. If this is the case we do the bitwise op and see if the
7428// result is a Smi. If so, great, otherwise we try to find a heap number to
7429// write the answer into (either by allocating or by overwriting).
ager@chromium.org357bf652010-04-12 11:30:10 +00007430// On entry the operands are in lhs and rhs. On exit the answer is in r0.
7431void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
7432 Register lhs,
7433 Register rhs) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007434 Label slow, result_not_a_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00007435 Label rhs_is_smi, lhs_is_smi;
7436 Label done_checking_rhs, done_checking_lhs;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007437
ager@chromium.org357bf652010-04-12 11:30:10 +00007438 __ tst(lhs, Operand(kSmiTagMask));
7439 __ b(eq, &lhs_is_smi); // It's a Smi so don't check it's a heap number.
7440 __ CompareObjectType(lhs, r4, r4, HEAP_NUMBER_TYPE);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007441 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007442 GetInt32(masm, lhs, r3, r5, r4, &slow);
7443 __ jmp(&done_checking_lhs);
7444 __ bind(&lhs_is_smi);
7445 __ mov(r3, Operand(lhs, ASR, 1));
7446 __ bind(&done_checking_lhs);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007447
ager@chromium.org357bf652010-04-12 11:30:10 +00007448 __ tst(rhs, Operand(kSmiTagMask));
7449 __ b(eq, &rhs_is_smi); // It's a Smi so don't check it's a heap number.
7450 __ CompareObjectType(rhs, r4, r4, HEAP_NUMBER_TYPE);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007451 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007452 GetInt32(masm, rhs, r2, r5, r4, &slow);
7453 __ jmp(&done_checking_rhs);
7454 __ bind(&rhs_is_smi);
7455 __ mov(r2, Operand(rhs, ASR, 1));
7456 __ bind(&done_checking_rhs);
7457
7458 ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007459
7460 // r0 and r1: Original operands (Smi or heap numbers).
7461 // r2 and r3: Signed int32 operands.
7462 switch (op_) {
7463 case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
7464 case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
7465 case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
7466 case Token::SAR:
7467 // Use only the 5 least significant bits of the shift count.
7468 __ and_(r2, r2, Operand(0x1f));
7469 __ mov(r2, Operand(r3, ASR, r2));
7470 break;
7471 case Token::SHR:
7472 // Use only the 5 least significant bits of the shift count.
7473 __ and_(r2, r2, Operand(0x1f));
7474 __ mov(r2, Operand(r3, LSR, r2), SetCC);
7475 // SHR is special because it is required to produce a positive answer.
7476 // The code below for writing into heap numbers isn't capable of writing
7477 // the register as an unsigned int so we go to slow case if we hit this
7478 // case.
7479 __ b(mi, &slow);
7480 break;
7481 case Token::SHL:
7482 // Use only the 5 least significant bits of the shift count.
7483 __ and_(r2, r2, Operand(0x1f));
7484 __ mov(r2, Operand(r3, LSL, r2));
7485 break;
7486 default: UNREACHABLE();
7487 }
7488 // check that the *signed* result fits in a smi
7489 __ add(r3, r2, Operand(0x40000000), SetCC);
7490 __ b(mi, &result_not_a_smi);
7491 __ mov(r0, Operand(r2, LSL, kSmiTagSize));
7492 __ Ret();
7493
7494 Label have_to_allocate, got_a_heap_number;
7495 __ bind(&result_not_a_smi);
7496 switch (mode_) {
7497 case OVERWRITE_RIGHT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007498 __ tst(rhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007499 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007500 __ mov(r5, Operand(rhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007501 break;
7502 }
7503 case OVERWRITE_LEFT: {
ager@chromium.org357bf652010-04-12 11:30:10 +00007504 __ tst(lhs, Operand(kSmiTagMask));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007505 __ b(eq, &have_to_allocate);
ager@chromium.org357bf652010-04-12 11:30:10 +00007506 __ mov(r5, Operand(lhs));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007507 break;
7508 }
7509 case NO_OVERWRITE: {
7510 // Get a new heap number in r5. r6 and r7 are scratch.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007511 __ AllocateHeapNumber(r5, r6, r7, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007512 }
7513 default: break;
7514 }
7515 __ bind(&got_a_heap_number);
7516 // r2: Answer as signed int32.
7517 // r5: Heap number to write answer into.
7518
7519 // Nothing can go wrong now, so move the heap number to r0, which is the
7520 // result.
7521 __ mov(r0, Operand(r5));
7522
7523 // Tail call that writes the int32 in r2 to the heap number in r0, using
7524 // r3 as scratch. r0 is preserved and returned.
7525 WriteInt32ToHeapNumberStub stub(r2, r0, r3);
7526 __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
7527
7528 if (mode_ != NO_OVERWRITE) {
7529 __ bind(&have_to_allocate);
7530 // Get a new heap number in r5. r6 and r7 are scratch.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00007531 __ AllocateHeapNumber(r5, r6, r7, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007532 __ jmp(&got_a_heap_number);
7533 }
7534
7535 // If all else failed then we go to the runtime system.
7536 __ bind(&slow);
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007537 __ Push(lhs, rhs); // Restore stack.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007538 switch (op_) {
7539 case Token::BIT_OR:
7540 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
7541 break;
7542 case Token::BIT_AND:
7543 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
7544 break;
7545 case Token::BIT_XOR:
7546 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
7547 break;
7548 case Token::SAR:
7549 __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
7550 break;
7551 case Token::SHR:
7552 __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
7553 break;
7554 case Token::SHL:
7555 __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
7556 break;
7557 default:
7558 UNREACHABLE();
7559 }
7560}
7561
7562
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007563// Can we multiply by x with max two shifts and an add.
7564// This answers yes to all integers from 2 to 10.
7565static bool IsEasyToMultiplyBy(int x) {
7566 if (x < 2) return false; // Avoid special cases.
7567 if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
7568 if (IsPowerOf2(x)) return true; // Simple shift.
7569 if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
7570 if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
7571 return false;
7572}
7573
7574
7575// Can multiply by anything that IsEasyToMultiplyBy returns true for.
7576// Source and destination may be the same register. This routine does
7577// not set carry and overflow the way a mul instruction would.
7578static void MultiplyByKnownInt(MacroAssembler* masm,
7579 Register source,
7580 Register destination,
7581 int known_int) {
7582 if (IsPowerOf2(known_int)) {
7583 __ mov(destination, Operand(source, LSL, BitPosition(known_int)));
7584 } else if (PopCountLessThanEqual2(known_int)) {
7585 int first_bit = BitPosition(known_int);
7586 int second_bit = BitPosition(known_int ^ (1 << first_bit));
7587 __ add(destination, source, Operand(source, LSL, second_bit - first_bit));
7588 if (first_bit != 0) {
7589 __ mov(destination, Operand(destination, LSL, first_bit));
7590 }
7591 } else {
7592 ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
7593 int the_bit = BitPosition(known_int + 1);
7594 __ rsb(destination, source, Operand(source, LSL, the_bit));
7595 }
7596}
7597
7598
7599// This function (as opposed to MultiplyByKnownInt) takes the known int in a
7600// a register for the cases where it doesn't know a good trick, and may deliver
7601// a result that needs shifting.
7602static void MultiplyByKnownInt2(
7603 MacroAssembler* masm,
7604 Register result,
7605 Register source,
7606 Register known_int_register, // Smi tagged.
7607 int known_int,
7608 int* required_shift) { // Including Smi tag shift
7609 switch (known_int) {
7610 case 3:
7611 __ add(result, source, Operand(source, LSL, 1));
7612 *required_shift = 1;
7613 break;
7614 case 5:
7615 __ add(result, source, Operand(source, LSL, 2));
7616 *required_shift = 1;
7617 break;
7618 case 6:
7619 __ add(result, source, Operand(source, LSL, 1));
7620 *required_shift = 2;
7621 break;
7622 case 7:
7623 __ rsb(result, source, Operand(source, LSL, 3));
7624 *required_shift = 1;
7625 break;
7626 case 9:
7627 __ add(result, source, Operand(source, LSL, 3));
7628 *required_shift = 1;
7629 break;
7630 case 10:
7631 __ add(result, source, Operand(source, LSL, 2));
7632 *required_shift = 2;
7633 break;
7634 default:
7635 ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
7636 __ mul(result, source, known_int_register);
7637 *required_shift = 0;
7638 }
7639}
7640
7641
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00007642const char* GenericBinaryOpStub::GetName() {
7643 if (name_ != NULL) return name_;
7644 const int len = 100;
7645 name_ = Bootstrapper::AllocateAutoDeletedArray(len);
7646 if (name_ == NULL) return "OOM";
7647 const char* op_name = Token::Name(op_);
7648 const char* overwrite_name;
7649 switch (mode_) {
7650 case NO_OVERWRITE: overwrite_name = "Alloc"; break;
7651 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
7652 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
7653 default: overwrite_name = "UnknownOverwrite"; break;
7654 }
7655
7656 OS::SNPrintF(Vector<char>(name_, len),
7657 "GenericBinaryOpStub_%s_%s%s",
7658 op_name,
7659 overwrite_name,
7660 specialized_on_rhs_ ? "_ConstantRhs" : 0);
7661 return name_;
7662}
7663
7664
ager@chromium.org5c838252010-02-19 08:53:10 +00007665
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007666void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007667 // lhs_ : x
7668 // rhs_ : y
7669 // r0 : result
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007670
ager@chromium.org357bf652010-04-12 11:30:10 +00007671 Register result = r0;
7672 Register lhs = lhs_;
7673 Register rhs = rhs_;
7674
7675 // This code can't cope with other register allocations yet.
7676 ASSERT(result.is(r0) &&
7677 ((lhs.is(r0) && rhs.is(r1)) ||
7678 (lhs.is(r1) && rhs.is(r0))));
7679
7680 Register smi_test_reg = VirtualFrame::scratch0();
7681 Register scratch = VirtualFrame::scratch1();
7682
7683 // All ops need to know whether we are dealing with two Smis. Set up
7684 // smi_test_reg to tell us that.
7685 if (ShouldGenerateSmiCode()) {
7686 __ orr(smi_test_reg, lhs, Operand(rhs));
7687 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007688
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007689 switch (op_) {
7690 case Token::ADD: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007691 Label not_smi;
7692 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00007693 if (ShouldGenerateSmiCode()) {
7694 ASSERT(kSmiTag == 0); // Adjust code below.
7695 __ tst(smi_test_reg, Operand(kSmiTagMask));
7696 __ b(ne, &not_smi);
7697 __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
7698 // Return if no overflow.
7699 __ Ret(vc);
7700 __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
7701 }
7702 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::ADD);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007703 break;
7704 }
7705
7706 case Token::SUB: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007707 Label not_smi;
7708 // Fast path.
ager@chromium.org357bf652010-04-12 11:30:10 +00007709 if (ShouldGenerateSmiCode()) {
7710 ASSERT(kSmiTag == 0); // Adjust code below.
7711 __ tst(smi_test_reg, Operand(kSmiTagMask));
7712 __ b(ne, &not_smi);
7713 if (lhs.is(r1)) {
7714 __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
7715 // Return if no overflow.
7716 __ Ret(vc);
7717 __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
7718 } else {
7719 __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
7720 // Return if no overflow.
7721 __ Ret(vc);
7722 __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
7723 }
7724 }
7725 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::SUB);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007726 break;
7727 }
7728
7729 case Token::MUL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007730 Label not_smi, slow;
ager@chromium.org357bf652010-04-12 11:30:10 +00007731 if (ShouldGenerateSmiCode()) {
7732 ASSERT(kSmiTag == 0); // adjust code below
7733 __ tst(smi_test_reg, Operand(kSmiTagMask));
7734 Register scratch2 = smi_test_reg;
7735 smi_test_reg = no_reg;
7736 __ b(ne, &not_smi);
7737 // Remove tag from one operand (but keep sign), so that result is Smi.
7738 __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
7739 // Do multiplication
7740 // scratch = lower 32 bits of ip * lhs.
7741 __ smull(scratch, scratch2, lhs, ip);
7742 // Go slow on overflows (overflow bit is not set).
7743 __ mov(ip, Operand(scratch, ASR, 31));
7744 // No overflow if higher 33 bits are identical.
7745 __ cmp(ip, Operand(scratch2));
7746 __ b(ne, &slow);
7747 // Go slow on zero result to handle -0.
7748 __ tst(scratch, Operand(scratch));
7749 __ mov(result, Operand(scratch), LeaveCC, ne);
7750 __ Ret(ne);
7751 // We need -0 if we were multiplying a negative number with 0 to get 0.
7752 // We know one of them was zero.
7753 __ add(scratch2, rhs, Operand(lhs), SetCC);
7754 __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
7755 __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
7756 // Slow case. We fall through here if we multiplied a negative number
7757 // with 0, because that would mean we should produce -0.
7758 __ bind(&slow);
7759 }
7760 HandleBinaryOpSlowCases(masm, &not_smi, lhs, rhs, Builtins::MUL);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007761 break;
7762 }
7763
7764 case Token::DIV:
7765 case Token::MOD: {
7766 Label not_smi;
ager@chromium.org357bf652010-04-12 11:30:10 +00007767 if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007768 Label smi_is_unsuitable;
ager@chromium.org357bf652010-04-12 11:30:10 +00007769 __ BranchOnNotSmi(lhs, &not_smi);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007770 if (IsPowerOf2(constant_rhs_)) {
7771 if (op_ == Token::MOD) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007772 __ and_(rhs,
7773 lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007774 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
7775 SetCC);
7776 // We now have the answer, but if the input was negative we also
7777 // have the sign bit. Our work is done if the result is
7778 // positive or zero:
ager@chromium.org357bf652010-04-12 11:30:10 +00007779 if (!rhs.is(r0)) {
7780 __ mov(r0, rhs, LeaveCC, pl);
7781 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007782 __ Ret(pl);
7783 // A mod of a negative left hand side must return a negative number.
7784 // Unfortunately if the answer is 0 then we must return -0. And we
ager@chromium.org357bf652010-04-12 11:30:10 +00007785 // already optimistically trashed rhs so we may need to restore it.
7786 __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007787 // Next two instructions are conditional on the answer being -0.
ager@chromium.org357bf652010-04-12 11:30:10 +00007788 __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007789 __ b(eq, &smi_is_unsuitable);
7790 // We need to subtract the dividend. Eg. -3 % 4 == -3.
ager@chromium.org357bf652010-04-12 11:30:10 +00007791 __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007792 } else {
7793 ASSERT(op_ == Token::DIV);
ager@chromium.org357bf652010-04-12 11:30:10 +00007794 __ tst(lhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007795 Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
7796 __ b(ne, &smi_is_unsuitable); // Go slow on negative or remainder.
7797 int shift = 0;
7798 int d = constant_rhs_;
7799 while ((d & 1) == 0) {
7800 d >>= 1;
7801 shift++;
7802 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007803 __ mov(r0, Operand(lhs, LSR, shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007804 __ bic(r0, r0, Operand(kSmiTagMask));
7805 }
7806 } else {
7807 // Not a power of 2.
ager@chromium.org357bf652010-04-12 11:30:10 +00007808 __ tst(lhs, Operand(0x80000000u));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007809 __ b(ne, &smi_is_unsuitable);
7810 // Find a fixed point reciprocal of the divisor so we can divide by
7811 // multiplying.
7812 double divisor = 1.0 / constant_rhs_;
7813 int shift = 32;
7814 double scale = 4294967296.0; // 1 << 32.
7815 uint32_t mul;
7816 // Maximise the precision of the fixed point reciprocal.
7817 while (true) {
7818 mul = static_cast<uint32_t>(scale * divisor);
7819 if (mul >= 0x7fffffff) break;
7820 scale *= 2.0;
7821 shift++;
7822 }
7823 mul++;
ager@chromium.org357bf652010-04-12 11:30:10 +00007824 Register scratch2 = smi_test_reg;
7825 smi_test_reg = no_reg;
7826 __ mov(scratch2, Operand(mul));
7827 __ umull(scratch, scratch2, scratch2, lhs);
7828 __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
7829 // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
7830 // rhs is still the known rhs. rhs is Smi tagged.
7831 // lhs is still the unkown lhs. lhs is Smi tagged.
7832 int required_scratch_shift = 0; // Including the Smi tag shift of 1.
7833 // scratch = scratch2 * rhs.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007834 MultiplyByKnownInt2(masm,
ager@chromium.org357bf652010-04-12 11:30:10 +00007835 scratch,
7836 scratch2,
7837 rhs,
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007838 constant_rhs_,
ager@chromium.org357bf652010-04-12 11:30:10 +00007839 &required_scratch_shift);
7840 // scratch << required_scratch_shift is now the Smi tagged rhs *
7841 // (lhs / rhs) where / indicates integer division.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007842 if (op_ == Token::DIV) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007843 __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007844 __ b(ne, &smi_is_unsuitable); // There was a remainder.
ager@chromium.org357bf652010-04-12 11:30:10 +00007845 __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007846 } else {
7847 ASSERT(op_ == Token::MOD);
ager@chromium.org357bf652010-04-12 11:30:10 +00007848 __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007849 }
7850 }
7851 __ Ret();
7852 __ bind(&smi_is_unsuitable);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007853 }
ager@chromium.org357bf652010-04-12 11:30:10 +00007854 HandleBinaryOpSlowCases(
7855 masm,
7856 &not_smi,
7857 lhs,
7858 rhs,
7859 op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007860 break;
7861 }
7862
7863 case Token::BIT_OR:
7864 case Token::BIT_AND:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007865 case Token::BIT_XOR:
7866 case Token::SAR:
7867 case Token::SHR:
7868 case Token::SHL: {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007869 Label slow;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007870 ASSERT(kSmiTag == 0); // adjust code below
ager@chromium.org357bf652010-04-12 11:30:10 +00007871 __ tst(smi_test_reg, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007872 __ b(ne, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007873 Register scratch2 = smi_test_reg;
7874 smi_test_reg = no_reg;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007875 switch (op_) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007876 case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
7877 case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
7878 case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007879 case Token::SAR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007880 // Remove tags from right operand.
ager@chromium.org357bf652010-04-12 11:30:10 +00007881 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
7882 __ mov(result, Operand(lhs, ASR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007883 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00007884 __ bic(result, result, Operand(kSmiTagMask));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007885 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007886 case Token::SHR:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007887 // Remove tags from operands. We can't do this on a 31 bit number
7888 // because then the 0s get shifted into bit 30 instead of bit 31.
ager@chromium.org357bf652010-04-12 11:30:10 +00007889 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
7890 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
7891 __ mov(scratch, Operand(scratch, LSR, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007892 // Unsigned shift is not allowed to produce a negative number, so
7893 // check the sign bit and the sign bit after Smi tagging.
ager@chromium.org357bf652010-04-12 11:30:10 +00007894 __ tst(scratch, Operand(0xc0000000));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007895 __ b(ne, &slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007896 // Smi tag result.
ager@chromium.org357bf652010-04-12 11:30:10 +00007897 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007898 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007899 case Token::SHL:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007900 // Remove tags from operands.
ager@chromium.org357bf652010-04-12 11:30:10 +00007901 __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
7902 __ GetLeastBitsFromSmi(scratch2, rhs, 5);
7903 __ mov(scratch, Operand(scratch, LSL, scratch2));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007904 // Check that the signed result fits in a Smi.
ager@chromium.org357bf652010-04-12 11:30:10 +00007905 __ add(scratch2, scratch, Operand(0x40000000), SetCC);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007906 __ b(mi, &slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007907 __ mov(result, Operand(scratch, LSL, kSmiTagSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007908 break;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007909 default: UNREACHABLE();
7910 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007911 __ Ret();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007912 __ bind(&slow);
ager@chromium.org357bf652010-04-12 11:30:10 +00007913 HandleNonSmiBitwiseOp(masm, lhs, rhs);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007914 break;
7915 }
7916
7917 default: UNREACHABLE();
7918 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007919 // This code should be unreachable.
7920 __ stop("Unreachable");
ager@chromium.org357bf652010-04-12 11:30:10 +00007921
7922 // Generate an unreachable reference to the DEFAULT stub so that it can be
7923 // found at the end of this stub when clearing ICs at GC.
7924 // TODO(kaznacheev): Check performance impact and get rid of this.
7925 if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
7926 GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
7927 __ CallStub(&uninit);
7928 }
7929}
7930
7931
7932void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
7933 Label get_result;
7934
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00007935 __ Push(r1, r0);
ager@chromium.org357bf652010-04-12 11:30:10 +00007936
7937 // Internal frame is necessary to handle exceptions properly.
7938 __ EnterInternalFrame();
7939 // Call the stub proper to get the result in r0.
7940 __ Call(&get_result);
7941 __ LeaveInternalFrame();
7942
7943 __ push(r0);
7944
7945 __ mov(r0, Operand(Smi::FromInt(MinorKey())));
7946 __ push(r0);
7947 __ mov(r0, Operand(Smi::FromInt(op_)));
7948 __ push(r0);
7949 __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
7950 __ push(r0);
7951
7952 __ TailCallExternalReference(
7953 ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
7954 6,
7955 1);
7956
7957 // The entry point for the result calculation is assumed to be immediately
7958 // after this sequence.
7959 __ bind(&get_result);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007960}
7961
7962
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007963Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
ager@chromium.org357bf652010-04-12 11:30:10 +00007964 GenericBinaryOpStub stub(key, type_info);
7965 return stub.GetCode();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007966}
7967
7968
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007969void StackCheckStub::Generate(MacroAssembler* masm) {
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007970 // Do tail-call to runtime routine. Runtime routines expect at least one
7971 // argument, so give it a Smi.
7972 __ mov(r0, Operand(Smi::FromInt(0)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007973 __ push(r0);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00007974 __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007975
7976 __ StubReturn(1);
7977}
7978
7979
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007980void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007981 Label slow, done;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00007982
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007983 if (op_ == Token::SUB) {
7984 // Check whether the value is a smi.
7985 Label try_float;
7986 __ tst(r0, Operand(kSmiTagMask));
7987 __ b(ne, &try_float);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007988
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007989 // Go slow case if the value of the expression is zero
7990 // to make sure that we switch between 0 and -0.
7991 __ cmp(r0, Operand(0));
7992 __ b(eq, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007993
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007994 // The value of the expression is a smi that is not zero. Try
7995 // optimistic subtraction '0 - value'.
7996 __ rsb(r1, r0, Operand(0), SetCC);
7997 __ b(vs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00007998
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00007999 __ mov(r0, Operand(r1)); // Set r0 to result.
8000 __ b(&done);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008001
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008002 __ bind(&try_float);
8003 __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE);
8004 __ b(ne, &slow);
8005 // r0 is a heap number. Get a new heap number in r1.
8006 if (overwrite_) {
8007 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8008 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8009 __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8010 } else {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00008011 __ AllocateHeapNumber(r1, r2, r3, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008012 __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
8013 __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
8014 __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
8015 __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
8016 __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
8017 __ mov(r0, Operand(r1));
8018 }
8019 } else if (op_ == Token::BIT_NOT) {
8020 // Check if the operand is a heap number.
8021 __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE);
8022 __ b(ne, &slow);
8023
8024 // Convert the heap number is r0 to an untagged integer in r1.
8025 GetInt32(masm, r0, r1, r2, r3, &slow);
8026
8027 // Do the bitwise operation (move negated) and check if the result
8028 // fits in a smi.
8029 Label try_float;
8030 __ mvn(r1, Operand(r1));
8031 __ add(r2, r1, Operand(0x40000000), SetCC);
8032 __ b(mi, &try_float);
8033 __ mov(r0, Operand(r1, LSL, kSmiTagSize));
8034 __ b(&done);
8035
8036 __ bind(&try_float);
8037 if (!overwrite_) {
8038 // Allocate a fresh heap number, but don't overwrite r0 until
8039 // we're sure we can do it without going through the slow case
8040 // that needs the value in r0.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00008041 __ AllocateHeapNumber(r2, r3, r4, &slow);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008042 __ mov(r0, Operand(r2));
8043 }
8044
8045 // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
8046 // have to set up a frame.
8047 WriteInt32ToHeapNumberStub stub(r1, r0, r2);
8048 __ push(lr);
8049 __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
8050 __ pop(lr);
8051 } else {
8052 UNIMPLEMENTED();
8053 }
8054
8055 __ bind(&done);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008056 __ StubReturn(1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008057
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008058 // Handle the slow case by jumping to the JavaScript builtin.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008059 __ bind(&slow);
8060 __ push(r0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008061 switch (op_) {
8062 case Token::SUB:
8063 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
8064 break;
8065 case Token::BIT_NOT:
8066 __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
8067 break;
8068 default:
8069 UNREACHABLE();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008070 }
ager@chromium.orga1645e22009-09-09 19:27:10 +00008071}
8072
8073
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008074void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008075 // r0 holds the exception.
8076
8077 // Adjust this code if not the case.
8078 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
8079
8080 // Drop the sp to the top of the handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008081 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
8082 __ ldr(sp, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008083
8084 // Restore the next handler and frame pointer, discard handler state.
8085 ASSERT(StackHandlerConstants::kNextOffset == 0);
8086 __ pop(r2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008087 __ str(r2, MemOperand(r3));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008088 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
8089 __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state.
8090
8091 // Before returning we restore the context from the frame pointer if
8092 // not NULL. The frame pointer is NULL in the exception handler of a
8093 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008094 __ cmp(fp, Operand(0));
8095 // Set cp to NULL if fp is NULL.
8096 __ mov(cp, Operand(0), LeaveCC, eq);
8097 // Restore cp otherwise.
8098 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008099#ifdef DEBUG
8100 if (FLAG_debug_code) {
8101 __ mov(lr, Operand(pc));
8102 }
8103#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008104 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008105 __ pop(pc);
8106}
8107
8108
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008109void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
8110 UncatchableExceptionType type) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008111 // Adjust this code if not the case.
8112 ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
8113
8114 // Drop sp to the top stack handler.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008115 __ mov(r3, Operand(ExternalReference(Top::k_handler_address)));
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008116 __ ldr(sp, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008117
8118 // Unwind the handlers until the ENTRY handler is found.
8119 Label loop, done;
8120 __ bind(&loop);
8121 // Load the type of the current stack handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008122 const int kStateOffset = StackHandlerConstants::kStateOffset;
8123 __ ldr(r2, MemOperand(sp, kStateOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008124 __ cmp(r2, Operand(StackHandler::ENTRY));
8125 __ b(eq, &done);
8126 // Fetch the next handler in the list.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008127 const int kNextOffset = StackHandlerConstants::kNextOffset;
8128 __ ldr(sp, MemOperand(sp, kNextOffset));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008129 __ jmp(&loop);
8130 __ bind(&done);
8131
8132 // Set the top handler address to next handler past the current ENTRY handler.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008133 ASSERT(StackHandlerConstants::kNextOffset == 0);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008134 __ pop(r2);
8135 __ str(r2, MemOperand(r3));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008136
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008137 if (type == OUT_OF_MEMORY) {
8138 // Set external caught exception to false.
8139 ExternalReference external_caught(Top::k_external_caught_exception_address);
8140 __ mov(r0, Operand(false));
8141 __ mov(r2, Operand(external_caught));
8142 __ str(r0, MemOperand(r2));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008143
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008144 // Set pending exception and r0 to out of memory exception.
8145 Failure* out_of_memory = Failure::OutOfMemoryException();
8146 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
8147 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address)));
8148 __ str(r0, MemOperand(r2));
8149 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008150
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008151 // Stack layout at this point. See also StackHandlerConstants.
8152 // sp -> state (ENTRY)
8153 // fp
8154 // lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008155
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008156 // Discard handler state (r2 is not used) and restore frame pointer.
8157 ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
8158 __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state.
8159 // Before returning we restore the context from the frame pointer if
8160 // not NULL. The frame pointer is NULL in the exception handler of a
8161 // JS entry frame.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008162 __ cmp(fp, Operand(0));
8163 // Set cp to NULL if fp is NULL.
8164 __ mov(cp, Operand(0), LeaveCC, eq);
8165 // Restore cp otherwise.
8166 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008167#ifdef DEBUG
8168 if (FLAG_debug_code) {
8169 __ mov(lr, Operand(pc));
8170 }
8171#endif
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008172 ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008173 __ pop(pc);
8174}
8175
8176
8177void CEntryStub::GenerateCore(MacroAssembler* masm,
8178 Label* throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008179 Label* throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008180 Label* throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008181 bool do_gc,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008182 bool always_allocate,
8183 int frame_alignment_skew) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008184 // r0: result parameter for PerformGC, if any
8185 // r4: number of arguments including receiver (C callee-saved)
8186 // r5: pointer to builtin function (C callee-saved)
8187 // r6: pointer to the first argument (C callee-saved)
8188
8189 if (do_gc) {
8190 // Passing r0.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008191 __ PrepareCallCFunction(1, r1);
8192 __ CallCFunction(ExternalReference::perform_gc_function(), 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008193 }
8194
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008195 ExternalReference scope_depth =
8196 ExternalReference::heap_always_allocate_scope_depth();
8197 if (always_allocate) {
8198 __ mov(r0, Operand(scope_depth));
8199 __ ldr(r1, MemOperand(r0));
8200 __ add(r1, r1, Operand(1));
8201 __ str(r1, MemOperand(r0));
8202 }
8203
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008204 // Call C built-in.
8205 // r0 = argc, r1 = argv
8206 __ mov(r0, Operand(r4));
8207 __ mov(r1, Operand(r6));
8208
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008209 int frame_alignment = MacroAssembler::ActivationFrameAlignment();
8210 int frame_alignment_mask = frame_alignment - 1;
8211#if defined(V8_HOST_ARCH_ARM)
8212 if (FLAG_debug_code) {
8213 if (frame_alignment > kPointerSize) {
8214 Label alignment_as_expected;
8215 ASSERT(IsPowerOf2(frame_alignment));
8216 __ sub(r2, sp, Operand(frame_alignment_skew));
8217 __ tst(r2, Operand(frame_alignment_mask));
8218 __ b(eq, &alignment_as_expected);
8219 // Don't use Check here, as it will call Runtime_Abort re-entering here.
8220 __ stop("Unexpected alignment");
8221 __ bind(&alignment_as_expected);
8222 }
8223 }
8224#endif
8225
8226 // Just before the call (jump) below lr is pushed, so the actual alignment is
8227 // adding one to the current skew.
8228 int alignment_before_call =
8229 (frame_alignment_skew + kPointerSize) & frame_alignment_mask;
8230 if (alignment_before_call > 0) {
8231 // Push until the alignment before the call is met.
8232 __ mov(r2, Operand(0));
8233 for (int i = alignment_before_call;
8234 (i & frame_alignment_mask) != 0;
8235 i += kPointerSize) {
8236 __ push(r2);
8237 }
8238 }
8239
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008240 // TODO(1242173): To let the GC traverse the return address of the exit
8241 // frames, we need to know where the return address is. Right now,
8242 // we push it on the stack to be able to find it again, but we never
8243 // restore from it in case of changes, which makes it impossible to
8244 // support moving the C entry code stub. This should be fixed, but currently
8245 // this is OK because the CEntryStub gets generated so early in the V8 boot
8246 // sequence that it is not moving ever.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008247 masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00008248 masm->push(lr);
8249 masm->Jump(r5);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008250
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008251 // Restore sp back to before aligning the stack.
8252 if (alignment_before_call > 0) {
8253 __ add(sp, sp, Operand(alignment_before_call));
8254 }
8255
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008256 if (always_allocate) {
8257 // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
8258 // though (contain the result).
8259 __ mov(r2, Operand(scope_depth));
8260 __ ldr(r3, MemOperand(r2));
8261 __ sub(r3, r3, Operand(1));
8262 __ str(r3, MemOperand(r2));
8263 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008264
8265 // check for failure result
8266 Label failure_returned;
8267 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
8268 // Lower 2 bits of r2 are 0 iff r0 has failure tag.
8269 __ add(r2, r0, Operand(1));
8270 __ tst(r2, Operand(kFailureTagMask));
8271 __ b(eq, &failure_returned);
8272
8273 // Exit C frame and return.
8274 // r0:r1: result
8275 // sp: stack pointer
8276 // fp: frame pointer
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008277 __ LeaveExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008278
8279 // check if we should retry or throw exception
8280 Label retry;
8281 __ bind(&failure_returned);
8282 ASSERT(Failure::RETRY_AFTER_GC == 0);
8283 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
8284 __ b(eq, &retry);
8285
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008286 // Special handling of out of memory exceptions.
8287 Failure* out_of_memory = Failure::OutOfMemoryException();
8288 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
8289 __ b(eq, throw_out_of_memory_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008290
8291 // Retrieve the pending exception and clear the variable.
ager@chromium.org32912102009-01-16 10:38:43 +00008292 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008293 __ ldr(r3, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00008294 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008295 __ ldr(r0, MemOperand(ip));
8296 __ str(r3, MemOperand(ip));
8297
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008298 // Special handling of termination exceptions which are uncatchable
8299 // by javascript code.
8300 __ cmp(r0, Operand(Factory::termination_exception()));
8301 __ b(eq, throw_termination_exception);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008302
8303 // Handle normal exception.
8304 __ jmp(throw_normal_exception);
8305
8306 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
8307}
8308
8309
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008310void CEntryStub::Generate(MacroAssembler* masm) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008311 // Called from JavaScript; parameters are on stack as if calling JS function
8312 // r0: number of arguments including receiver
8313 // r1: pointer to builtin function
8314 // fp: frame pointer (restored after C call)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008315 // sp: stack pointer (restored as callee's sp after C call)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008316 // cp: current context (C callee-saved)
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008317
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008318 // Result returned in r0 or r0+r1 by default.
8319
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008320 // NOTE: Invocations of builtins may return failure objects
8321 // instead of a proper result. The builtin entry handles
8322 // this by performing a garbage collection and retrying the
8323 // builtin once.
8324
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008325 // Enter the exit frame that transitions from JavaScript to C++.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00008326 __ EnterExitFrame(mode_);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008327
8328 // r4: number of arguments (C callee-saved)
8329 // r5: pointer to builtin function (C callee-saved)
8330 // r6: pointer to first argument (C callee-saved)
8331
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008332 Label throw_normal_exception;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008333 Label throw_termination_exception;
8334 Label throw_out_of_memory_exception;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008335
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008336 // Call into the runtime system.
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008337 GenerateCore(masm,
8338 &throw_normal_exception,
8339 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008340 &throw_out_of_memory_exception,
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00008341 false,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008342 false,
8343 -kPointerSize);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008344
8345 // Do space-specific GC and retry runtime call.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008346 GenerateCore(masm,
8347 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008348 &throw_termination_exception,
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008349 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008350 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008351 false,
8352 0);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008353
8354 // Do full GC and retry runtime call one final time.
8355 Failure* failure = Failure::InternalError();
8356 __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
8357 GenerateCore(masm,
8358 &throw_normal_exception,
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008359 &throw_termination_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008360 &throw_out_of_memory_exception,
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00008361 true,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008362 true,
8363 kPointerSize);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008364
8365 __ bind(&throw_out_of_memory_exception);
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00008366 GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
8367
8368 __ bind(&throw_termination_exception);
8369 GenerateThrowUncatchable(masm, TERMINATION);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008370
8371 __ bind(&throw_normal_exception);
8372 GenerateThrowTOS(masm);
8373}
8374
8375
8376void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
8377 // r0: code entry
8378 // r1: function
8379 // r2: receiver
8380 // r3: argc
8381 // [sp+0]: argv
8382
8383 Label invoke, exit;
8384
8385 // Called from C, so do not pop argc and args on exit (preserve sp)
8386 // No need to save register-passed args
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008387 // Save callee-saved registers (incl. cp and fp), sp, and lr
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008388 __ stm(db_w, sp, kCalleeSaved | lr.bit());
8389
8390 // Get address of argv, see stm above.
8391 // r0: code entry
8392 // r1: function
8393 // r2: receiver
8394 // r3: argc
ager@chromium.org5c838252010-02-19 08:53:10 +00008395 __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008396
8397 // Push a frame with special values setup to mark it as an entry frame.
8398 // r0: code entry
8399 // r1: function
8400 // r2: receiver
8401 // r3: argc
8402 // r4: argv
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008403 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008404 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
8405 __ mov(r7, Operand(Smi::FromInt(marker)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008406 __ mov(r6, Operand(Smi::FromInt(marker)));
8407 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address)));
8408 __ ldr(r5, MemOperand(r5));
lrn@chromium.orgc34f5802010-04-28 12:53:43 +00008409 __ Push(r8, r7, r6, r5);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008410
8411 // Setup frame pointer for the frame to be pushed.
8412 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
8413
8414 // Call a faked try-block that does the invoke.
8415 __ bl(&invoke);
8416
8417 // Caught exception: Store result (exception) in the pending
8418 // exception field in the JSEnv and return a failure sentinel.
8419 // Coming in here the fp will be invalid because the PushTryHandler below
8420 // sets it to 0 to signal the existence of the JSEntry frame.
ager@chromium.org32912102009-01-16 10:38:43 +00008421 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008422 __ str(r0, MemOperand(ip));
ager@chromium.org3bf7b912008-11-17 09:09:45 +00008423 __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008424 __ b(&exit);
8425
8426 // Invoke: Link this frame into the handler chain.
8427 __ bind(&invoke);
8428 // Must preserve r0-r4, r5-r7 are available.
8429 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008430 // If an exception not caught by another handler occurs, this handler
8431 // returns control to the code after the bl(&invoke) above, which
8432 // restores all kCalleeSaved registers (including cp and fp) to their
8433 // saved values before returning a failure to C.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008434
8435 // Clear any pending exceptions.
8436 __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
8437 __ ldr(r5, MemOperand(ip));
ager@chromium.org32912102009-01-16 10:38:43 +00008438 __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008439 __ str(r5, MemOperand(ip));
8440
8441 // Invoke the function by calling through JS entry trampoline builtin.
8442 // Notice that we cannot store a reference to the trampoline code directly in
8443 // this stub, because runtime stubs are not traversed when doing GC.
8444
8445 // Expected registers by Builtins::JSEntryTrampoline
8446 // r0: code entry
8447 // r1: function
8448 // r2: receiver
8449 // r3: argc
8450 // r4: argv
8451 if (is_construct) {
8452 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
8453 __ mov(ip, Operand(construct_entry));
8454 } else {
8455 ExternalReference entry(Builtins::JSEntryTrampoline);
8456 __ mov(ip, Operand(entry));
8457 }
8458 __ ldr(ip, MemOperand(ip)); // deref address
8459
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008460 // Branch and link to JSEntryTrampoline. We don't use the double underscore
8461 // macro for the add instruction because we don't want the coverage tool
8462 // inserting instructions here after we read the pc.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008463 __ mov(lr, Operand(pc));
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008464 masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008465
8466 // Unlink this frame from the handler chain. When reading the
8467 // address of the next handler, there is no need to use the address
8468 // displacement since the current stack pointer (sp) points directly
8469 // to the stack handler.
8470 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset));
8471 __ mov(ip, Operand(ExternalReference(Top::k_handler_address)));
8472 __ str(r3, MemOperand(ip));
8473 // No need to restore registers
8474 __ add(sp, sp, Operand(StackHandlerConstants::kSize));
8475
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008476
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008477 __ bind(&exit); // r0 holds result
8478 // Restore the top frame descriptors from the stack.
8479 __ pop(r3);
8480 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
8481 __ str(r3, MemOperand(ip));
8482
8483 // Reset the stack to the callee saved registers.
8484 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
8485
8486 // Restore callee-saved registers and return.
8487#ifdef DEBUG
ager@chromium.org65dad4b2009-04-23 08:48:43 +00008488 if (FLAG_debug_code) {
8489 __ mov(lr, Operand(pc));
8490 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008491#endif
8492 __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
8493}
8494
8495
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008496// This stub performs an instanceof, calling the builtin function if
8497// necessary. Uses r1 for the object, r0 for the function that it may
8498// be an instance of (these are fetched from the stack).
8499void InstanceofStub::Generate(MacroAssembler* masm) {
8500 // Get the object - slow case for smis (we may need to throw an exception
8501 // depending on the rhs).
8502 Label slow, loop, is_instance, is_not_instance;
8503 __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
8504 __ BranchOnSmi(r0, &slow);
8505
8506 // Check that the left hand is a JS object and put map in r3.
8507 __ CompareObjectType(r0, r3, r2, FIRST_JS_OBJECT_TYPE);
8508 __ b(lt, &slow);
8509 __ cmp(r2, Operand(LAST_JS_OBJECT_TYPE));
8510 __ b(gt, &slow);
8511
8512 // Get the prototype of the function (r4 is result, r2 is scratch).
ager@chromium.org5c838252010-02-19 08:53:10 +00008513 __ ldr(r1, MemOperand(sp, 0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008514 // r1 is function, r3 is map.
8515
8516 // Look up the function and the map in the instanceof cache.
8517 Label miss;
8518 __ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
8519 __ cmp(r1, ip);
8520 __ b(ne, &miss);
8521 __ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
8522 __ cmp(r3, ip);
8523 __ b(ne, &miss);
8524 __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
8525 __ pop();
8526 __ pop();
8527 __ mov(pc, Operand(lr));
8528
8529 __ bind(&miss);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008530 __ TryGetFunctionPrototype(r1, r4, r2, &slow);
8531
8532 // Check that the function prototype is a JS object.
8533 __ BranchOnSmi(r4, &slow);
8534 __ CompareObjectType(r4, r5, r5, FIRST_JS_OBJECT_TYPE);
8535 __ b(lt, &slow);
8536 __ cmp(r5, Operand(LAST_JS_OBJECT_TYPE));
8537 __ b(gt, &slow);
8538
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008539 __ StoreRoot(r1, Heap::kInstanceofCacheFunctionRootIndex);
8540 __ StoreRoot(r3, Heap::kInstanceofCacheMapRootIndex);
8541
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008542 // Register mapping: r3 is object map and r4 is function prototype.
8543 // Get prototype of object into r2.
8544 __ ldr(r2, FieldMemOperand(r3, Map::kPrototypeOffset));
8545
8546 // Loop through the prototype chain looking for the function prototype.
8547 __ bind(&loop);
8548 __ cmp(r2, Operand(r4));
8549 __ b(eq, &is_instance);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00008550 __ LoadRoot(ip, Heap::kNullValueRootIndex);
8551 __ cmp(r2, ip);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008552 __ b(eq, &is_not_instance);
8553 __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
8554 __ ldr(r2, FieldMemOperand(r2, Map::kPrototypeOffset));
8555 __ jmp(&loop);
8556
8557 __ bind(&is_instance);
8558 __ mov(r0, Operand(Smi::FromInt(0)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008559 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008560 __ pop();
8561 __ pop();
8562 __ mov(pc, Operand(lr)); // Return.
8563
8564 __ bind(&is_not_instance);
8565 __ mov(r0, Operand(Smi::FromInt(1)));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008566 __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008567 __ pop();
8568 __ pop();
8569 __ mov(pc, Operand(lr)); // Return.
8570
8571 // Slow-case. Tail call builtin.
8572 __ bind(&slow);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008573 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
8574}
8575
8576
ager@chromium.org7c537e22008-10-16 08:43:32 +00008577void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
8578 // The displacement is the offset of the last parameter (if any)
8579 // relative to the frame pointer.
8580 static const int kDisplacement =
8581 StandardFrameConstants::kCallerSPOffset - kPointerSize;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008582
ager@chromium.org7c537e22008-10-16 08:43:32 +00008583 // Check that the key is a smi.
8584 Label slow;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00008585 __ BranchOnNotSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008586
ager@chromium.org7c537e22008-10-16 08:43:32 +00008587 // Check if the calling frame is an arguments adaptor frame.
8588 Label adaptor;
8589 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
8590 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008591 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org7c537e22008-10-16 08:43:32 +00008592 __ b(eq, &adaptor);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008593
ager@chromium.org7c537e22008-10-16 08:43:32 +00008594 // Check index against formal parameters count limit passed in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00008595 // through register r0. Use unsigned comparison to get negative
ager@chromium.org7c537e22008-10-16 08:43:32 +00008596 // check for free.
8597 __ cmp(r1, r0);
8598 __ b(cs, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008599
ager@chromium.org7c537e22008-10-16 08:43:32 +00008600 // Read the argument from the stack and return it.
8601 __ sub(r3, r0, r1);
8602 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
8603 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00008604 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008605
8606 // Arguments adaptor case: Check index against actual arguments
8607 // limit found in the arguments adaptor frame. Use unsigned
8608 // comparison to get negative check for free.
8609 __ bind(&adaptor);
8610 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
8611 __ cmp(r1, r0);
8612 __ b(cs, &slow);
8613
8614 // Read the argument from the adaptor frame and return it.
8615 __ sub(r3, r0, r1);
8616 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
8617 __ ldr(r0, MemOperand(r3, kDisplacement));
ager@chromium.org9085a012009-05-11 19:22:57 +00008618 __ Jump(lr);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008619
8620 // Slow-case: Handle non-smi or out-of-bounds access to arguments
8621 // by calling the runtime system.
8622 __ bind(&slow);
8623 __ push(r1);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008624 __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008625}
8626
8627
8628void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
ager@chromium.org5c838252010-02-19 08:53:10 +00008629 // sp[0] : number of parameters
8630 // sp[4] : receiver displacement
8631 // sp[8] : function
8632
ager@chromium.org7c537e22008-10-16 08:43:32 +00008633 // Check if the calling frame is an arguments adaptor frame.
ager@chromium.org5c838252010-02-19 08:53:10 +00008634 Label adaptor_frame, try_allocate, runtime;
ager@chromium.org7c537e22008-10-16 08:43:32 +00008635 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
8636 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
ager@chromium.org18ad94b2009-09-02 08:22:29 +00008637 __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
ager@chromium.org5c838252010-02-19 08:53:10 +00008638 __ b(eq, &adaptor_frame);
8639
8640 // Get the length from the frame.
8641 __ ldr(r1, MemOperand(sp, 0));
8642 __ b(&try_allocate);
ager@chromium.org7c537e22008-10-16 08:43:32 +00008643
8644 // Patch the arguments.length and the parameters pointer.
ager@chromium.org5c838252010-02-19 08:53:10 +00008645 __ bind(&adaptor_frame);
8646 __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
8647 __ str(r1, MemOperand(sp, 0));
8648 __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
ager@chromium.org7c537e22008-10-16 08:43:32 +00008649 __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
8650 __ str(r3, MemOperand(sp, 1 * kPointerSize));
8651
ager@chromium.org5c838252010-02-19 08:53:10 +00008652 // Try the new space allocation. Start out with computing the size
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008653 // of the arguments object and the elements array in words.
ager@chromium.org5c838252010-02-19 08:53:10 +00008654 Label add_arguments_object;
8655 __ bind(&try_allocate);
8656 __ cmp(r1, Operand(0));
8657 __ b(eq, &add_arguments_object);
8658 __ mov(r1, Operand(r1, LSR, kSmiTagSize));
8659 __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
8660 __ bind(&add_arguments_object);
8661 __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
8662
8663 // Do the allocation of both objects in one go.
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008664 __ AllocateInNewSpace(
8665 r1,
8666 r0,
8667 r2,
8668 r3,
8669 &runtime,
8670 static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
ager@chromium.org5c838252010-02-19 08:53:10 +00008671
8672 // Get the arguments boilerplate from the current (global) context.
8673 int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
8674 __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
8675 __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
8676 __ ldr(r4, MemOperand(r4, offset));
8677
8678 // Copy the JS object part.
8679 for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
8680 __ ldr(r3, FieldMemOperand(r4, i));
8681 __ str(r3, FieldMemOperand(r0, i));
8682 }
8683
8684 // Setup the callee in-object property.
8685 ASSERT(Heap::arguments_callee_index == 0);
8686 __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
8687 __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
8688
8689 // Get the length (smi tagged) and set that as an in-object property too.
8690 ASSERT(Heap::arguments_length_index == 1);
8691 __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
8692 __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
8693
8694 // If there are no actual arguments, we're done.
8695 Label done;
8696 __ cmp(r1, Operand(0));
8697 __ b(eq, &done);
8698
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008699 // Get the parameters pointer from the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00008700 __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
ager@chromium.org5c838252010-02-19 08:53:10 +00008701
8702 // Setup the elements pointer in the allocated arguments object and
8703 // initialize the header in the elements fixed array.
8704 __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
8705 __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
8706 __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
8707 __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
8708 __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008709 __ mov(r1, Operand(r1, LSR, kSmiTagSize)); // Untag the length for the loop.
ager@chromium.org5c838252010-02-19 08:53:10 +00008710
8711 // Copy the fixed array slots.
8712 Label loop;
8713 // Setup r4 to point to the first array slot.
8714 __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
8715 __ bind(&loop);
8716 // Pre-decrement r2 with kPointerSize on each iteration.
8717 // Pre-decrement in order to skip receiver.
8718 __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
8719 // Post-increment r4 with kPointerSize on each iteration.
8720 __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
8721 __ sub(r1, r1, Operand(1));
8722 __ cmp(r1, Operand(0));
8723 __ b(ne, &loop);
8724
8725 // Return and remove the on-stack parameters.
8726 __ bind(&done);
8727 __ add(sp, sp, Operand(3 * kPointerSize));
8728 __ Ret();
8729
ager@chromium.org7c537e22008-10-16 08:43:32 +00008730 // Do the runtime call to allocate the arguments object.
8731 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00008732 __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00008733}
8734
8735
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008736void RegExpExecStub::Generate(MacroAssembler* masm) {
8737 // Just jump directly to runtime if native RegExp is not selected at compile
8738 // time or if regexp entry in generated code is turned off runtime switch or
8739 // at compilation.
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008740#ifdef V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008741 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008742#else // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008743 if (!FLAG_regexp_entry_native) {
8744 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
8745 return;
8746 }
8747
8748 // Stack frame on entry.
8749 // sp[0]: last_match_info (expected JSArray)
8750 // sp[4]: previous index
8751 // sp[8]: subject string
8752 // sp[12]: JSRegExp object
8753
8754 static const int kLastMatchInfoOffset = 0 * kPointerSize;
8755 static const int kPreviousIndexOffset = 1 * kPointerSize;
8756 static const int kSubjectOffset = 2 * kPointerSize;
8757 static const int kJSRegExpOffset = 3 * kPointerSize;
8758
8759 Label runtime, invoke_regexp;
8760
8761 // Allocation of registers for this function. These are in callee save
8762 // registers and will be preserved by the call to the native RegExp code, as
8763 // this code is called using the normal C calling convention. When calling
8764 // directly from generated code the native RegExp code will not do a GC and
8765 // therefore the content of these registers are safe to use after the call.
8766 Register subject = r4;
8767 Register regexp_data = r5;
8768 Register last_match_info_elements = r6;
8769
8770 // Ensure that a RegExp stack is allocated.
8771 ExternalReference address_of_regexp_stack_memory_address =
8772 ExternalReference::address_of_regexp_stack_memory_address();
8773 ExternalReference address_of_regexp_stack_memory_size =
8774 ExternalReference::address_of_regexp_stack_memory_size();
8775 __ mov(r0, Operand(address_of_regexp_stack_memory_size));
8776 __ ldr(r0, MemOperand(r0, 0));
8777 __ tst(r0, Operand(r0));
8778 __ b(eq, &runtime);
8779
8780 // Check that the first argument is a JSRegExp object.
8781 __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
8782 ASSERT_EQ(0, kSmiTag);
8783 __ tst(r0, Operand(kSmiTagMask));
8784 __ b(eq, &runtime);
8785 __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
8786 __ b(ne, &runtime);
8787
8788 // Check that the RegExp has been compiled (data contains a fixed array).
8789 __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
8790 if (FLAG_debug_code) {
8791 __ tst(regexp_data, Operand(kSmiTagMask));
8792 __ Check(nz, "Unexpected type for RegExp data, FixedArray expected");
8793 __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
8794 __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
8795 }
8796
8797 // regexp_data: RegExp data (FixedArray)
8798 // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
8799 __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
8800 __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
8801 __ b(ne, &runtime);
8802
8803 // regexp_data: RegExp data (FixedArray)
8804 // Check that the number of captures fit in the static offsets vector buffer.
8805 __ ldr(r2,
8806 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
8807 // Calculate number of capture registers (number_of_captures + 1) * 2. This
8808 // uses the asumption that smis are 2 * their untagged value.
8809 ASSERT_EQ(0, kSmiTag);
8810 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
8811 __ add(r2, r2, Operand(2)); // r2 was a smi.
8812 // Check that the static offsets vector buffer is large enough.
8813 __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
8814 __ b(hi, &runtime);
8815
8816 // r2: Number of capture registers
8817 // regexp_data: RegExp data (FixedArray)
8818 // Check that the second argument is a string.
8819 __ ldr(subject, MemOperand(sp, kSubjectOffset));
8820 __ tst(subject, Operand(kSmiTagMask));
8821 __ b(eq, &runtime);
8822 Condition is_string = masm->IsObjectStringType(subject, r0);
8823 __ b(NegateCondition(is_string), &runtime);
8824 // Get the length of the string to r3.
8825 __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset));
8826
8827 // r2: Number of capture registers
ager@chromium.orgac091b72010-05-05 07:34:42 +00008828 // r3: Length of subject string as a smi
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008829 // subject: Subject string
8830 // regexp_data: RegExp data (FixedArray)
8831 // Check that the third argument is a positive smi less than the subject
8832 // string length. A negative value will be greater (unsigned comparison).
8833 __ ldr(r0, MemOperand(sp, kPreviousIndexOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00008834 __ tst(r0, Operand(kSmiTagMask));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008835 __ b(ne, &runtime);
ager@chromium.orgac091b72010-05-05 07:34:42 +00008836 __ cmp(r3, Operand(r0));
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +00008837 __ b(ls, &runtime);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008838
8839 // r2: Number of capture registers
8840 // subject: Subject string
8841 // regexp_data: RegExp data (FixedArray)
8842 // Check that the fourth object is a JSArray object.
8843 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
8844 __ tst(r0, Operand(kSmiTagMask));
8845 __ b(eq, &runtime);
8846 __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
8847 __ b(ne, &runtime);
8848 // Check that the JSArray is in fast case.
8849 __ ldr(last_match_info_elements,
8850 FieldMemOperand(r0, JSArray::kElementsOffset));
8851 __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00008852 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00008853 __ cmp(r0, ip);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008854 __ b(ne, &runtime);
8855 // Check that the last match info has space for the capture registers and the
8856 // additional information.
8857 __ ldr(r0,
8858 FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
8859 __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00008860 __ cmp(r2, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008861 __ b(gt, &runtime);
8862
8863 // subject: Subject string
8864 // regexp_data: RegExp data (FixedArray)
8865 // Check the representation and encoding of the subject string.
8866 Label seq_string;
8867 const int kStringRepresentationEncodingMask =
8868 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
8869 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
8870 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
8871 __ and_(r1, r0, Operand(kStringRepresentationEncodingMask));
8872 // First check for sequential string.
8873 ASSERT_EQ(0, kStringTag);
8874 ASSERT_EQ(0, kSeqStringTag);
8875 __ tst(r1, Operand(kIsNotStringMask | kStringRepresentationMask));
8876 __ b(eq, &seq_string);
8877
8878 // subject: Subject string
8879 // regexp_data: RegExp data (FixedArray)
8880 // Check for flat cons string.
8881 // A flat cons string is a cons string where the second part is the empty
8882 // string. In that case the subject string is just the first part of the cons
8883 // string. Also in this case the first part of the cons string is known to be
8884 // a sequential string or an external string.
8885 __ and_(r0, r0, Operand(kStringRepresentationMask));
8886 __ cmp(r0, Operand(kConsStringTag));
8887 __ b(ne, &runtime);
8888 __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
8889 __ LoadRoot(r1, Heap::kEmptyStringRootIndex);
8890 __ cmp(r0, r1);
8891 __ b(ne, &runtime);
8892 __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
8893 __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
8894 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
8895 ASSERT_EQ(0, kSeqStringTag);
8896 __ tst(r0, Operand(kStringRepresentationMask));
8897 __ b(nz, &runtime);
8898 __ and_(r1, r0, Operand(kStringRepresentationEncodingMask));
8899
8900 __ bind(&seq_string);
8901 // r1: suject string type & kStringRepresentationEncodingMask
8902 // subject: Subject string
8903 // regexp_data: RegExp data (FixedArray)
8904 // Check that the irregexp code has been generated for an ascii string. If
8905 // it has, the field contains a code object otherwise it contains the hole.
8906#ifdef DEBUG
8907 const int kSeqAsciiString = kStringTag | kSeqStringTag | kAsciiStringTag;
8908 const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
8909 CHECK_EQ(4, kSeqAsciiString);
8910 CHECK_EQ(0, kSeqTwoByteString);
8911#endif
8912 // Find the code object based on the assumptions above.
8913 __ mov(r3, Operand(r1, ASR, 2), SetCC);
8914 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
8915 __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
8916
8917 // Check that the irregexp code has been generated for the actual string
8918 // encoding. If it has, the field contains a code object otherwise it contains
8919 // the hole.
8920 __ CompareObjectType(r7, r0, r0, CODE_TYPE);
8921 __ b(ne, &runtime);
8922
8923 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
8924 // r7: code
8925 // subject: Subject string
8926 // regexp_data: RegExp data (FixedArray)
8927 // Load used arguments before starting to push arguments for call to native
8928 // RegExp code to avoid handling changing stack height.
8929 __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
8930 __ mov(r1, Operand(r1, ASR, kSmiTagSize));
8931
8932 // r1: previous index
8933 // r3: encoding of subject string (1 if ascii, 0 if two_byte);
8934 // r7: code
8935 // subject: Subject string
8936 // regexp_data: RegExp data (FixedArray)
8937 // All checks done. Now push arguments for native regexp code.
8938 __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2);
8939
8940 static const int kRegExpExecuteArguments = 7;
8941 __ push(lr);
8942 __ PrepareCallCFunction(kRegExpExecuteArguments, r0);
8943
8944 // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript.
8945 __ mov(r0, Operand(1));
8946 __ str(r0, MemOperand(sp, 2 * kPointerSize));
8947
8948 // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area.
8949 __ mov(r0, Operand(address_of_regexp_stack_memory_address));
8950 __ ldr(r0, MemOperand(r0, 0));
8951 __ mov(r2, Operand(address_of_regexp_stack_memory_size));
8952 __ ldr(r2, MemOperand(r2, 0));
8953 __ add(r0, r0, Operand(r2));
8954 __ str(r0, MemOperand(sp, 1 * kPointerSize));
8955
8956 // Argument 5 (sp[0]): static offsets vector buffer.
8957 __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector()));
8958 __ str(r0, MemOperand(sp, 0 * kPointerSize));
8959
8960 // For arguments 4 and 3 get string length, calculate start of string data and
8961 // calculate the shift of the index (0 for ASCII and 1 for two byte).
8962 __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00008963 __ mov(r0, Operand(r0, ASR, kSmiTagSize));
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00008964 ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize);
8965 __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
8966 __ eor(r3, r3, Operand(1));
8967 // Argument 4 (r3): End of string data
8968 // Argument 3 (r2): Start of string data
8969 __ add(r2, r9, Operand(r1, LSL, r3));
8970 __ add(r3, r9, Operand(r0, LSL, r3));
8971
8972 // Argument 2 (r1): Previous index.
8973 // Already there
8974
8975 // Argument 1 (r0): Subject string.
8976 __ mov(r0, subject);
8977
8978 // Locate the code entry and call it.
8979 __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
8980 __ CallCFunction(r7, kRegExpExecuteArguments);
8981 __ pop(lr);
8982
8983 // r0: result
8984 // subject: subject string (callee saved)
8985 // regexp_data: RegExp data (callee saved)
8986 // last_match_info_elements: Last match info elements (callee saved)
8987
8988 // Check the result.
8989 Label success;
8990 __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS));
8991 __ b(eq, &success);
8992 Label failure;
8993 __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
8994 __ b(eq, &failure);
8995 __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
8996 // If not exception it can only be retry. Handle that in the runtime system.
8997 __ b(ne, &runtime);
8998 // Result must now be exception. If there is no pending exception already a
8999 // stack overflow (on the backtrack stack) was detected in RegExp code but
9000 // haven't created the exception yet. Handle that in the runtime system.
9001 // TODO(592): Rerunning the RegExp to get the stack overflow exception.
9002 __ mov(r0, Operand(ExternalReference::the_hole_value_location()));
9003 __ ldr(r0, MemOperand(r0, 0));
9004 __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address)));
9005 __ ldr(r1, MemOperand(r1, 0));
9006 __ cmp(r0, r1);
9007 __ b(eq, &runtime);
9008 __ bind(&failure);
9009 // For failure and exception return null.
9010 __ mov(r0, Operand(Factory::null_value()));
9011 __ add(sp, sp, Operand(4 * kPointerSize));
9012 __ Ret();
9013
9014 // Process the result from the native regexp code.
9015 __ bind(&success);
9016 __ ldr(r1,
9017 FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
9018 // Calculate number of capture registers (number_of_captures + 1) * 2.
9019 ASSERT_EQ(0, kSmiTag);
9020 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9021 __ add(r1, r1, Operand(2)); // r1 was a smi.
9022
9023 // r1: number of capture registers
9024 // r4: subject string
9025 // Store the capture count.
9026 __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi.
9027 __ str(r2, FieldMemOperand(last_match_info_elements,
9028 RegExpImpl::kLastCaptureCountOffset));
9029 // Store last subject and last input.
9030 __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
9031 __ mov(r2, Operand(RegExpImpl::kLastSubjectOffset)); // Ditto.
9032 __ str(subject,
9033 FieldMemOperand(last_match_info_elements,
9034 RegExpImpl::kLastSubjectOffset));
9035 __ RecordWrite(r3, r2, r7);
9036 __ str(subject,
9037 FieldMemOperand(last_match_info_elements,
9038 RegExpImpl::kLastInputOffset));
9039 __ mov(r3, last_match_info_elements);
9040 __ mov(r2, Operand(RegExpImpl::kLastInputOffset));
9041 __ RecordWrite(r3, r2, r7);
9042
9043 // Get the static offsets vector filled by the native regexp code.
9044 ExternalReference address_of_static_offsets_vector =
9045 ExternalReference::address_of_static_offsets_vector();
9046 __ mov(r2, Operand(address_of_static_offsets_vector));
9047
9048 // r1: number of capture registers
9049 // r2: offsets vector
9050 Label next_capture, done;
9051 // Capture register counter starts from number of capture registers and
9052 // counts down until wraping after zero.
9053 __ add(r0,
9054 last_match_info_elements,
9055 Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
9056 __ bind(&next_capture);
9057 __ sub(r1, r1, Operand(1), SetCC);
9058 __ b(mi, &done);
9059 // Read the value from the static offsets vector buffer.
9060 __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
9061 // Store the smi value in the last match info.
9062 __ mov(r3, Operand(r3, LSL, kSmiTagSize));
9063 __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
9064 __ jmp(&next_capture);
9065 __ bind(&done);
9066
9067 // Return last match info.
9068 __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
9069 __ add(sp, sp, Operand(4 * kPointerSize));
9070 __ Ret();
9071
9072 // Do the runtime call to execute the regexp.
9073 __ bind(&runtime);
9074 __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
vegorov@chromium.orgdff694e2010-05-17 09:10:26 +00009075#endif // V8_INTERPRETED_REGEXP
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00009076}
9077
9078
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009079void CallFunctionStub::Generate(MacroAssembler* masm) {
9080 Label slow;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009081
9082 // If the receiver might be a value (string, number or boolean) check for this
9083 // and box it if it is.
9084 if (ReceiverMightBeValue()) {
9085 // Get the receiver from the stack.
9086 // function, receiver [, arguments]
9087 Label receiver_is_value, receiver_is_js_object;
9088 __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
9089
9090 // Check if receiver is a smi (which is a number value).
9091 __ BranchOnSmi(r1, &receiver_is_value);
9092
9093 // Check if the receiver is a valid JS object.
9094 __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
9095 __ b(ge, &receiver_is_js_object);
9096
9097 // Call the runtime to box the value.
9098 __ bind(&receiver_is_value);
9099 __ EnterInternalFrame();
9100 __ push(r1);
9101 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
9102 __ LeaveInternalFrame();
9103 __ str(r0, MemOperand(sp, argc_ * kPointerSize));
9104
9105 __ bind(&receiver_is_js_object);
9106 }
9107
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009108 // Get the function to call from the stack.
9109 // function, receiver [, arguments]
9110 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
9111
9112 // Check that the function is really a JavaScript function.
9113 // r1: pushed function (to be verified)
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009114 __ BranchOnSmi(r1, &slow);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009115 // Get the map of the function object.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00009116 __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009117 __ b(ne, &slow);
9118
9119 // Fast-case: Invoke the function now.
9120 // r1: pushed function
9121 ParameterCount actual(argc_);
9122 __ InvokeFunction(r1, actual, JUMP_FUNCTION);
9123
9124 // Slow-case: Non-function called.
9125 __ bind(&slow);
ager@chromium.org5c838252010-02-19 08:53:10 +00009126 // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
9127 // of the original receiver from the call site).
9128 __ str(r1, MemOperand(sp, argc_ * kPointerSize));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009129 __ mov(r0, Operand(argc_)); // Setup the number of arguments.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00009130 __ mov(r2, Operand(0));
9131 __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
9132 __ Jump(Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline)),
9133 RelocInfo::CODE_TARGET);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00009134}
9135
9136
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009137// Unfortunately you have to run without snapshots to see most of these
9138// names in the profile since most compare stubs end up in the snapshot.
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009139const char* CompareStub::GetName() {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009140 if (name_ != NULL) return name_;
9141 const int kMaxNameLength = 100;
9142 name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
9143 if (name_ == NULL) return "OOM";
9144
9145 const char* cc_name;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009146 switch (cc_) {
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009147 case lt: cc_name = "LT"; break;
9148 case gt: cc_name = "GT"; break;
9149 case le: cc_name = "LE"; break;
9150 case ge: cc_name = "GE"; break;
9151 case eq: cc_name = "EQ"; break;
9152 case ne: cc_name = "NE"; break;
9153 default: cc_name = "UnknownCondition"; break;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009154 }
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009155
9156 const char* strict_name = "";
9157 if (strict_ && (cc_ == eq || cc_ == ne)) {
9158 strict_name = "_STRICT";
9159 }
9160
9161 const char* never_nan_nan_name = "";
9162 if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
9163 never_nan_nan_name = "_NO_NAN";
9164 }
9165
9166 const char* include_number_compare_name = "";
9167 if (!include_number_compare_) {
9168 include_number_compare_name = "_NO_NUMBER";
9169 }
9170
9171 OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
9172 "CompareStub_%s%s%s%s",
9173 cc_name,
9174 strict_name,
9175 never_nan_nan_name,
9176 include_number_compare_name);
9177 return name_;
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00009178}
9179
9180
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009181int CompareStub::MinorKey() {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009182 // Encode the three parameters in a unique 16 bit value. To avoid duplicate
9183 // stubs the never NaN NaN condition is only taken into account if the
9184 // condition is equals.
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009185 ASSERT((static_cast<unsigned>(cc_) >> 28) < (1 << 13));
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00009186 return ConditionField::encode(static_cast<unsigned>(cc_) >> 28)
9187 | StrictField::encode(strict_)
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00009188 | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
9189 | IncludeNumberCompareField::encode(include_number_compare_);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00009190}
9191
9192
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009193// StringCharCodeAtGenerator
9194
9195void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
9196 Label flat_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009197 Label ascii_string;
9198 Label got_char_code;
9199
9200 // If the receiver is a smi trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009201 __ BranchOnSmi(object_, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009202
9203 // Fetch the instance type of the receiver into result register.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009204 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9205 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009206 // If the receiver is not a string trigger the non-string case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009207 __ tst(result_, Operand(kIsNotStringMask));
9208 __ b(ne, receiver_not_string_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009209
9210 // If the index is non-smi trigger the non-smi case.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009211 __ BranchOnNotSmi(index_, &index_not_smi_);
9212
9213 // Put smi-tagged index into scratch register.
9214 __ mov(scratch_, index_);
9215 __ bind(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009216
9217 // Check for index out of range.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009218 __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
9219 __ cmp(ip, Operand(scratch_));
9220 __ b(ls, index_out_of_range_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009221
9222 // We need special handling for non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009223 ASSERT(kSeqStringTag == 0);
9224 __ tst(result_, Operand(kStringRepresentationMask));
9225 __ b(eq, &flat_string);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009226
9227 // Handle non-flat strings.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009228 __ tst(result_, Operand(kIsConsStringMask));
9229 __ b(eq, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009230
9231 // ConsString.
9232 // Check whether the right hand side is the empty string (i.e. if
9233 // this is really a flat string in a cons string). If that is not
9234 // the case we would rather go to the runtime system now to flatten
9235 // the string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009236 __ ldr(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
9237 __ LoadRoot(ip, Heap::kEmptyStringRootIndex);
9238 __ cmp(result_, Operand(ip));
9239 __ b(ne, &call_runtime_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009240 // Get the first of the two strings and load its instance type.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009241 __ ldr(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
9242 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9243 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
9244 // If the first cons component is also non-flat, then go to runtime.
9245 ASSERT(kSeqStringTag == 0);
9246 __ tst(result_, Operand(kStringRepresentationMask));
9247 __ b(nz, &call_runtime_);
9248
9249 // Check for 1-byte or 2-byte string.
9250 __ bind(&flat_string);
9251 ASSERT(kAsciiStringTag != 0);
9252 __ tst(result_, Operand(kStringEncodingMask));
9253 __ b(nz, &ascii_string);
9254
9255 // 2-byte string.
9256 // Load the 2-byte character code into the result register. We can
9257 // add without shifting since the smi tag size is the log2 of the
9258 // number of bytes in a two-byte character.
9259 ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
9260 __ add(scratch_, object_, Operand(scratch_));
9261 __ ldrh(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
9262 __ jmp(&got_char_code);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009263
9264 // ASCII string.
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009265 // Load the byte into the result register.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009266 __ bind(&ascii_string);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009267 __ add(scratch_, object_, Operand(scratch_, LSR, kSmiTagSize));
9268 __ ldrb(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009269
9270 __ bind(&got_char_code);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009271 __ mov(result_, Operand(result_, LSL, kSmiTagSize));
9272 __ bind(&exit_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009273}
9274
9275
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009276void StringCharCodeAtGenerator::GenerateSlow(
9277 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
9278 __ Abort("Unexpected fallthrough to CharCodeAt slow case");
ager@chromium.orgac091b72010-05-05 07:34:42 +00009279
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009280 // Index is not a smi.
9281 __ bind(&index_not_smi_);
9282 // If index is a heap number, try converting it to an integer.
9283 __ CheckMap(index_, scratch_,
9284 Factory::heap_number_map(), index_not_number_, true);
9285 call_helper.BeforeCall(masm);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00009286 __ Push(object_, index_);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009287 __ push(index_); // Consumed by runtime conversion function.
9288 if (index_flags_ == STRING_INDEX_IS_NUMBER) {
9289 __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
9290 } else {
9291 ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
9292 // NumberToSmi discards numbers that are not exact integers.
9293 __ CallRuntime(Runtime::kNumberToSmi, 1);
9294 }
9295 if (!scratch_.is(r0)) {
9296 // Save the conversion result before the pop instructions below
9297 // have a chance to overwrite it.
9298 __ mov(scratch_, r0);
9299 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009300 __ pop(index_);
9301 __ pop(object_);
sgjesse@chromium.org82dbbab2010-06-02 08:57:44 +00009302 // Reload the instance type.
9303 __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
9304 __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009305 call_helper.AfterCall(masm);
9306 // If index is still not a smi, it must be out of range.
9307 __ BranchOnNotSmi(scratch_, index_out_of_range_);
9308 // Otherwise, return to the fast path.
9309 __ jmp(&got_smi_index_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009310
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009311 // Call runtime. We get here when the receiver is a string and the
9312 // index is a number, but the code of getting the actual character
9313 // is too complex (e.g., when the string needs to be flattened).
9314 __ bind(&call_runtime_);
9315 call_helper.BeforeCall(masm);
9316 __ Push(object_, index_);
9317 __ CallRuntime(Runtime::kStringCharCodeAt, 2);
9318 if (!result_.is(r0)) {
9319 __ mov(result_, r0);
9320 }
9321 call_helper.AfterCall(masm);
9322 __ jmp(&exit_);
9323
9324 __ Abort("Unexpected fallthrough from CharCodeAt slow case");
9325}
9326
9327
9328// -------------------------------------------------------------------------
9329// StringCharFromCodeGenerator
9330
9331void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00009332 // Fast case of Heap::LookupSingleCharacterStringFromCode.
9333 ASSERT(kSmiTag == 0);
9334 ASSERT(kSmiShiftSize == 0);
9335 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009336 __ tst(code_,
9337 Operand(kSmiTagMask |
9338 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
9339 __ b(nz, &slow_case_);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009340
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009341 __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
9342 // At this point code register contains smi tagged ascii char code.
ager@chromium.orgac091b72010-05-05 07:34:42 +00009343 ASSERT(kSmiTag == 0);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009344 __ add(result_, result_, Operand(code_, LSL, kPointerSizeLog2 - kSmiTagSize));
9345 __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
9346 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
9347 __ cmp(result_, Operand(ip));
9348 __ b(eq, &slow_case_);
9349 __ bind(&exit_);
9350}
ager@chromium.orgac091b72010-05-05 07:34:42 +00009351
ager@chromium.orgac091b72010-05-05 07:34:42 +00009352
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009353void StringCharFromCodeGenerator::GenerateSlow(
9354 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
9355 __ Abort("Unexpected fallthrough to CharFromCode slow case");
9356
9357 __ bind(&slow_case_);
9358 call_helper.BeforeCall(masm);
9359 __ push(code_);
9360 __ CallRuntime(Runtime::kCharFromCode, 1);
9361 if (!result_.is(r0)) {
9362 __ mov(result_, r0);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009363 }
ricow@chromium.org30ce4112010-05-31 10:38:25 +00009364 call_helper.AfterCall(masm);
9365 __ jmp(&exit_);
9366
9367 __ Abort("Unexpected fallthrough from CharFromCode slow case");
9368}
9369
9370
9371// -------------------------------------------------------------------------
9372// StringCharAtGenerator
9373
9374void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
9375 char_code_at_generator_.GenerateFast(masm);
9376 char_from_code_generator_.GenerateFast(masm);
9377}
9378
9379
9380void StringCharAtGenerator::GenerateSlow(
9381 MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
9382 char_code_at_generator_.GenerateSlow(masm, call_helper);
9383 char_from_code_generator_.GenerateSlow(masm, call_helper);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009384}
9385
9386
9387void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
9388 Register dest,
9389 Register src,
9390 Register count,
9391 Register scratch,
9392 bool ascii) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009393 Label loop;
9394 Label done;
9395 // This loop just copies one character at a time, as it is only used for very
9396 // short strings.
9397 if (!ascii) {
9398 __ add(count, count, Operand(count), SetCC);
9399 } else {
9400 __ cmp(count, Operand(0));
9401 }
9402 __ b(eq, &done);
9403
9404 __ bind(&loop);
9405 __ ldrb(scratch, MemOperand(src, 1, PostIndex));
9406 // Perform sub between load and dependent store to get the load time to
9407 // complete.
9408 __ sub(count, count, Operand(1), SetCC);
9409 __ strb(scratch, MemOperand(dest, 1, PostIndex));
9410 // last iteration.
9411 __ b(gt, &loop);
9412
9413 __ bind(&done);
9414}
9415
9416
9417enum CopyCharactersFlags {
9418 COPY_ASCII = 1,
9419 DEST_ALWAYS_ALIGNED = 2
9420};
9421
9422
ager@chromium.orgac091b72010-05-05 07:34:42 +00009423void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
9424 Register dest,
9425 Register src,
9426 Register count,
9427 Register scratch1,
9428 Register scratch2,
9429 Register scratch3,
9430 Register scratch4,
9431 Register scratch5,
9432 int flags) {
ager@chromium.org5c838252010-02-19 08:53:10 +00009433 bool ascii = (flags & COPY_ASCII) != 0;
9434 bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
9435
9436 if (dest_always_aligned && FLAG_debug_code) {
9437 // Check that destination is actually word aligned if the flag says
9438 // that it is.
9439 __ tst(dest, Operand(kPointerAlignmentMask));
9440 __ Check(eq, "Destination of copy not aligned.");
9441 }
9442
9443 const int kReadAlignment = 4;
9444 const int kReadAlignmentMask = kReadAlignment - 1;
9445 // Ensure that reading an entire aligned word containing the last character
9446 // of a string will not read outside the allocated area (because we pad up
9447 // to kObjectAlignment).
9448 ASSERT(kObjectAlignment >= kReadAlignment);
9449 // Assumes word reads and writes are little endian.
9450 // Nothing to do for zero characters.
9451 Label done;
9452 if (!ascii) {
9453 __ add(count, count, Operand(count), SetCC);
9454 } else {
9455 __ cmp(count, Operand(0));
9456 }
9457 __ b(eq, &done);
9458
9459 // Assume that you cannot read (or write) unaligned.
9460 Label byte_loop;
9461 // Must copy at least eight bytes, otherwise just do it one byte at a time.
9462 __ cmp(count, Operand(8));
9463 __ add(count, dest, Operand(count));
9464 Register limit = count; // Read until src equals this.
9465 __ b(lt, &byte_loop);
9466
9467 if (!dest_always_aligned) {
9468 // Align dest by byte copying. Copies between zero and three bytes.
9469 __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
9470 Label dest_aligned;
9471 __ b(eq, &dest_aligned);
9472 __ cmp(scratch4, Operand(2));
9473 __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
9474 __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
9475 __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
9476 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9477 __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
9478 __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
9479 __ bind(&dest_aligned);
9480 }
9481
9482 Label simple_loop;
9483
9484 __ sub(scratch4, dest, Operand(src));
9485 __ and_(scratch4, scratch4, Operand(0x03), SetCC);
9486 __ b(eq, &simple_loop);
9487 // Shift register is number of bits in a source word that
9488 // must be combined with bits in the next source word in order
9489 // to create a destination word.
9490
9491 // Complex loop for src/dst that are not aligned the same way.
9492 {
9493 Label loop;
9494 __ mov(scratch4, Operand(scratch4, LSL, 3));
9495 Register left_shift = scratch4;
9496 __ and_(src, src, Operand(~3)); // Round down to load previous word.
9497 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
9498 // Store the "shift" most significant bits of scratch in the least
9499 // signficant bits (i.e., shift down by (32-shift)).
9500 __ rsb(scratch2, left_shift, Operand(32));
9501 Register right_shift = scratch2;
9502 __ mov(scratch1, Operand(scratch1, LSR, right_shift));
9503
9504 __ bind(&loop);
9505 __ ldr(scratch3, MemOperand(src, 4, PostIndex));
9506 __ sub(scratch5, limit, Operand(dest));
9507 __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
9508 __ str(scratch1, MemOperand(dest, 4, PostIndex));
9509 __ mov(scratch1, Operand(scratch3, LSR, right_shift));
9510 // Loop if four or more bytes left to copy.
9511 // Compare to eight, because we did the subtract before increasing dst.
9512 __ sub(scratch5, scratch5, Operand(8), SetCC);
9513 __ b(ge, &loop);
9514 }
9515 // There is now between zero and three bytes left to copy (negative that
9516 // number is in scratch5), and between one and three bytes already read into
9517 // scratch1 (eight times that number in scratch4). We may have read past
9518 // the end of the string, but because objects are aligned, we have not read
9519 // past the end of the object.
9520 // Find the minimum of remaining characters to move and preloaded characters
9521 // and write those as bytes.
9522 __ add(scratch5, scratch5, Operand(4), SetCC);
9523 __ b(eq, &done);
9524 __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
9525 // Move minimum of bytes read and bytes left to copy to scratch4.
9526 __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
9527 // Between one and three (value in scratch5) characters already read into
9528 // scratch ready to write.
9529 __ cmp(scratch5, Operand(2));
9530 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9531 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
9532 __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
9533 __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
9534 __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
9535 // Copy any remaining bytes.
9536 __ b(&byte_loop);
9537
9538 // Simple loop.
9539 // Copy words from src to dst, until less than four bytes left.
9540 // Both src and dest are word aligned.
9541 __ bind(&simple_loop);
9542 {
9543 Label loop;
9544 __ bind(&loop);
9545 __ ldr(scratch1, MemOperand(src, 4, PostIndex));
9546 __ sub(scratch3, limit, Operand(dest));
9547 __ str(scratch1, MemOperand(dest, 4, PostIndex));
9548 // Compare to 8, not 4, because we do the substraction before increasing
9549 // dest.
9550 __ cmp(scratch3, Operand(8));
9551 __ b(ge, &loop);
9552 }
9553
9554 // Copy bytes from src to dst until dst hits limit.
9555 __ bind(&byte_loop);
9556 __ cmp(dest, Operand(limit));
9557 __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
9558 __ b(ge, &done);
9559 __ strb(scratch1, MemOperand(dest, 1, PostIndex));
9560 __ b(&byte_loop);
9561
9562 __ bind(&done);
9563}
9564
9565
ager@chromium.orgac091b72010-05-05 07:34:42 +00009566void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
9567 Register c1,
9568 Register c2,
9569 Register scratch1,
9570 Register scratch2,
9571 Register scratch3,
9572 Register scratch4,
9573 Register scratch5,
9574 Label* not_found) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009575 // Register scratch3 is the general scratch register in this function.
9576 Register scratch = scratch3;
9577
9578 // Make sure that both characters are not digits as such strings has a
9579 // different hash algorithm. Don't try to look for these in the symbol table.
9580 Label not_array_index;
9581 __ sub(scratch, c1, Operand(static_cast<int>('0')));
9582 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
9583 __ b(hi, &not_array_index);
9584 __ sub(scratch, c2, Operand(static_cast<int>('0')));
9585 __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
9586
9587 // If check failed combine both characters into single halfword.
9588 // This is required by the contract of the method: code at the
9589 // not_found branch expects this combination in c1 register
9590 __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
9591 __ b(ls, not_found);
9592
9593 __ bind(&not_array_index);
9594 // Calculate the two character string hash.
9595 Register hash = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009596 StringHelper::GenerateHashInit(masm, hash, c1);
9597 StringHelper::GenerateHashAddCharacter(masm, hash, c2);
9598 StringHelper::GenerateHashGetHash(masm, hash);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009599
9600 // Collect the two characters in a register.
9601 Register chars = c1;
9602 __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
9603
9604 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
9605 // hash: hash of two character string.
9606
9607 // Load symbol table
9608 // Load address of first element of the symbol table.
9609 Register symbol_table = c2;
9610 __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
9611
9612 // Load undefined value
9613 Register undefined = scratch4;
9614 __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
9615
9616 // Calculate capacity mask from the symbol table capacity.
9617 Register mask = scratch2;
9618 __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
9619 __ mov(mask, Operand(mask, ASR, 1));
9620 __ sub(mask, mask, Operand(1));
9621
9622 // Calculate untagged address of the first element of the symbol table.
9623 Register first_symbol_table_element = symbol_table;
9624 __ add(first_symbol_table_element, symbol_table,
9625 Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
9626
9627 // Registers
9628 // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
9629 // hash: hash of two character string
9630 // mask: capacity mask
9631 // first_symbol_table_element: address of the first element of
9632 // the symbol table
9633 // scratch: -
9634
9635 // Perform a number of probes in the symbol table.
9636 static const int kProbes = 4;
9637 Label found_in_symbol_table;
9638 Label next_probe[kProbes];
9639 for (int i = 0; i < kProbes; i++) {
9640 Register candidate = scratch5; // Scratch register contains candidate.
9641
9642 // Calculate entry in symbol table.
9643 if (i > 0) {
9644 __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
9645 } else {
9646 __ mov(candidate, hash);
9647 }
9648
9649 __ and_(candidate, candidate, Operand(mask));
9650
9651 // Load the entry from the symble table.
9652 ASSERT_EQ(1, SymbolTable::kEntrySize);
9653 __ ldr(candidate,
9654 MemOperand(first_symbol_table_element,
9655 candidate,
9656 LSL,
9657 kPointerSizeLog2));
9658
9659 // If entry is undefined no string with this hash can be found.
9660 __ cmp(candidate, undefined);
9661 __ b(eq, not_found);
9662
9663 // If length is not 2 the string is not a candidate.
9664 __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009665 __ cmp(scratch, Operand(Smi::FromInt(2)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009666 __ b(ne, &next_probe[i]);
9667
9668 // Check that the candidate is a non-external ascii string.
9669 __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
9670 __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
9671 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
9672 &next_probe[i]);
9673
9674 // Check if the two characters match.
9675 // Assumes that word load is little endian.
9676 __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
9677 __ cmp(chars, scratch);
9678 __ b(eq, &found_in_symbol_table);
9679 __ bind(&next_probe[i]);
9680 }
9681
9682 // No matching 2 character string found by probing.
9683 __ jmp(not_found);
9684
9685 // Scratch register contains result when we fall through to here.
9686 Register result = scratch;
9687 __ bind(&found_in_symbol_table);
ager@chromium.org357bf652010-04-12 11:30:10 +00009688 __ Move(r0, result);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009689}
9690
9691
ager@chromium.orgac091b72010-05-05 07:34:42 +00009692void StringHelper::GenerateHashInit(MacroAssembler* masm,
9693 Register hash,
9694 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009695 // hash = character + (character << 10);
9696 __ add(hash, character, Operand(character, LSL, 10));
9697 // hash ^= hash >> 6;
9698 __ eor(hash, hash, Operand(hash, ASR, 6));
9699}
9700
9701
ager@chromium.orgac091b72010-05-05 07:34:42 +00009702void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
9703 Register hash,
9704 Register character) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009705 // hash += character;
9706 __ add(hash, hash, Operand(character));
9707 // hash += hash << 10;
9708 __ add(hash, hash, Operand(hash, LSL, 10));
9709 // hash ^= hash >> 6;
9710 __ eor(hash, hash, Operand(hash, ASR, 6));
9711}
9712
9713
ager@chromium.orgac091b72010-05-05 07:34:42 +00009714void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
9715 Register hash) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009716 // hash += hash << 3;
9717 __ add(hash, hash, Operand(hash, LSL, 3));
9718 // hash ^= hash >> 11;
9719 __ eor(hash, hash, Operand(hash, ASR, 11));
9720 // hash += hash << 15;
9721 __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
9722
9723 // if (hash == 0) hash = 27;
9724 __ mov(hash, Operand(27), LeaveCC, nz);
9725}
9726
9727
ager@chromium.org5c838252010-02-19 08:53:10 +00009728void SubStringStub::Generate(MacroAssembler* masm) {
9729 Label runtime;
9730
9731 // Stack frame on entry.
9732 // lr: return address
9733 // sp[0]: to
9734 // sp[4]: from
9735 // sp[8]: string
9736
9737 // This stub is called from the native-call %_SubString(...), so
9738 // nothing can be assumed about the arguments. It is tested that:
9739 // "string" is a sequential string,
9740 // both "from" and "to" are smis, and
9741 // 0 <= from <= to <= string.length.
9742 // If any of these assumptions fail, we call the runtime system.
9743
9744 static const int kToOffset = 0 * kPointerSize;
9745 static const int kFromOffset = 1 * kPointerSize;
9746 static const int kStringOffset = 2 * kPointerSize;
9747
9748
9749 // Check bounds and smi-ness.
9750 __ ldr(r7, MemOperand(sp, kToOffset));
9751 __ ldr(r6, MemOperand(sp, kFromOffset));
9752 ASSERT_EQ(0, kSmiTag);
9753 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
9754 // I.e., arithmetic shift right by one un-smi-tags.
9755 __ mov(r2, Operand(r7, ASR, 1), SetCC);
9756 __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
9757 // If either r2 or r6 had the smi tag bit set, then carry is set now.
9758 __ b(cs, &runtime); // Either "from" or "to" is not a smi.
9759 __ b(mi, &runtime); // From is negative.
9760
9761 __ sub(r2, r2, Operand(r3), SetCC);
9762 __ b(mi, &runtime); // Fail if from > to.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009763 // Special handling of sub-strings of length 1 and 2. One character strings
9764 // are handled in the runtime system (looked up in the single character
9765 // cache). Two character strings are looked for in the symbol cache.
ager@chromium.org5c838252010-02-19 08:53:10 +00009766 __ cmp(r2, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009767 __ b(lt, &runtime);
ager@chromium.org5c838252010-02-19 08:53:10 +00009768
9769 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009770 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009771 // r6: from (smi)
9772 // r7: to (smi)
9773
9774 // Make sure first argument is a sequential (or flat) string.
9775 __ ldr(r5, MemOperand(sp, kStringOffset));
9776 ASSERT_EQ(0, kSmiTag);
9777 __ tst(r5, Operand(kSmiTagMask));
9778 __ b(eq, &runtime);
9779 Condition is_string = masm->IsObjectStringType(r5, r1);
9780 __ b(NegateCondition(is_string), &runtime);
9781
9782 // r1: instance type
9783 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009784 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009785 // r5: string
9786 // r6: from (smi)
9787 // r7: to (smi)
9788 Label seq_string;
9789 __ and_(r4, r1, Operand(kStringRepresentationMask));
9790 ASSERT(kSeqStringTag < kConsStringTag);
9791 ASSERT(kExternalStringTag > kConsStringTag);
9792 __ cmp(r4, Operand(kConsStringTag));
9793 __ b(gt, &runtime); // External strings go to runtime.
9794 __ b(lt, &seq_string); // Sequential strings are handled directly.
9795
9796 // Cons string. Try to recurse (once) on the first substring.
9797 // (This adds a little more generality than necessary to handle flattened
9798 // cons strings, but not much).
9799 __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
9800 __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
9801 __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
9802 __ tst(r1, Operand(kStringRepresentationMask));
9803 ASSERT_EQ(0, kSeqStringTag);
9804 __ b(ne, &runtime); // Cons and External strings go to runtime.
9805
9806 // Definitly a sequential string.
9807 __ bind(&seq_string);
9808
9809 // r1: instance type.
9810 // r2: length
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009811 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009812 // r5: string
9813 // r6: from (smi)
9814 // r7: to (smi)
9815 __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +00009816 __ cmp(r4, Operand(r7));
ager@chromium.org5c838252010-02-19 08:53:10 +00009817 __ b(lt, &runtime); // Fail if to > length.
9818
9819 // r1: instance type.
9820 // r2: result string length.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009821 // r3: from index (untaged smi)
ager@chromium.org5c838252010-02-19 08:53:10 +00009822 // r5: string.
9823 // r6: from offset (smi)
9824 // Check for flat ascii string.
9825 Label non_ascii_flat;
9826 __ tst(r1, Operand(kStringEncodingMask));
9827 ASSERT_EQ(0, kTwoByteStringTag);
9828 __ b(eq, &non_ascii_flat);
9829
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009830 Label result_longer_than_two;
9831 __ cmp(r2, Operand(2));
9832 __ b(gt, &result_longer_than_two);
9833
9834 // Sub string of length 2 requested.
9835 // Get the two characters forming the sub string.
9836 __ add(r5, r5, Operand(r3));
9837 __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
9838 __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
9839
9840 // Try to lookup two character string in symbol table.
9841 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009842 StringHelper::GenerateTwoCharacterSymbolTableProbe(
9843 masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009844 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9845 __ add(sp, sp, Operand(3 * kPointerSize));
9846 __ Ret();
9847
9848 // r2: result string length.
9849 // r3: two characters combined into halfword in little endian byte order.
9850 __ bind(&make_two_character_string);
9851 __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
9852 __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
9853 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9854 __ add(sp, sp, Operand(3 * kPointerSize));
9855 __ Ret();
9856
9857 __ bind(&result_longer_than_two);
9858
ager@chromium.org5c838252010-02-19 08:53:10 +00009859 // Allocate the result.
9860 __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
9861
9862 // r0: result string.
9863 // r2: result string length.
9864 // r5: string.
9865 // r6: from offset (smi)
9866 // Locate first character of result.
9867 __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9868 // Locate 'from' character of string.
9869 __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9870 __ add(r5, r5, Operand(r6, ASR, 1));
9871
9872 // r0: result string.
9873 // r1: first character of result string.
9874 // r2: result string length.
9875 // r5: first character of sub string to copy.
9876 ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009877 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
9878 COPY_ASCII | DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +00009879 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9880 __ add(sp, sp, Operand(3 * kPointerSize));
9881 __ Ret();
9882
9883 __ bind(&non_ascii_flat);
9884 // r2: result string length.
9885 // r5: string.
9886 // r6: from offset (smi)
9887 // Check for flat two byte string.
9888
9889 // Allocate the result.
9890 __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
9891
9892 // r0: result string.
9893 // r2: result string length.
9894 // r5: string.
9895 // Locate first character of result.
9896 __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9897 // Locate 'from' character of string.
9898 __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
9899 // As "from" is a smi it is 2 times the value which matches the size of a two
9900 // byte character.
9901 __ add(r5, r5, Operand(r6));
9902
9903 // r0: result string.
9904 // r1: first character of result.
9905 // r2: result length.
9906 // r5: first character of string to copy.
9907 ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
ager@chromium.orgac091b72010-05-05 07:34:42 +00009908 StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
9909 DEST_ALWAYS_ALIGNED);
ager@chromium.org5c838252010-02-19 08:53:10 +00009910 __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
9911 __ add(sp, sp, Operand(3 * kPointerSize));
9912 __ Ret();
9913
9914 // Just jump to runtime to create the sub string.
9915 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00009916 __ TailCallRuntime(Runtime::kSubString, 3, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +00009917}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009918
9919
9920void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
9921 Register left,
9922 Register right,
9923 Register scratch1,
9924 Register scratch2,
9925 Register scratch3,
9926 Register scratch4) {
9927 Label compare_lengths;
9928 // Find minimum length and length difference.
9929 __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
9930 __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
9931 __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
9932 Register length_delta = scratch3;
9933 __ mov(scratch1, scratch2, LeaveCC, gt);
9934 Register min_length = scratch1;
ager@chromium.orgac091b72010-05-05 07:34:42 +00009935 ASSERT(kSmiTag == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009936 __ tst(min_length, Operand(min_length));
9937 __ b(eq, &compare_lengths);
9938
ager@chromium.orgac091b72010-05-05 07:34:42 +00009939 // Untag smi.
9940 __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
9941
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009942 // Setup registers so that we only need to increment one register
9943 // in the loop.
9944 __ add(scratch2, min_length,
9945 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
9946 __ add(left, left, Operand(scratch2));
9947 __ add(right, right, Operand(scratch2));
9948 // Registers left and right points to the min_length character of strings.
9949 __ rsb(min_length, min_length, Operand(-1));
9950 Register index = min_length;
9951 // Index starts at -min_length.
9952
9953 {
9954 // Compare loop.
9955 Label loop;
9956 __ bind(&loop);
9957 // Compare characters.
9958 __ add(index, index, Operand(1), SetCC);
9959 __ ldrb(scratch2, MemOperand(left, index), ne);
9960 __ ldrb(scratch4, MemOperand(right, index), ne);
9961 // Skip to compare lengths with eq condition true.
9962 __ b(eq, &compare_lengths);
9963 __ cmp(scratch2, scratch4);
9964 __ b(eq, &loop);
9965 // Fallthrough with eq condition false.
9966 }
9967 // Compare lengths - strings up to min-length are equal.
9968 __ bind(&compare_lengths);
9969 ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
9970 // Use zero length_delta as result.
9971 __ mov(r0, Operand(length_delta), SetCC, eq);
9972 // Fall through to here if characters compare not-equal.
9973 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
9974 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
9975 __ Ret();
9976}
9977
9978
9979void StringCompareStub::Generate(MacroAssembler* masm) {
9980 Label runtime;
9981
9982 // Stack frame on entry.
ager@chromium.org5c838252010-02-19 08:53:10 +00009983 // sp[0]: right string
9984 // sp[4]: left string
9985 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
9986 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00009987
9988 Label not_same;
9989 __ cmp(r0, r1);
9990 __ b(ne, &not_same);
9991 ASSERT_EQ(0, EQUAL);
9992 ASSERT_EQ(0, kSmiTag);
9993 __ mov(r0, Operand(Smi::FromInt(EQUAL)));
9994 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2);
9995 __ add(sp, sp, Operand(2 * kPointerSize));
9996 __ Ret();
9997
9998 __ bind(&not_same);
9999
10000 // Check that both objects are sequential ascii strings.
10001 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
10002
10003 // Compare flat ascii strings natively. Remove arguments from stack first.
10004 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
10005 __ add(sp, sp, Operand(2 * kPointerSize));
10006 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
10007
10008 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
10009 // tagged as a small integer.
10010 __ bind(&runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010011 __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000010012}
10013
10014
ager@chromium.org5c838252010-02-19 08:53:10 +000010015void StringAddStub::Generate(MacroAssembler* masm) {
10016 Label string_add_runtime;
10017 // Stack on entry:
10018 // sp[0]: second argument.
10019 // sp[4]: first argument.
10020
10021 // Load the two arguments.
10022 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
10023 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
10024
10025 // Make sure that both arguments are strings if not known in advance.
10026 if (string_check_) {
10027 ASSERT_EQ(0, kSmiTag);
10028 __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
10029 // Load instance types.
10030 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10031 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10032 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10033 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10034 ASSERT_EQ(0, kStringTag);
10035 // If either is not a string, go to runtime.
10036 __ tst(r4, Operand(kIsNotStringMask));
10037 __ tst(r5, Operand(kIsNotStringMask), eq);
10038 __ b(ne, &string_add_runtime);
10039 }
10040
10041 // Both arguments are strings.
10042 // r0: first string
10043 // r1: second string
10044 // r4: first string instance type (if string_check_)
10045 // r5: second string instance type (if string_check_)
10046 {
10047 Label strings_not_empty;
10048 // Check if either of the strings are empty. In that case return the other.
10049 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
10050 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
ager@chromium.orgac091b72010-05-05 07:34:42 +000010051 ASSERT(kSmiTag == 0);
10052 __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
ager@chromium.org5c838252010-02-19 08:53:10 +000010053 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010054 ASSERT(kSmiTag == 0);
10055 // Else test if second string is empty.
10056 __ cmp(r3, Operand(Smi::FromInt(0)), ne);
ager@chromium.org5c838252010-02-19 08:53:10 +000010057 __ b(ne, &strings_not_empty); // If either string was empty, return r0.
10058
10059 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10060 __ add(sp, sp, Operand(2 * kPointerSize));
10061 __ Ret();
10062
10063 __ bind(&strings_not_empty);
10064 }
10065
ager@chromium.orgac091b72010-05-05 07:34:42 +000010066 __ mov(r2, Operand(r2, ASR, kSmiTagSize));
10067 __ mov(r3, Operand(r3, ASR, kSmiTagSize));
ager@chromium.org5c838252010-02-19 08:53:10 +000010068 // Both strings are non-empty.
10069 // r0: first string
10070 // r1: second string
10071 // r2: length of first string
10072 // r3: length of second string
10073 // r4: first string instance type (if string_check_)
10074 // r5: second string instance type (if string_check_)
10075 // Look at the length of the result of adding the two strings.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010076 Label string_add_flat_result, longer_than_two;
ager@chromium.org5c838252010-02-19 08:53:10 +000010077 // Adding two lengths can't overflow.
10078 ASSERT(String::kMaxLength * 2 > String::kMaxLength);
10079 __ add(r6, r2, Operand(r3));
10080 // Use the runtime system when adding two one character strings, as it
10081 // contains optimizations for this specific case using the symbol table.
10082 __ cmp(r6, Operand(2));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010083 __ b(ne, &longer_than_two);
10084
10085 // Check that both strings are non-external ascii strings.
10086 if (!string_check_) {
10087 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10088 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10089 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10090 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10091 }
10092 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
10093 &string_add_runtime);
10094
10095 // Get the two characters forming the sub string.
10096 __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10097 __ ldrb(r3, FieldMemOperand(r1, SeqAsciiString::kHeaderSize));
10098
10099 // Try to lookup two character string in symbol table. If it is not found
10100 // just allocate a new one.
10101 Label make_two_character_string;
ager@chromium.orgac091b72010-05-05 07:34:42 +000010102 StringHelper::GenerateTwoCharacterSymbolTableProbe(
10103 masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010104 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10105 __ add(sp, sp, Operand(2 * kPointerSize));
10106 __ Ret();
10107
10108 __ bind(&make_two_character_string);
10109 // Resulting string has length 2 and first chars of two strings
10110 // are combined into single halfword in r2 register.
10111 // So we can fill resulting string without two loops by a single
10112 // halfword store instruction (which assumes that processor is
10113 // in a little endian mode)
10114 __ mov(r6, Operand(2));
10115 __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
10116 __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
10117 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10118 __ add(sp, sp, Operand(2 * kPointerSize));
10119 __ Ret();
10120
10121 __ bind(&longer_than_two);
ager@chromium.org5c838252010-02-19 08:53:10 +000010122 // Check if resulting string will be flat.
10123 __ cmp(r6, Operand(String::kMinNonFlatLength));
10124 __ b(lt, &string_add_flat_result);
10125 // Handle exceptionally long strings in the runtime system.
10126 ASSERT((String::kMaxLength & 0x80000000) == 0);
10127 ASSERT(IsPowerOf2(String::kMaxLength + 1));
10128 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
10129 __ cmp(r6, Operand(String::kMaxLength + 1));
10130 __ b(hs, &string_add_runtime);
10131
10132 // If result is not supposed to be flat, allocate a cons string object.
10133 // If both strings are ascii the result is an ascii cons string.
10134 if (!string_check_) {
10135 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10136 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10137 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10138 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10139 }
10140 Label non_ascii, allocated;
10141 ASSERT_EQ(0, kTwoByteStringTag);
10142 __ tst(r4, Operand(kStringEncodingMask));
10143 __ tst(r5, Operand(kStringEncodingMask), ne);
10144 __ b(eq, &non_ascii);
10145
10146 // Allocate an ASCII cons string.
10147 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
10148 __ bind(&allocated);
10149 // Fill the fields of the cons string.
10150 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
10151 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
10152 __ mov(r0, Operand(r7));
10153 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10154 __ add(sp, sp, Operand(2 * kPointerSize));
10155 __ Ret();
10156
10157 __ bind(&non_ascii);
10158 // Allocate a two byte cons string.
10159 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
10160 __ jmp(&allocated);
10161
10162 // Handle creating a flat result. First check that both strings are
10163 // sequential and that they have the same encoding.
10164 // r0: first string
10165 // r1: second string
10166 // r2: length of first string
10167 // r3: length of second string
10168 // r4: first string instance type (if string_check_)
10169 // r5: second string instance type (if string_check_)
10170 // r6: sum of lengths.
10171 __ bind(&string_add_flat_result);
10172 if (!string_check_) {
10173 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
10174 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
10175 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
10176 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
10177 }
10178 // Check that both strings are sequential.
10179 ASSERT_EQ(0, kSeqStringTag);
10180 __ tst(r4, Operand(kStringRepresentationMask));
10181 __ tst(r5, Operand(kStringRepresentationMask), eq);
10182 __ b(ne, &string_add_runtime);
10183 // Now check if both strings have the same encoding (ASCII/Two-byte).
10184 // r0: first string.
10185 // r1: second string.
10186 // r2: length of first string.
10187 // r3: length of second string.
10188 // r6: sum of lengths..
10189 Label non_ascii_string_add_flat_result;
10190 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
10191 __ eor(r7, r4, Operand(r5));
10192 __ tst(r7, Operand(kStringEncodingMask));
10193 __ b(ne, &string_add_runtime);
10194 // And see if it's ASCII or two-byte.
10195 __ tst(r4, Operand(kStringEncodingMask));
10196 __ b(eq, &non_ascii_string_add_flat_result);
10197
10198 // Both strings are sequential ASCII strings. We also know that they are
10199 // short (since the sum of the lengths is less than kMinNonFlatLength).
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010200 // r6: length of resulting flat string
ager@chromium.org5c838252010-02-19 08:53:10 +000010201 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
10202 // Locate first character of result.
10203 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10204 // Locate first character of first argument.
10205 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10206 // r0: first character of first string.
10207 // r1: second string.
10208 // r2: length of first string.
10209 // r3: length of second string.
10210 // r6: first character of result.
10211 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010212 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000010213
10214 // Load second argument and locate first character.
10215 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
10216 // r1: first character of second string.
10217 // r3: length of second string.
10218 // r6: next character of result.
10219 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010220 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
ager@chromium.org5c838252010-02-19 08:53:10 +000010221 __ mov(r0, Operand(r7));
10222 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10223 __ add(sp, sp, Operand(2 * kPointerSize));
10224 __ Ret();
10225
10226 __ bind(&non_ascii_string_add_flat_result);
10227 // Both strings are sequential two byte strings.
10228 // r0: first string.
10229 // r1: second string.
10230 // r2: length of first string.
10231 // r3: length of second string.
10232 // r6: sum of length of strings.
10233 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
10234 // r0: first string.
10235 // r1: second string.
10236 // r2: length of first string.
10237 // r3: length of second string.
10238 // r7: result string.
10239
10240 // Locate first character of result.
10241 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10242 // Locate first character of first argument.
10243 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10244
10245 // r0: first character of first string.
10246 // r1: second string.
10247 // r2: length of first string.
10248 // r3: length of second string.
10249 // r6: first character of result.
10250 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010251 StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000010252
10253 // Locate first character of second argument.
10254 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
10255
10256 // r1: first character of second string.
10257 // r3: length of second string.
10258 // r6: next character of result (after copy of first string).
10259 // r7: result string.
ager@chromium.orgac091b72010-05-05 07:34:42 +000010260 StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
ager@chromium.org5c838252010-02-19 08:53:10 +000010261
10262 __ mov(r0, Operand(r7));
10263 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
10264 __ add(sp, sp, Operand(2 * kPointerSize));
10265 __ Ret();
10266
10267 // Just jump to runtime to add the two strings.
10268 __ bind(&string_add_runtime);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000010269 __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
ager@chromium.org5c838252010-02-19 08:53:10 +000010270}
10271
10272
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000010273#undef __
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010274
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000010275} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000010276
10277#endif // V8_TARGET_ARCH_ARM